背景:最近在看一些命令和代码,看不明白的东西就查询了一些材料,这里记录了下来以用于后续温习。
一. 标准输入stdin、标准输出stdout及标准错误输出stderr
我们可以从图中看到,整个处理过程有点类似于 《信号与系统》这门课,Linux命令即是“系统”,然后有 stdin作为输入信号,stdout和stderr作为输出信号。
对于一个命令,有两种输入,一种是标准输入stdin,另外一种是命令行参数(这里不是命令的选项)。
有的命令接收标准输入,被称为管道命令,而有的命令不接收标准输入。
- 标准输入 (stdin) :代码为 0 ,使用 < 或 << ;
- 标准输出 (stdout):代码为 1 ,使用 > 或 >> ;
- 标准错误输出(stderr):代码为 2 ,使用 2> 或 2>> ;
<、>和2>是清空旧数据添加新数据, 而>>和2>>是追加,而<<代表后面是结束符
当执行一个命令时候,标准输出stdout和标准错误输出stderr 默认会同时输出到屏幕中。
有的时候也需要将标准输出和标准错误输出 到一个命令,对这两个输出再进行分析或者进行grep,可以使用 |&进行
下面是各种不同的情况:
// 1. 标准输出 输出到 list_right , 标准错误输出 输出到 list_error
$ find /home -name .bashrc > list_right 2> list_error
// 2. 标准输出 输出到 list_right , 标准错误输出 输出到 屏幕
$ find /home -name .bashrc > list_right
// 3. 标准输出 输出到 屏幕 , 标准错误输出 输出到 list_error
$ find /home -name .bashrc 2> list_error
// 4. 标准输出和标准错误输出 到 同一个文件
$ find /home -name .bashrc > list 2>&1 <==正确
$ find /home -name .bashrc &> list <==正确
// 5. 标准输出和标准错误输出 到 一个命令 使用 |&
$ strace ls |& grep -e read -e write
// 6. 当然也可以使用下面这种形式
$ strace ls 2>&1 | grep -e read -e write
/dev/null 是一个信息黑洞,可以将 标准错误输出 到这里:
$ find /home -name .bashrc 2> /dev/null
< 和 << 是『将原本需要由键盘输入的数据,改由文件内容来取代』的意思,键盘输入的数据其实就是标准输入stdin。
// cat命令接收标准输入
// < 从bashrc文件中读取内容转换为stdin
// > 通过重定向到catfile中
cat > catfile < ~/.bashrc
ll catfile ~/.bashrc
-rw-r--r-- 1 root root 194 Sep 26 13:36 /root/.bashrc
-rw-r--r-- 1 root root 194 Feb 6 18:29 catfile
# 注意看,这两个文件的大小会一模一样!几乎像是使用 cp 来复制一般!
// << 代表 后面的字符串是结束符号,这里是 "eof",也可以是其他字符,例如"end"
cat > catfile << "eof"
> This is a test.
> OK now stop
> eof <==输入这关键词,立刻就结束而不需要输入 [ctrl]+d
grep命令就能接受标准输入以及文件,命令格式为:
grep [OPTION...] PATTERNS [FILE...]
一般情况下:
grep -in main test.c // 即在test.c中查找main字符
等同于
cat test.c | grep -in main // 通过cat打印到标准输出再经过管道转为标准输入进行查找
详细请查看:
我记不住的grep和find命令_main find-CSDN博客
小结:
cat # read from terminal; write to terminal cat <input.txt # read from input.txt; write to terminal cat >output.txt # read from terminal; write to output.txt cat <input.txt >output.txt # read from input.txt; write to output.txt
二. 多个命令的执行
有分号; 有&& 有 ||
执行命令 | 执行效果 |
cmd1 && cmd2 | 1. 若 cmd1 运行完毕且正确运行($?=0),则开始运行 cmd2。 2. 若 cmd1 运行完毕且为错误 ($?≠0),则 cmd2 不运行。 |
cmd1 || cmd2 | 1. 若 cmd1 运行完毕且正确运行($?=0),则 cmd2 不运行。 2. 若 cmd1 运行完毕且为错误 ($?≠0),则开始运行 cmd2。 |
三. 管道和xargs
通过管道符号 | 来进行数据传递,从command1传递到command2再传递到command3,都是由一个命令的标准输出stdout 经过管道| 转换为 标准输入stdin 再传递到下一个命令,如下图所示:
管道符号 | 只会处理标准输出stdout不处理标准错误输出 stderr
管道符只是在stdin或stdout进行数据传递,但是很多Linux程序需要将命令行参数作为输入,而这些Linux程序可以使用xargs命令和管道进行传递。
xargs这个命令就是在产生某个命令的参数的意思,即 xargs 可以读入 stdin 的数据,并且以空格符或断行字符作为分辨,将 stdin 的数据分隔成为 arguments,也可以从文件中读取数据。
{{arguments_source}} | xargs {{command}}
xargs函数是将 标准输入stdin 变为命令行参数
很多命令其实并不支持管道命令,因此我们可以透过 xargs 来提供该命令引用 standard input 之用。举例来说,我们使用如下的范例来说明:
// cat 命令 读取 files_to_delete文件并标准输出stdout到屏幕
$ cat files_to_delete
obsolete_note.md
yucky_recipe.pdf
cringe_tiktok.mov
// 标准输出stdout 通过 管道符 | 转为 标准输入stdin
// xargs将标准输入stdin转换为 命令的相关参数
// 最后 执行删除命令
$ cat files_to_delete | xargs rm
echo 函数是将 命令行参数 变为 标准输入stdin
只要记住xargs是echo的相反操作,就能记住xargs的功能。
// 下面的命令是等效的
// echo $DATA 是标准输出stdout
// | 将标准输出stdout 转换为 stdin
// xargs 将标准输入stdin 转换为 各类参数
// 所以下面两个命令是等效的
#echo $DATA | xargs echo | $CMD
#echo $DATA | $CMD
// 下面的命令是等效的
// echo $DATA 将$DATA输出到标准输出stdout
// | 将标准输出stdout 转换为 标准输入stdin
// xargs 将标准输入stdin 转换为 参数
// 这样等效为 $CMD $DATA
#echo $DATA | xargs $CMD
#$CMD $DATA
// 下面的命令是无任何显示的
# echo $DATA | echo
原因是 管道右侧的echo不接受管道传来的标准输入作为参数
echo命令是xargs命令的反向操作,xargs命令是echo命令的反向操作。
xargs命令和echo命令频繁出现在各种shell脚本中,echo一般很好理解,直接从命令行参数输出到标准输出。
大多数时候,xargs
命令都是跟管道一起使用的。同时它也可以单独使用但一般没有什么意义。
默认情况下,xargs
将换行符和空格作为分隔符, 这里使用 -d 选项将“\t”作为分隔符。
echo命令的 -e参数表示 打开反斜杠功能
# echo -e "a\tb\tc"
a b c
# echo -e "a\tb\tc" | xargs -d "\t" echo
a b c
-p
参数打印出要执行的命令,询问用户是否要执行
-t
参数则是打印出最终要执行的命令,然后直接执行,不需要用户确认
-0参数
由于xargs
默认将空格作为分隔符,所以不太适合处理文件名,因为文件名可能包含空格。
find
命令有一个特别的参数-print0
,指定输出的文件列表以null
分隔
然后,xargs
命令的-0
参数表示用null
当作分隔符
find . -type f -print0 | xargs -0 rm
上面命令首先找出当前路径下的文件,并输出文件列表以null分隔,然后删除当前路径下的所有文件。由于分隔符是null
,所以处理包含空格的文件名,也不会报错。
另外,xargs
特别适合find
命令,每找到一个*.txt文件,就可以通过grep进行搜索文本中是否含有"abc",因为它对每个参数执行一次命令
$ find . -name "*.txt" | xargs grep "abc"
上面命令找出所有 TXT 文件以后,对每个文件搜索一次是否包含字符串abc
。
-L参数及-n参数
// 这样会报错
$ xargs find -name
"*.txt"
"*.md"
find: paths must precede expression: `*.md'
// -L 指定每行作为一个命令行参数
// 这样可以多次查找 多个扩展名的文件
$ xargs -L 1 find -name
"*.txt"
./foo.txt
./hello.txt
"*.md"
./README.md
// 如果我们同时查找*.md和*.pdf文件,可以使用下面命令
// -n 表示 指定将每一项标准输入作为命令行参数,分别执行一次命令find
$ xargs -n 1 find -name
"*.md" "*.pdf"
./obsolete_note.md
./yucky_recipe.pdf
-I 指定 替代符号
-I 指 file被 one,two,three进行替代
然后执行echo和创建文件夹
$ cat foo.txt
one
two
three
$ cat foo.txt | xargs -I file sh -c 'echo file; mkdir file'
one
two
three
$ ls
one two three
xargs默认只用一个进程执行命令。如果命令要执行多次,必须等上一次执行完,才能执行下一次。--max-procs
参数指定同时用多少个进程并行执行命令。--max-procs 2
表示同时最多使用两个进程,--max-procs 0
表示不限制进程数。
docker ps -q | xargs -n 1 --max-procs 0 docker kill
四. 示例
1. 如果你有一个文件,此文件内容是被删除的文件,如何进行删除?
可以使用:
// 可以使用这个命令进行删除操作
// cat命令将文件内容显示到 标准输出stdout
// | 将标准输出stdout 变为 标准输入stdin
// xargs 将标准输入stdin 变为命令的参数
$ cat files_to_delete | xargs rm
或者是
$ rm obsolete_note.md yucky_recipe.pdf cringe_tiktok.mov
也就是说:
xargs动态构建了这个命令$rm obsolete_note.md yucky_recipe.pdf cringe_tiktok.mov
2. 如果磁盘上有很多的文件需要删除,你如何删除?
方式一:直接在命令行中手工输入命令及这些文件的名称
rm -rf xxx yyy zzz .....
方式二:我们直接将当前目录下的所有文件输出到一个文本文件中
$ ls > fuckrm
$ cat fuckrm | xargs rm
结论:
1. stdin/stdout/stderr
2. 管道是实现“将前面的标准输出作为后面的标准输入”
3. xargs是实现“将标准输入转换作为命令的参数”
4. 大部分的Linux命令都是使用参数argument作为输入而不能接收stdin,而使用xargs则完美的解决了问题,xargs将stdin转为命令参数。
参考资料:
1. http://cn.linux.vbird.org/linux_basic/0320bash_5.php
2. http://cn.linux.vbird.org/linux_basic/0320bash_6.php
3. xargs is the inverse function of echo
4. https://shapeshed.com/unix-xargs/
5. https://www.howtoforge.com/tutorial/linux-xargs-command/