强制 gnu-parallel 将替换字符串视为命令

Force gnu-parallel to treat replacement string as command

我想在使用替换字符串时将包含命令列表的文件传递给 gnu-parallel(例如:{%})。

不幸的是,如果使用替换字符串,gnu-parallel 会将文件中的命令解释为 /bin/bash.

的参数

这是我想要做的:

parallel -j 8 'CUDA_VISIBLE_DEVICES=$(({%} - 1)) {}' < commands.txt

其中commands.txt的内容是:

/path/to/binary -arg1 a -arg2 1.0
/path/to/binary -arg1 a -arg2 1.1
...
/path/to/binary -arg1 z -arg2 9.9

但是,这会引发以下错误:

/bin/bash: /path/to/binary -arg1 a -arg2 1.0: command not found

我希望 GNU 与 运行 并行:

CUDA_VISIBLE_DEVICES=0 /path/to/binary -arg1 a -arg2 1.0

环境变量CUDA_VISIBLE_DEVICES的目的是让每个进程运行在不同的GPU上运行(默认所有进程运行在同一个GPU上)。如果我不需要 CUDA_VISIBLE_DEVICES,下面的代码将完美运行:

parallel -j 8 < commands.txt

我该如何解决这个问题?

使用 --colsep ' ' 就可以了:

parallel -j 8 --colsep ' ' 'CUDA_VISIBLE_DEVICES=$(({%} - 1)) {}' < commands.txt

虽然 --colsep 有时可能有效,但并不总是正确的选择。这将创建文件 abcdef:

echo 'touch abc\ def' | parallel -v --colsep ' ' A=B {}

通常情况下,使用 eval:

取消对表达式的引用会更好
echo 'touch abc\ def' | parallel -v eval A=B {}

所以:

parallel -j 8 'eval CUDA_VISIBLE_DEVICES=$(({%} - 1)) {}' < commands.txt

如果您经常使用 $(({%} - 1)),请考虑制作自己的替换字符串:

echo '--rpl {%-1}\ $_=slot()-1' >> ~/.parallel/config
parallel -j 8 'eval CUDA_VISIBLE_DEVICES={%-1} {}' < commands.txt

甚至:

echo '--rpl '"'"'{CUDA} $_="CUDA_VISIBLE_DEVICES=".(slot()-1)'"'" >> .parallel/config
parallel -j 8 'eval {CUDA} {}' < commands.txt