有些命令不支持输入流

前面我们讲过,有些命令是不支持输入流的,只支持命令行参数,如最常用的 ls。我们通常这样使用

$ ls /var/
backups  cache  crash  lib  local  lock  log  mail  metrics  opt  run  snap  spool  tmp

但是不能这样使用

$ echo "/var" | ls

这样使用的话,ls 会忽略管道传递给它的输入流。最终相当于,仅仅运行了不带参数的 ls。

xargs 的用处

那么,如果命令行或脚本确实需要用到类似的写法,有什么解决办法呢?答案就是用 xargs,xargs 的其中一个作用就是可以将管道或标准输入的数据转换成命令行参数,例如

$ echo "/var" | xargs ls
backups  cache	crash  lib  local  lock  log  mail  metrics  opt  run  snap  spool  tmp

以上,加了一个 xargs ,就可以将管道的输出转换成 ls 的参数。

原理分析

管道本来的作用是连接 echo 的标准输出到 ls 的标准输入,加了 xargs,就相当于管道连接了 echo 的标准输出到 xargs 的标准输入,xargs 又将其标准输入的内容转换成命令的参数,传递给命令。

源码分析

先上源码

xargs.c

# define READ_ARGS(l, e, nmc, mc) process_stdin(l, e, nmc, mc)

int xargs_main(int argc, char **argv)
{
	while ((list = READ_ARGS(list, eof_str, n_max_chars, max_chars)) != NULL ||	/* 从标准输入读取数据作为参数列表 */
		(opt & OPT_NO_EMPTY) == 0)
	{
		opt |= OPT_NO_EMPTY;
		n = 0;
		n_chars = 0;

		/* allocating pointers for execvp:
		   a*arg, n*arg from stdin, NULL */
		args = xcalloc(n + a + 1, sizeof(char *));

		/* Store the command to be executed
		   (taken from the command line) */
		for (i = 0; i < a; i++)
			args[i] = argv[i];
		/* (taken from stdin) */
		for (cur = list; n; cur = cur->link) {
			args[i++] = cur->data;
			n--;
		}

		if ((opt & OPT_INTERACTIVE) == 0 || xargs_ask_confirmation() != 0) {
			child_error = xargs_exec(args); /* 执行命令,其中 args 的值可能为: args[0] = "ls", args[1] = "-a" */
		}

		/* clean up */
		for (i = a; args[i]; i++) {
			cur = list;
			list = list->link;
			free(cur);
		}
		free(args);
		if (child_error > 0 && child_error != 123) {
			break;
		}
	}
    return child_error;
}

所以,xargs 的原理就是读取标准输入,转成参数列表,借助 exec 函数,执行命令和参数。

提升

我们发现,通过 xargs 执行的 ls 命令,失去了颜色,那是因为在 shell 中执行的 ls,实际上执行的是 ls --color=auto,ls 是 alias 给 ls --color=auto 起的别名。要想我们的输出带颜色,可以使用如下命令:

xargs-ls

呐,颜色回来了