基础命令学习目录
为什么要用 xargs, 问题的来源
在工作中经常会接触到 xargs 命令, 特别是在别人写的脚本里面也经常会遇到, 但是却很容易与管道搞混淆, 本篇会详细讲解到底什么是 xargs 命令, 为什么要用 xargs 命令以及与管道的区别. 为什么要用 xargs 呢, 我们知道, Linux 命令可以从两个地方读取要处理的内容, 一个是通过命令行参数, 一个是标准输入. 例如 cat,grep 就是这样的命令, 举个例子:
- ? http://www.cnblogs.com/#
- echo 'main' | cat test.cpp
这种情况下 cat 会输出 test.cpp 的内容, 而不是'main'字符串, 如果 test.cpp 不存在则 cat 命令报告该文件不存在, 并不会尝试从标准输入中读取. echo 'main' | 会通过管道将 echo 的标准输出 (也就是字符串'main') 导入到 cat 的标准输入, 也就是说此时 cat 的标准输入中是有内容的, 其内容就是字符串'main'但是上面的内容中 cat 不会从它的标准输入中读入要处理的内容.(注: 标准输入是有一个缓冲区的, 就像我们在程序中使用 scanf 函数从标准输入中读取一样, 实际上是从标准输入的缓冲区中读取的). 其实基本上 Linux 的命令中很多的命令的设计是先从命令行参数中获取参数, 然后从标准输入中读取, 反映在程序上, 命令行参数是通过 main 函数 int main(int argc,char*argv[]) 的函数参数获得的, 而标准输入则是通过标准输入函数例如 C 语言中的 scanf 读取到的. 他们获取的地方是不一样的. 例如:
- ? http://www.cnblogs.com/#
- echo 'main' | cat
这条命令中 cat 会从其标准输入中读取内容并处理, 也就是会输出'main' 字符串. echo 命令将其标准输出的内容'main' 通过管道定向到 cat 的标准输入中.
- ? http://www.cnblogs.com/#
- cat
如果仅仅输入 cat 并回车, 则该程序会等待输入, 我们需要从键盘输入要处理的内容给 cat, 此时 cat 也是从标准输入中得到要处理的内容的, 因为我们的 cat 命令行中也没有指定要处理的文件名. 大多数命令有一个参数 - 如果直接在命令的最后指定 - 则表示从标准输入中读取, 例如:
- ? http://www.cnblogs.com/#
- echo 'main' | cat -
这样也是可行的, 会显示'main' 字符串, 同样输入 cat - 直接回车与输入 cat 直接回车的效果也一样, 但是如果这样呢:
- ? http://www.cnblogs.com/#
- echo 'main' | cat test.cpp -
同时指定 test.cpp 和 - 参数, 此时 cat 程序还是会显示 test.cpp 的内容. 但是有一个程序的策略则不同, 它是 grep, 例如:
- ? http://www.cnblogs.com/#
- echo 'main' | grep 'main' test.cpp -
该命令的输出结果是:
- test.cpp:int main()
- (standard input):main
此时 grep 会同时处理标准输入和文件 test.cpp 中的内容, 也就是说会在标准输入中搜索'main' 也会在文件 test.cpp (该文件名从 grep 命令行参数中获得)中搜索'main'. 也就是说当命令行中 test.cpp 和 - 两个参数同时存在的时候, 不同的程序处理不同. 我们看到了 cat 与 grep 处理就不同. 但是有一点是一样的, 首先在命令行中查找要处理的内容的来源(是从文件还是从标准输入, 还是都有), 如果在命令行中找不到与要处理的内容的来源相关的参数则默认从标准输入中读取要处理的内容了.
另外很多程序是不处理标准输入的, 例如 kill , rm 这些程序如果命令行参数中没有指定要处理的内容则不会默认从标准输入中读取. 所以:
- ? http://www.cnblogs.com/#
- echo '516' | kill
这种命里是不能执行的.
- ? http://www.cnblogs.com/#
- echo 'test' | rm -f
这种也是没有效果的.
这两个命令只接受命令行参数中指定的处理内容, 不从标准输入中获取处理内容. 想想也很正常, kill 是结束进程, rm 是删除文件, 如果要结束的进程 pid 和要删除的文件名需要从标准输入中读取, 这个也很怪异吧. 但是像 cat 与 grep 这些文字处理工具从标准输入中读取待处理的内容则很自然.
但是有时候我们的脚本却需要 echo '516' | kill 这样的效果, 例如 ps -ef | grep 'ddd' | kill 这样的效果, 筛选出符合某条件的进程 pid 然后结束. 这种需求对于我们来说是理所当然而且是很常见的, 那么应该怎样达到这样的效果呢. 有几个解决办法:
1. 通过 kill `ps -ef | grep 'ddd'`
- # 这种形式, 这个时候实际上等同于拼接字符串得到的命令, 其效果类似于 kill $pid
- 2. for procid in $(ps -aux | grep "some search" | awk '{print $2}'); do kill -9 $procid; done
- # 其实与第一种原理一样, 只不过需要多次 kill 的时候是循环处理的, 每次处理一个
- 3. ps -ef | grep 'ddd' | xargs kill
- #OK, 使用了 xargs 命令, 铺垫了这么久终于铺到了主题上. xargs 命令可以通过管道接受字符串, 并将接收到的字符串通过空格分割成许多参数(默认情况下是通过空格分割) 然后将参数传递给其后面的命令, 作为后面命令的命令行参数
回到顶部 http://www.cnblogs.com/#_labelTop
xargs 是什么, 与管道有什么不同
xargs 与管道有什么不同呢, 这是两个很容易混淆的东西, 看了上面的 xargs 的例子还是有点云里雾里的话, 我们来看下面的例子弄清楚为什么需要 xargs:
echo '--help' | cat
输出:
- --help
- echo '--help' | xargs cat
输出:
- ? http://www.cnblogs.com/#
- Usage: cat [OPTION]... [FILE]...
- Concatenate FILE(s), or standard input, to standard output.
- -A, --show-all equivalent to -vET
- -b, --number-nonblank number nonempty output lines
- -e equivalent to -vE
- -E, --show-ends display $ at end of each line
- -n, --number number all output lines
- -s, --squeeze-blank suppress repeated empty output lines
- -t equivalent to -vT
- -T, --show-tabs display TAB characters as ^I
- -u (ignored)
- -v, --show-nonprinting use ^ and M- notation, except for LFD and TAB
- --help display this help and exit
- --version output version information and exit
- With no FILE, or when FILE is -, read standard input.
- Examples:
- cat f - g Output f's contents, then standard input, then g's contents.
- cat Copy standard input to standard output.
- Report cat bugs to bug-coreutils@gnu.org
- GNU coreutils home page: //www.gnu.org/software/coreutils/>
- General help using GNU software: //www.gnu.org/gethelp/>
- For complete documentation, run: info coreutils 'cat invocation'
可以看到 echo '--help' | cat 该命令输出的是 echo 的内容, 也就是说将 echo 的内容当作 cat 处理的文件内容了, 实际上就是 echo 命令的输出通过管道定向到 cat 的输入了. 然后 cat 从其标准输入中读取待处理的文本内容. 这等价于在 test.txt 文件中有一行字符 '--help' 然后运行 cat test.txt 的效果.
而 echo '--help' | xargs cat 等价于 cat --help 什么意思呢, 就是 xargs 将其接受的字符串 --help 做成 cat 的一个命令参数来运行 cat 命令, 同样 echo 'test.c test.cpp' | xargs cat 等价于 cat test.c test.cpp 此时会将 test.c 和 test.cpp 的内容都显示出来.
回到顶部 http://www.cnblogs.com/#_labelTop
xargs 的一些有用的选项
相信到这里应该都知道 xargs 的作用了, 那么我们看看 xargs 还有一些有用的选项:
1. -d 选项
默认情况下 xargs 将其标准输入中的内容以空白 (包括空格, Tab, 回车换行等) 分割成多个之后当作命令行参数传递给其后面的命令, 并运行之, 我们可以使用 -d 命令指定分隔符, 例如:
echo '11@22@33' | xargs echo
输出:
11@22@33
默认情况下以空白分割, 那么 11@22@33 这个字符串中没有空白, 所以实际上等价于 echo 11@22@33 其中字符串 '11@22@33' 被当作 echo 命令的一个命令行参数
echo '11@22@33' | xargs -d '@' echo
输出:
11 22 33
指定以 @符号分割参数, 所以等价于 echo 11 22 33 相当于给 echo 传递了 3 个参数, 分别是 11,22,33
2. -p 选项
使用该选项之后 xargs 并不会马上执行其后面的命令, 而是输出即将要执行的完整的命令(包括命令以及传递给命令的命令行参数), 询问是否执行, 输入 y 才继续执行, 否则不执行. 这种方式可以清楚的看到执行的命令是什么样子, 也就是 xargs 传递给命令的参数是什么, 例如:
echo '11@22@33' | xargs -p -d '@' echo
输出:
echo 11 22 33
?...y ==>这里询问是否执行命令 echo 11 22 33 输入 y 并回车, 则显示执行结果, 否则不执行
11 22 33 ==>执行结果
3. -n 选项
该选项表示将 xargs 生成的命令行参数, 每次传递几个参数给其后面的命令执行, 例如如果 xargs 从标准输入中读入内容, 然后以分隔符分割之后生成的命令行参数有 10 个, 使用 -n 3 之后表示一次传递给 xargs 后面的命令是 3 个参数, 因为一共有 10 个参数, 所以要执行 4 次, 才能将参数用完. 例如:
echo '11@22@33@44@55@66@77@88@99@00' | xargs -d '@' -n 3 echo
输出结果:
- 11 22 33
- 44 55 66
- 77 88 99
- 00
等价于:
- echo 11 22 33
- echo 44 55 66
- echo 77 88 99
- echo 00
实际上运行了 4 次, 每次传递 3 个参数, 最后还剩一个, 就直接传递一个参数.
4. -E 选项, 有的系统的 xargs 版本可能是 - e eof-str
该选项指定一个字符串, 当 xargs 解析出多个命令行参数的时候, 如果搜索到 - e 指定的命令行参数, 则只会将 - e 指定的命令行参数之前的参数 (不包括 - e 指定的这个参数) 传递给 xargs 后面的命令
echo '11 22 33' | xargs -E '33' echo
输出:
11 22
可以看到正常情况下有 3 个命令行参数 11,22,33 由于使用了 - E '33' 表示在将命令行参数 33 之前的参数传递给执行的命令, 33 本身不传递. 等价于 echo 11 22 这里 - E 实际上有搜索的作用, 表示只取 xargs 读到的命令行参数前面的某些部分给命令执行.
注意:-E 只有在 xargs 不指定 - d 的时候有效, 如果指定了 - d 则不起作用, 而不管 - d 指定的是什么字符, 空格也不行.
echo '11 22 33' | xargs -d '' -E'33' echo => 输出 11 22 33
echo '11@22@33@44@55@66@77@88@99@00 aa 33 bb' | xargs -E '33' -d '@' -p echo => 输出 11 22 33 44 55 66 77 88 99 00 aa 33 bb
- ## -0 选项表示以 '\0' 为分隔符, 一般与 find 结合使用
- find . -name "*.txt"
输出:
./2.txt
./3.txt
./1.txt => 默认情况下 find 的输出结果是每条记录后面加上换行, 也就是每条记录是一个新行
find . -name "*.txt" -print0
输出:
./2.txt./3.txt./1.txt => 加上 -print0 参数表示 find 输出的每条结果后面加上 '\0' 而不是换行
find . -name "*.txt" -print0 | xargs -0 echo
输出:
- ./2.txt ./3.txt ./1.txt
- find . -name "*.txt" -print0 | xargs -d '\0' echo
输出:
./2.txt ./3.txt ./1.txt
xargs 的 -0 和 -d '\0' 表示其从标准输入中读取的内容使用 '\0' 来分割, 由于 find 的结果是使用 '\0' 分隔的, 所以 xargs 使用 '\0' 将 find 的结果分隔之后得到 3 个参数: ./2.txt ./3.txt ./1.txt 注意中间是有空格的. 上面的结果就等价于 echo ./2.txt ./3.txt ./1.txt
实际上使用 xargs 默认的空白分隔符也是可以的 find . -name "*.txt" | xargs echo 因为换行符也是 xargs 的默认空白符的一种. find 命令如果不加 - print0 其搜索结果的每一条字符串后面实际上是加了换行
- ? http://www.cnblogs.com/#
- find /sbin -perm +700 |ls -l // 这个命令是错误, 因为标准输入不能作为 ls 的参数
- find /sbin -perm +700 |xargs ls -l // 这样才是正确的
- ? http://www.cnblogs.com/#
- [b3335@MIC ~]$ echo '`0123`4 56789'|xargs -t echo
- echo `0123`4 56789
- `0123`4 56789
- ? http://www.cnblogs.com/#
- [b3335@MIC ~]$ echo `01234` 56789
- -bash: 01234: command not found
- 56789
- ? http://www.cnblogs.com/#
- [b3335@MIC ~]$ echo 01234 , 56789|xargs -E ","
- 01234
- ? http://www.cnblogs.com/#
- ls | xargs -t -i mv {
- } {
- }.bak
- ? http://www.cnblogs.com/#
- [b3335@MIC test]$ echo "01234 56789"|xargs -t -s 11
- echo 01234
- 01234
- echo 56789
- 56789
- ? http://www.cnblogs.com/#
- [b3335@MIC test]$ echo -e "01234\n56789\n01234" | xargs -t -L 2 echo
- echo 01234 56789
- 01234 56789
- echo 01234
- 01234
- ? http://www.cnblogs.com/#
- // 列出文件内容
- cat test.txt
- a b c d e
- f g h i j
- k l m n o
- // 多行输入单行输出:
- cat test.txt | xargs
- a b c d e f g h i j k l m n o
来源: http://www.bubuko.com/infodetail-2946948.html