利用程序名用做参数
利用程序名用做参数
背景
无意中发现Linux中的关机重启等程序都是systemctl的软连接:
lrwxrwxrwx 1 root root 14 11月10日 08:25 halt -> /bin/systemctl
lrwxrwxrwx 1 root root 14 11月10日 08:25 poweroff -> /bin/systemctl
lrwxrwxrwx 1 root root 14 11月10日 08:25 reboot -> /bin/systemctl
lrwxrwxrwx 1 root root 14 11月10日 08:25 runlevel -> /bin/systemctl
lrwxrwxrwx 1 root root 14 11月10日 08:25 shutdown -> /bin/systemctl
lrwxrwxrwx 1 root root 14 11月10日 08:25 telinit -> /bin/systemctl
所以好奇是怎么将参数传递给systemctl的,随后看了下systemctl的源码:
int systemctl_dispatch_parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
if (invoked_as(argv, "halt")) {
arg_action = ACTION_HALT;
return halt_parse_argv(argc, argv);
} else if (invoked_as(argv, "poweroff")) {
arg_action = ACTION_POWEROFF;
return halt_parse_argv(argc, argv);
} else if (invoked_as(argv, "reboot")) {
arg_action = ACTION_REBOOT;
return halt_parse_argv(argc, argv);
} else if (invoked_as(argv, "shutdown")) {
arg_action = ACTION_POWEROFF;
return shutdown_parse_argv(argc, argv);
} else if (invoked_as(argv, "init")) {
/* Matches invocations as "init" as well as "telinit", which are synonymous when run
* as PID != 1 on SysV.
*
* On SysV "telinit" was the official command to communicate with PID 1, but "init" would
* redirect itself to "telinit" if called with PID != 1. We follow the same logic here still,
* though we add one level of indirection, as we implement "telinit" in "systemctl". Hence,
* for us if you invoke "init" you get "systemd", but it will execve() "systemctl"
* immediately with argv[] unmodified if PID is != 1. If you invoke "telinit" you directly
* get "systemctl". In both cases we shall do the same thing, which is why we do
* invoked_as(argv, "init") here, as a quick way to match both.
*
* Also see redirect_telinit() in src/core/main.c. */
if (sd_booted() > 0) {
arg_action = _ACTION_INVALID;
return telinit_parse_argv(argc, argv);
} else {
/* Hmm, so some other init system is running, we need to forward this request to it.
*/
arg_action = ACTION_TELINIT;
return 1;
}
} else if (invoked_as(argv, "runlevel")) {
arg_action = ACTION_RUNLEVEL;
return runlevel_parse_argv(argc, argv);
}
arg_action = ACTION_SYSTEMCTL;
return systemctl_parse_argv(argc, argv);
}
接着跟到invoke_as:
bool invoked_as(char *argv[], const char *token) {
if (!argv || isempty(argv[0]))
return false;
if (isempty(token))
return false;
return strstr(last_path_component(argv[0]), token);
}
可以看到,就是通过argv[0]来传递参数的。
总结
我们在编写程序的时候,一般都是处理argv[1]及之后的参数,很少会去处理argv[0]。
但是如果我们想让程序表现出不同的行为,就像不同的程序一样,这时候可以将程序创建数个软连接,通过argv[0]来区分。这样的好处我认为有以下两点:
- 不用每次都在程序后面加单独的参数,使用起来简洁
- 软连接的名字可以让使用者一眼就看出程序的功能,语意上更明确

浙公网安备 33010602011771号