我记不住的那些管道和xargs

背景:最近在看一些命令和代码,看不明白的东西就查询了一些材料,这里记录了下来以用于后续温习。

一.  标准输入stdin、标准输出stdout及标准错误输出stderr

我们可以从图中看到,整个处理过程有点类似于 《信号与系统》这门课,Linux命令即是“系统”,然后有 stdin作为输入信号,stdout和stderr作为输出信号。

对于一个命令,有两种输入,一种是标准输入stdin,另外一种是命令行参数(这里不是命令的选项)。

有的命令接收标准输入,被称为管道命令,而有的命令不接收标准输入。

  1. 标准输入  (stdin) :代码为 0 ,使用 < 或 << ;
  2. 标准输出  (stdout):代码为 1 ,使用 > 或 >> ;
  3. 标准错误输出(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 && cmd21. 若 cmd1 运行完毕且正确运行($?=0),则开始运行 cmd2。
2. 若 cmd1 运行完毕且为错误 ($?≠0),则 cmd2 不运行。
cmd1 || cmd21. 若 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/

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Penguinbupt

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值