【shell】-exec和xargs|对每个结果都执行|前命令结果作为后命令的参数|逐行处理

目录

实现效果:

参数说明

-exec参数

xargs参数

-exec和xargs 后执行多条语句

-exec和xargs 执行自定义函数

 如何正确组合“ xargs”,“ bash -c”和环境变量?

-exec和xargs的区别

         -exec和xargs的区别

         实战

注意事项和报错记录


实现效果:

对前一命令的每一个结果都执行某个操作

exec和xargs二者的区别

1.使用-exec选项,此选项是把参数一个一个传递给shell命令,即传递一个文件名执行一次cp命令;
2.而xargs命令,是把参数一次性传递,即把搜索的所有文件名一次姓传给shell命令处理;

参数说明

-exec参数

-exec command:command

-exec 后面接其他执行的指令来处理前一个命令得到的结果。(注意{} 左右两边要留空格)

例如:find   /root   -name  tom_renam  -exec  grep  -nR  "hello"  {}  \;


find在/root目录下搜索名为tom_renam的文件,在搜出的tom_renam文件中搜索“hello”

find /root -name tom_renam -exec grep -nR "hello" {}  \;

{ } 代表的是「由 find 找到的内容」找到的结果会被放置到 { } 位置中;
-exec  到 ; 代表处理搜寻到的结果的动作,因为「;」在bash的环境下是有特殊意义的,要在前面加反斜杠\转译。

其他例子:

find /root/.ssh -exec ls -l {} \;       #显示find /root/.ssh搜出的结果
find /root -size 10M -exec rm -rf {} \; #删除find /root -size 10M搜出的结果
find /root -name tom -exec mv {} tom_rename \;  #重命名
find /root -name tom_rename -exec mv {} /tmp \; #将find /root -name tom_rename搜出的移到/tmp

https://www.cnblogs.com/itxdm/p/5936907.html

xargs参数

xargs可以将标准输出(数据流)转换成命令参数作为标准输入( 进行横排输出)。

当 -I 与 xargs 结合使用,每一个参数命令都会被执行一次:

当 -I 与 xargs 结合使用,每一个参数命令都会被执行一次:

#显示。-I {} :定义前一命令的结果放入{}, ls -ld  {} : ls -ld 结果
find /root -name Tom | xargs -I {}   ls -ld  {} 

#删除。如果后一条命令的参数在末尾
find /root -name Tom | xargs  rm -rf 

#移动。如果后一条命令的参数在中间,需指定{},并在后一命令参数的位置放{}占位
find /root -name *.sh| xargs -I {} mv {} /tmp 



定义界定符

在 “xargs” 中是有 “界定符” 的,类似 find 中 { } ,代表的是「由 xargs 找到的内容」。xargs 不需要 “;” 做结束符。

使用 xargs 命令时并不是一定要使用 “{}” 方括号的,可能是因为 find 命令的( -exec )默认是 “{}” (为了统一)使用其他的定义符都是可以的(甚至你都可以用英文,数学等作为定义界定符)

Linux xargs 命令;https://www.runoob.com/linux/linux-comm-xargs.html

命令格式:

somecommand |xargs -item  command

参数:

  • -a file 从文件中读入作为 stdin
  • -e flag ,注意有的时候可能会是-E,flag必须是一个以空格分隔的标志,当xargs分析到含有flag这个标志的时候就停止。
  • -p 当每次执行一个argument的时候询问一次用户。
  • -n num 后面加次数,表示命令在执行的时候一次用的argument的个数,默认是用所有的。
  • -t 表示先打印命令,然后再执行。
  • -i 或者是-I,这得看linux支持了,将xargs的每项名称,一般是一行一行赋值给 {},可以用 {} 代替。
  • -r no-run-if-empty 当xargs的输入为空的时候则停止xargs,不用再去执行了。
  • -s num 命令行的最大字符数,指的是 xargs 后面那个命令的最大命令行字符数。
  • -L num 从标准输入一次读取 num 行送给 command 命令。
  • -l 同 -L。
  • -d delim 分隔符,默认的xargs分隔符是回车,argument的分隔符是空格,这里修改的是xargs的分隔符。
  • -x exit的意思,主要是配合-s使用。。
  • -P 修改最大的进程数,默认是1,为0时候为as many as it can ,这个例子我没有想到,应该平时都用不到的吧

-exec和xargs 后执行多条语句

 使用xargs调用多个命令

shell 管道符| 后面的xargs 一次执行多条命令,用xargs获取到的参数

find ./*.js|xargs -i  sh -c 'ls -l {}; cat -n {}'|vim -

这条命令不是很安全,This is insecure. What if your file.txt contains a datum with $(rm -rf ~) as a substring?

ls $(rm -rf ./*) 执行这条命令的时候,会把当前目录下面的东西全部删除!!


这台命令可以先输出文件名,然后再打印文件的内容。


cat file.txt | xargs -i  sh -c 'command {} | command2 {} && command3 {}'
-----------------------------------------------------------------------------
 "sh -c" 命令,它可以让 bash 将一个字串作为完整的命令来执行,

shell 管道符| 后面的xargs 一次执行多条命令,http://wjhsh.net/oxspirt-p-12198503.html

单个 cat a.txt | xargs -I % echo %

多个 cat a.txt | xargs -I % {command1; command2; ... }

https://blog.csdn.net/xfxf996/article/details/107958997

https://www.codenong.com/6958689/

-exec和xargs 执行自定义函数

-exec和xargs只能执行shell内部的函数,如果想让--exec和xargs执行你自定义的函数,则需要先export -f 导出函数,例如:

function process() { 
    echo "I can do anything I want"
    echo " per element $1"
    echo " that I want here"
}

export -f process

$command | grep -F "Specific :: Criterion" | awk '{print $6}' | xargs -I % -n 1 bash -c "process %";

 如何正确组合“ xargs”,“ bash -c”和环境变量?

How to combine "xargs", "bash -c" and environment variables properly? - Stack Overflow

其他 如何正确组合“ xargs”,“ bash -c”和环境变量? - 糯米PHP

基于更好地了解如何诊断和解决问题,而不只是提供解决方案,以下是我经过思考的过程:

首先,我设置一个测试环境:

$ mkdir -p files
$ ( cd files; touch touch date03222020-pid001 date03222020-pid009 date03222020-pid011; )
$ export LAST_EXECUTE=date03222020
$ ls files/$LAST_EXECUTE*
files/date03222020-pid001  files/date03222020-pid009  files/date03222020-pid011

现在,让我们尝试通过执行echo而不是命令来查看xargs在做什么:

$ ls files/$LAST_EXECUTE* | xargs -IX echo bash -c "python script.py X ${X#$LAST_EXECUTE}"
bash -c python script.py files/date03222020-pid001 
bash -c python script.py files/date03222020-pid009 
bash -c python script.py files/date03222020-pid011 

由于${X#LAST_EXECUTE}用双引号引起来,因此它已作为xargs命令的一部分进行了扩展。由于$X没有值,因此从逻辑上讲什么都没有。

我们可以给它一个值来说明:

$ X=Hello
$ ls files/$LAST_EXECUTE* | xargs -IX echo bash -c "python script.py ${X#$LAST_EXECUTE}"
bash -c python script.py files/date03222020-pid001 Hello
bash -c python script.py files/date03222020-pid009 Hello
bash -c python script.py files/date03222020-pid011 Hello

但是该xargs命令的目的是X用参数替换字符串,而不是定义shell变量$X。并且替换字符串X也会导致不希望的行为(特别是因为存在Xin LAST_EXECUTE,如我们所见,使用单引号而不是双引号:

$ ls files/$LAST_EXECUTE* | xargs -IX echo bash -c 'python script.py X ${X#$LAST_EXECUTE}'
bash -c python script.py files/date03222020-pid001 ${files/date03222020-pid001#$LAST_Efiles/date03222020-pid001ECUTE}
bash -c python script.py files/date03222020-pid009 ${files/date03222020-pid009#$LAST_Efiles/date03222020-pid009ECUTE}
bash -c python script.py files/date03222020-pid011 ${files/date03222020-pid011#$LAST_Efiles/date03222020-pid011ECUTE}

如果我们尝试实际执行该操作(通过删除echo),则bash将解释${files/date03222020-pid011#$LAST_Efiles/date03222020-pid011ECUTE}为值中的字符串替换$files,而不是前缀删除。但是由于可能没有导出的shell变量$files,因此也有可能导致空的参数扩展。

实际上,我们真正想做的事情要简单得多:将参数从传递xargsbash,而不是传递给python脚本。在bash脚本中,我们可以根据需要操纵参数。在此过程中,我们将尝试正确引用bash命令行:

$ ls files/$LAST_EXECUTE* | xargs -IX echo bash -c 'python script.py "$1" "${1#$LAST_EXECUTE}"' _ X
bash -c python script.py "$1" "${1#$LAST_Efiles/date03222020-pid001ECUTE}" _ "files/date03222020-pid001"
bash -c python script.py "$1" "${1#$LAST_Efiles/date03222020-pid009ECUTE}" _ "files/date03222020-pid009"
bash -c python script.py "$1" "${1#$LAST_Efiles/date03222020-pid011ECUTE}" _ "files/date03222020-pid011"

注意_上面的。bash -c command后面可以跟更多的位置参数。该命令之后的第一个参数被作为值$0(命令解释程序的名称),和随后的有$1$2...。这与运行bash脚本略有不同。当我们使用时bash script.sh,下一个参数是$1,并且$0是隐式的。因此,通常将它$0用作参数被认为是不好的样式(这可能会导致麻烦,因为bash会根据您提供的名称修改其行为)。我可以用bash作为$0参数,这可能被认为是更正确的,但_更短,更传统。无论如何,它_允许下一个参数(X,它将替换为从xargs输入中读取的每个参数),并以形式传递给脚本$1

另请注意,我没有尝试报价X。这是因为xargs将参数逐字传递到已执行的程序(替换后)。它不会将参数串联到命令行中,也不会调用Shell来解释参数。区别很重要,如果看起来令人困惑,您可能需要尝试一下。

无论如何,最后的更改仍然不能消除它,因为如上所述,其中LAST_EXECUTE有一个X。您需要注意这一点,因为xargs不会起作用。像这样的单个字符X确实不是很好的xargs替换模式,因为即使您不注意它们,它们也很可能出现在命令字符串中。相反,我们将使用常用模式:{}。这往往不会出现在命令行中,但是您仍然需要确保每次使用{}都将被替换:

$ ls files/$LAST_EXECUTE* | xargs -I{} echo bash -c 'python script.py "$1" "${1#$LAST_EXECUTE}"' _ {}
bash -c python script.py "$1" "${1#$LAST_EXECUTE}" _ files/date03222020-pid001
bash -c python script.py "$1" "${1#$LAST_EXECUTE}" _ files/date03222020-pid009
bash -c python script.py "$1" "${1#$LAST_EXECUTE}" _ files/date03222020-pid011

现在移动,echo以便我们实际调用bash

$ ls files/$LAST_EXECUTE* | xargs -I{} bash -c 'echo python script.py "$1" "${1#$LAST_EXECUTE}"' _ {}
python script.py files/date03222020-pid001 files/date03222020-pid001
python script.py files/date03222020-pid009 files/date03222020-pid009
python script.py files/date03222020-pid011 files/date03222020-pid011

好,我们越来越近了。但这很明显前缀删除$LAST_EXECUTE将不起作用,因为它不是前缀。应该是files/$LAST_EXECUTE

$ ls files/$LAST_EXECUTE* | xargs -I{} bash -c 'echo python script.py "$1" "${1#files/$LAST_EXECUTE}"' _ {}
python script.py files/date03222020-pid001 -pid001
python script.py files/date03222020-pid009 -pid009
python script.py files/date03222020-pid011 -pid011

或更好 files/$LAST_EXECUTE-

$ ls files/$LAST_EXECUTE* | xargs -I{} bash -c 'echo python script.py "$1" "${1#files/$LAST_EXECUTE-}"' _ {}
python script.py files/date03222020-pid001 pid001
python script.py files/date03222020-pid009 pid009
python script.py files/date03222020-pid011 pid011

最后,我们确实应该避免尝试解析ls。尽管这些文件名可能表现良好,但实际上没有办法确定文件名的外观。它可能包括空格,甚至包括换行符,因此无法保证xargs会正确划分输入。

无论如何,没有充分的理由ls。参数ls来自glob的扩展,因此唯一ls的增加就是将文件名放在单独的行上。我们可以直接使用glob扩展,这会更好,因为shell不会尝试进行字分割或以其他方式处理glob扩展的结果。为了使xargs能够很好地分隔输入参数序列,我们可以使用printfNUL而不是换行符来放置NUL,因为没有文件名可以包含NUL字符,然后告诉xargs使用( -standard但通常实现)-0选项:

$ printf '%s\0' files/$LAST_EXECUTE* | xargs -0 -I{} bash -c 'echo python script.py "$1" "${1#files/$LAST_EXECUTE-}"' _ {}
python script.py files/date03222020-pid001 pid001
python script.py files/date03222020-pid009 pid009
python script.py files/date03222020-pid011 pid011

-exec和xargs的区别

2016-12-22 22:50:58

 -exec和xargs的区别

当你在命令行执行:

$find . -name 'core' -type f -exec rm {} /;

时,find -exec 命令会对每个匹配的文件执行一个单独的rm操作(execute a separate rm for each one), 正如你手动敲入下面命令:

 

rm ./bin/core

rm ./source/shopping_cart/core

rm ./backups/core ...

但是使用这种方式,如果有100个文件匹配了,那么就需要启100个进程,一个进程处理一个rm命令。一般来说,其越多进程,意味着越耗性能。我们可以换个思路,我们将要删除文件当作参数传递给rm不就可以了吗?也就是说:

rm ./bin/core

rm ./source/shopping_cart/core

rm ./backups/core ...

改成:

rm ./bin/core ./source/shopping_cart/core ./backups/core

但是前提是后面的命令必须支持多参数。相有些命令,比如unzip,就不支持输入多个jar包,所以必须用-exec。
xargs,顾名思义,是对参数进行处理的命令。它的任务就是将输入行转换成下一个命令的参数列表。因此上面的find -exec命令可以改写成:

find . -name 'core' -type f -print | xargs rm

With this approach, xargs bundles together as many filename arguments as possible for submission to each invocation of rm that's needed, in compliance with the OS's maximum allowed size for an argument list. This means xargs is guaranteed not only to handle all the arguments, but also to use the smallest possible number of processes in doing so. For example, if each command can handle 100 arguments, and there are 110 filenames to process, there will be two invocations of the command, respectively handling 100 and 10 arguments.

其中操作系统允许的最大参数长度由如下命令得到:

forrest@ubuntu:~$ getconf ARG_MAX
2097152

这意味着xargs保证不会因为参数过多而挂掉。所以目前看来唯一需要保证的就是后面的命令支持多参数。比如前面说过的unzip,就不支持多参数,如果你使用xargs xxx.jar

 

forrest@ubuntu:~/work/intl-standalone/searchaddbuild/deploy/WORLDS-INF/lib$ unzip -l alibaba-intl-biz-account-api-1.0-Dev.jar Archive: alibaba-intl-biz-account-api-1.0-Dev.jar Length Date Time Name --------- ---------- ----- ---- 0

2010-11-24 19:43 META-INF/ 147

2010-11-24 19:42 META-INF/MANIFEST.MF 0

2010-11-24 19:42 com/ 0 2010-11-24 19:42 com/alibaba/ 0

2010-11-24 19:42 com/alibaba/intl/ 0

2010-11-24 19:42 com/alibaba/intl/biz/ 0 2010-11-24 19:42 com/alibaba/intl/biz/company/ 。。。 931 2010-11-24 19:42 com/alibaba/intl/biz/member/api/exception/IllegalRegistInfoException.class 1055 2010-11-24 19:42 com/alibaba/intl/biz/member/api/AccountCoreInfoRemoteServiceContainer.class 2030 2010-11-24 19:42 com/alibaba/intl/biz/AccountCenterServicesLocator.class 467 2010-11-24 19:42 META-INF/INDEX.LIST --------- ------- 43764 51 files

但是如果你用xargs unzip,则会得到如下输出:

而使用-exec就没有问题:

ls -exec是有问题的,因为ls会将-e作为它的一个选项解释,即:ls -e
xargs的-l选项
用xargs的-l选项,可以达到跟-exec一样的作用:
forrest@ubuntu:~/work_back/intl-standalone/searchaddbuild/deploy/WORLDS-INF/lib$ find -name "*.jar" | xargs -l unzip -l | grep napoli.properties
404 2010-11-16 17:11 META-INF/autoconf/biz-napoli.properties.vm
666 2010-11-27 01:49 biz/napoli.properties
forrest@ubuntu:~/work_back/intl-standalone/searchaddbuild/deploy/WORLDS-INF/lib$ 
另外,xargs也用{}表示当前处理的参数:
forrest@ubuntu:~/work_back/intl-standalone/searchaddbuild/deploy/WORLDS-INF/lib$ ls | xargs -t -I {} mv {} {}.old
mv activation.jar activation.jar.old 
mv activemq-core-5.2.0.jar activemq-core-5.2.0.jar.old 
。。。

这一命令序列通过在每个名字结尾添加 .old 来重命名在当前目录里的所有文件。-I 标志告诉 xargs 命令插入有{}(花括号)出现的ls目录列表的每一行。

实战

1. SVN提交代码,如果你用-exec提交每个文件,必然被BS。所以最好是用xargs:

$svn st | grep '^[AMD]' | cut -c9- | xargs svn ci -m 

这样只会有一次提交记录。

2. 将lib下面非jar包删除

forrest@ubuntu:~/work_back/intl-standalone/searchaddbuild/deploy/WORLDS-INF/lib$ ls | sed '/.jar/ d' | xargs rm -rf

3. 查找某个文件是否在jar包中

 

forrest@ubuntu:~/work_back/intl-standalone/searchaddbuild/deploy/WORLDS-INF/lib$ find -name "*.jar" -exec unzip -l {} /; | grep napoli.properties 404

2010-11-16 17:11 META-INF/autoconf/biz-napoli.properties.vm 666

2010-11-27 01:49 biz/napoli.properties

但是注意到这个结果只能告诉你有这个文件,但是没有告诉你是那个jar包。如果你想知道是哪个jar包,可以用如下命令(这个暴强的命令来自于海锋,我等膜拜):

 

forrest@ubuntu:~/work_back/intl-standalone/searchaddbuild/deploy/WORLDS-INF/lib$ find -name "*.jar" -exec sh -c 'unzip -l $1 | xargs printf "$1 %s/n"' {} {} /; | grep napoli.properties

./alibaba-intl-commons-napoli-1.0-Dev.jar META-INF/autoconf/biz-napoli.properties.vm

./alibaba-intl-commons-napoli-1.0-Dev.jar biz/napoli.properties

聪妈提供了一个更简单的命令:

 第三种方式——使用单反引号(``)作命令替换command substitution 

达到的效果与xargs非常类似,但是xargs有对命令参数作超长检查,而这个不会。所以不建议在这里使用。但是使用``从上一个命令中获取输入结果是非常有用的。

 

forrest@ubuntu:~/work_back/intl-standalone/searchaddbuild_trunk/deploy/WORLDS-INF/lib$ unzip -l `find . -name "*.jar"` Archive:

./poi-scratchpad-3.0.jar Length Date Time Name --------- ---------- ----- ---- --------- ------- 0 0

files forrest@ubuntu:~/work_back/intl-standalone/searchaddbuild_trunk/deploy/WORLDS-INF/lib$ for i in `find . -name "*.jar"`; do unzip -l $i | grep napoli.properties; done 404

2010-11-16 17:11 META-INF/autoconf/biz-napoli.properties.vm 705

2010-11-26 20:21 biz/napoli.properties

 原文:http://m.blog.csdn.net/article/details?id=6260720

注意事项和报错记录

xargs命令如果遇到文件名里有空格或者换行符,就会出错。因为xargs识别字符段的标识是空格或者换行符,所以如果一个文件名里有空格或者换行符,xargs就会把它识别成两个字符串,自然就出错了。

这时候就需要-print0和-0了。

find -print0表示在find的每一个结果之后加一个NULL字符,而不是默认加一个换行符。find的默认在每一个结果后加一个'\n',所以输出结果是一行一行的。当使用了-print0之后,就变成一行了。

然后xargs -0表示xargs用NULL来作为分隔符。这样前后搭配就不会出现空格和换行符的错误了。选择NULL做分隔符,是因为一般编程语言把NULL作为字符串结束的标志,所以文件名不可能以NULL结尾,这样确保万无一失。

原文链接:https://blog.csdn.net/zjz5740/article/details/115652286

posted on 2022-10-04 01:21  bdy  阅读(147)  评论(0编辑  收藏  举报

导航