find
我一直在尝试处理with的输出parallel
,这又调用了 shell(需要一些文本替换)。我观察到一些奇怪的行为,我无法向自己解释。
每个目录中都有一堆文件,称它们为file1.xtc
, file2.xtc
.其中一些具有诸如 等名称file1.part0002.xtc
。如果传递的文件find
具有该*.part000x.*
名称,我需要删除该*.part000x.*
位,以便生成的命令类似于
command -f file1.part0001.xtc -s file1.tpr
我使用了find
andparallel
达到了这个效果,但是parallel
的替换(特别是位{.}
)还不够充分(它们删除了.xtc
扩展名,留下了.part0001
单独的),所以这是我用来检查输出的命令:
find 1st 2nd 3rd -name '*.xtc' -print0 | parallel -0 sh -c 'name=""; name="{.}"; echo {.} ${name%.*}.tpr'
如果我使用上面的命令,首先声明name
并为其分配一个空字符串(或任何其他与此相关的内容),结果是
file1.part0001 file1.tpr
根据需要(这些是我的命令需要使用的名称)。但是,如果我运行这个
find 1st 2nd 3rd -name '*.xtc' -print0 | parallel -0 sh -c 'name="{.}"; echo {.} ${name%.*}.tpr'
结果是:
file1.part0001 .tpr
或者它表现得好像$name
不存在一样。
所以我的问题是:
- 这种行为的原因是什么?
- 处理这个问题的首选方式是什么?
第一个问题在这里更重要,因为我上面使用的方法是一种解决方法,虽然不太漂亮,但有效。这不是我第一次需要进行这样的文本替换,这种行为仍然让我感到困惑。
输出sh --version
GNU bash, version 3.2.48(1)-release (x86_64-apple-darwin11)
我安装和使用的较新版本的输出,bash
而不是sh
在上面的命令中(达到相同的效果)(/usr/local/bin/bash --version
)
GNU bash, version 4.2.0(1)-release (i386-apple-darwin11.4.2)
答案1
你的问题与bash无关。事实上,既然你告诉parallel
运行sh
,你甚至可能没有使用bash
。
问题是,正如其文档所示,parallel 并不是 xargs 的真正替代品。相反,它将其参数累积到单个字符串中(它们之间有空格),然后将其解释为一系列命令。因此,就您而言,您有:
sh -c 'name="{.}"; echo {.} ${name%.*}.tpr'
这被解释为
sh -c 'name="{.}";
echo {.} ${name.*}.tpr
由于这是两个单独的命令,并且第一个命令在子 shell ( sh -c
) 中执行,$name
因此未在第二个命令中设置。
现在,您可以在字符串的开头添加任何内容,例如true
:
sh -c 'true; name="{.}"; echo {.} ${name%.*}.tpr'
这将被解释为:
sh -c 'true'
name="{.}"
echo {.} ${name%.*}.tpr'
在这种情况下,对 的调用sh
本质上是一次性的; thenname
在由 set 维护的环境中设置parallel
,最后echo
用name
set 调用。
因此,最简单的解决方案似乎就是删除不必要的调用sh
:
find 1st 2nd 3rd -name '*.xtc' -print0 |
parallel -0 'name={.}; echo {.} "${name%.*}.tpr"'
笔记:根据@StephaneChazelas 给出的提示,我删除了周围的引号{.}
并将它们添加到了周围${name%.*}.ptr
。 parallel 自己引用自己的替换,这会以一些奇怪的方式干扰显式引用。但是,它不会在 shell 替换中添加引号,如果替换可能被分词,则应添加引号。
如果您确实想出于某种原因使用子 shell(或特定的子 shell),另一种选择是使用以下选项-q
:
find 1st 2nd 3rd -name '*.xtc' -print0 |
parallel -0 -q sh -c 'name="{.}"; echo "{.}" "${name%.*}.tpr"'
笔记:如上所述,我调整了报价。在这种情况下,显式-q
禁止对替换的引用,因此您必须显式地引用它们。然而,这是文本引用,不如 shell 引用准确;如果替换包含双引号字符,则该字符不会被转义,因此它将关闭显式引号,破坏命令行并有效引入命令注入漏洞(文件名包含$
、`
或\
人物)。为此,除其他原因外,-q
不鼓励该选择。