Shell中如何获取{}的值?含xargs多线程及单引号嵌套问题
解决xargs多线程环境下变量传递与单引号嵌套awk的问题
先来说你第一个遇到的变量输出为空的问题:这是因为shell变量解析顺序搞的鬼!当你用双引号包裹sh -c的命令内容时,$filename会被当前执行这条命令的shell先解析,而这时候filename={}还没被xargs赋值呢,所以自然输出空字符串。改用单引号是完全正确的选择——单引号会把里面的内容原封不动传给子shell,让filename={}在xargs启动的子进程里真正生效。
接下来是单引号里嵌套awk的问题,确实挺头疼的,因为awk脚本通常也用单引号包裹,直接嵌套会触发shell的语法错误。这里给你三个实用的解决办法:
方法1:用双引号包裹awk脚本,转义$符号
把awk的脚本用双引号括起来,同时把awk里的$转义成\$,这样当前shell就不会把它当成自己的位置参数,而是完整传给awk:
find . -name "*.network" -o -name "*.index" | xargs -n 1 -I {} -P 5 sh -c 'ncols=3; filename={}; echo "$filename" | awk "{print \$1}"'
比如这里的\$1会被awk解析成第一列,而不是被shell当成当前脚本的第一个参数。
方法2:用ASCII码拼接单引号
这是shell里处理单引号嵌套的经典技巧:用'\''来表示一个单引号。具体来说,就是先闭合当前的单引号,添加一个转义的单引号,再重新打开单引号。这样awk的脚本就能被正确的单引号包裹:
find . -name "*.network" -o -name "*.index" | xargs -n 1 -I {} -P 5 sh -c 'ncols=3; filename={}; echo "$filename" | awk '\''{print $1}'\'''
拆解一下:sh -c的单引号内容到awk这里时,'\''相当于在单引号字符串里插入了一个单引号,这样awk的{print $1}就被正确包裹了。
方法3:把awk脚本存为变量再传递
先把awk的脚本定义成一个变量,再用双引号传给awk,同样要注意转义$:
find . -name "*.network" -o -name "*.index" | xargs -n 1 -I {} -P 5 sh -c 'ncols=3; filename={}; awk_script="{print \$1}"; echo "$filename" | awk "$awk_script"'
这种方式可读性会好一点,尤其是awk脚本比较长的时候。
最后再提两个小细节:
- 一定要给
$filename加上双引号,也就是"$filename",避免文件名里有空格、特殊字符时出现意外错误; - 用
xargs -P开启多线程时,如果后续操作涉及写入同一个文件,记得加锁(比如用flock),防止多个进程同时写入导致数据混乱。
内容的提问来源于stack exchange,提问作者Lily Hao
相关产品推荐
相关产品推荐

