我正在尝试构建一个包装器来多次执行一个工具,然后连接一些结果。我想将两组文件传递给我的包装器脚本,然后对每对文件运行该工具。我希望它的行为如下:
multitool.sh -a a*.txt -b b*.txt
(扩展通配符以匹配所有可用文件)
然后在里面,我对、、、等运行multitool.sh
该工具,其中 a 和 b文件的数量可变。a1.txt b1.txt
a2.txt b1.txt
a1.txt b2.txt
a2.txt b2.txt
我按照本教程解释了选项处理的基础知识,并且我能够使用它getops
来处理-h
,但不能处理其他任何事情。
这就是我现在的情况:
#!/bin/bash
while getopts ":hpg:" option; do
case $option in
h) # display help
echo "-h to print this help and exit, -p is peak files, -g is bedgraph files."
exit;;
p) # get list of peak files to process
l_peaks=$OPTARG;;
g) # get list of bedgraph files to process
l_bgraphs=$OPTARG;;
\?) # Invalid option
echo "Error! Invalid option!"
echo "-h to print this help and exit, -p is peak files, -g is bedgraph files."
exit;;
esac
done
echo "$l_peaks"
echo "$l_bgraphs"
我正在与一个不太懂计算机的团队合作,所以如果我可以将包装器保持为简单的一行执行,那就最好了。
我怎样才能将这些文件列表作为一个选项传递?
不,不要将多个文件名压缩成一个字符串。首先,因为 shell 不支持这个 (*),而且一般来说,文件名可以是任何字符串。没有一个字符或字符序列可以用作文件名中无效的分隔符。除了 NUL 字节,但无论如何您都不能将其作为命令行参数传递。
(* 可能的例外是 zsh。)
因此,只需用您获得的文件名作为参数填充两个数组即可。从那里,循环遍历列表也很简单。
但是,
getopts
当它看到非选项参数时会停止,所以我们必须手动查找一些分隔符字符串。(同样,这必然是文件名,但至少它需要匹配完整的文件名,否则事情就会变得混乱。让我们选择::
,如果你有这样的文件名,一定要将其作为传递./::
。)运行结果如下:
根据需要调整 getopts 循环,但记得保留 shift 命令以清除已处理的 getopts 选项。
也就是说,我上面假设您想将第一个 A 与所有 B 结合起来,将第二个 A 与所有 B 结合起来,等等。
相反,如果您希望第一个 A 与第一个 B 配对,第二个 A 与第二个 B 配对,等等,那么最后一个循环将需要类似于:
不过,正如 @terdon 在评论中指出的那样,GNU parallel可以直接做到这一点。
就像这样:
它通过 shell 传递给定的命令,因此特殊字符需要一些额外的引号。当然,您需要小心使用分隔符,因为额外的引号
+
相当有意义。如果您愿意复制 的方法,
find
并引用两个通配符 glob 模式,则可以使用脚本适当地扩展值。在此配置中,引用的模式随后由 shell(因此由getopts
)处理,每个模式仅作为一个参数。下面是一个示例,我几乎不加改变地采用了您现有的代码,但为了方便起见,对其进行了缩减。您的代码布局更好,但有趣的部分是对
-a
和-b
参数的处理:例子
命令处理需要多个参数的选项的常见方法之一是多次调用这些选项,如下所示:
(以上显示了将参数作为单独参数或在同一参数中提供的示例)
要将其应用于 glob,如果使用
zsh
,您可以执行以下操作:其中
P[prefix]
glob 限定符在每个生成的文件之前插入一个额外的-i
参数,以便最终运行cmd -vv -i file1 -i file2 ... -o out
。(还请注意
n
限定符,以确保文件按数字顺序排序,因此file10
位于之后file9
而不是之间file1
,就像file2
默认词典顺序一样)。或者:
或者:
或者,
histsubstpattern
启用该选项:在每个文件名前面添加
-i
(在同一个参数中),这样它最终会运行cmd -vv -ifile1 -ifile2 ... -o out1
。在 bash(或 bash 从中复制了该语法的 ksh93)中,您可以执行以下操作:
但请注意,bash 没有与 zsh 的
n
限定符等效的限定符,因此将在和file10
之间排序,并且除非您打开该选项,否则如果没有匹配的文件,它将使用文字作为参数进行调用。file1
file2
failglob
cmd
-ifile*
在
cmd
脚本本身中,您可以执行以下操作:我们知道文件列表将是一个偶数,因为它们应该相互配对。利用这一点,我们可以对所有给定的文件名进行循环,并在迭代它们时对它们进行计数。一旦我们到达中间点,我们就可以开始将列表中的第一个名称与循环中的当前名称配对,然后
shift
(删除第一个列表项)并重复。这是可行的,因为
for
循环总是迭代静态列表。因此,我们通过循环“保存”名称列表,并且可以在不干扰循环本身的情况下修改循环内的位置参数列表。请注意,所有这些都不需要
bash
shell;我们可以使用更简单的sh
shell 来运行它。测试:
如果需要进行选项解析,请在配对文件名的代码之前执行此操作: