我有一个程序需要以下语法的参数:
prog [-f filename | -g filename1 filename2] ...
每个文件名必须以该标志为前缀-f
。例如,以下是有效的调用prog
:
prog -f a.txt -g b.txt c.txt -f d.txt
prog -g a.txt b.txt -g c.txt d.txt
prog -f a.txt -f b.txt -f c.txt
…但以下不是:
prog -f a.txt b.txt
prog -f a.txt -g b.txt
prog a.txt
就我而言,我只关心-f
选择。
我的目录中有很多文件,所有文件都以.txt
.它们看起来像这样:
important-files/
├── a.txt
├── b.txt
├── c.txt
├── d.txt
└── filename with spaces.txt
我想避免需要一一列出每个文件。通常,我会使用一个简单的 glob 来实现:
$ prog important-files/*.txt
但这不起作用,因为它会产生以下无效调用:
$ prog important-files/a.txt important-files/b.txt important-files/c.txt important-files/d.txt 'important-files/filename with spaces.txt'
......当我真的想要这个调用时:
$ prog -f important-files/a.txt -f important-files/b.txt -f important-files/c.txt -f important-files/d.txt -f 'important-files/filename with spaces.txt'
…因为每个文件名必须以 为前缀,-f
以便prog
理解它们不应该被解释为-g
.
使用 glob 并为其扩展的每个文件添加标志的最短方法是什么?
答案1
使用printf
和xargs
支持空分隔输入:
printf -- '-f\0%s\0' important-files/*.txt | xargs -0 prog
printf
在参数上循环格式字符串,因此对于 glob 扩展中的每个文件名,它将打印-f
并用空字符分隔文件名。xargs
然后读取它并将其转换为 的参数prog
。这是--
必需的,因为某些实现在格式字符串中printf
存在前导问题。-
或者,-
可以替换为\055
,这是标准的。
原理与拉克什的回答,而是直接使用通配符扩展。
答案2
对于旧版本,您始终可以使用:
f=(*.txt)
prog -f$^f
$^f
使rcexpandparam
选项对于 的扩展$f
,其中数组以类似于 shell 的方式扩展rc
。
在rc
(或zsh -o rcexpandparam
)中:
f=(*.txt)
prog -f$f
扩展为prog -fa.txt -fb.txt
,有点像您在 csh(或其他支持大括号扩展的 shell)中编写的:prog -f{a.txt,b.txt}
。
(请注意,它的不同之处*.txt(P:-f:)
在于文件名粘在选项上,-ffile.txt
而不是传递两个参数-f
file.txt
。许多命令,包括所有使用标准getopt()
API 来解析选项的命令,都支持将参数传递给选项的两种方式)。
fish
还可以像这样扩展数组:
set f *.txt
prog -f$f
在 中bash
,您可以通过以下方式模拟它:
f=(*.txt)
prog "${f[@]/#/-f}"
和ksh93
:
f=(*.txt)
prog "${f[@]/*/-f\0}"
对于-f
和 相应的file.txt
s 作为单独的参数传递,另一个选项是zsh
使用其数组压缩运算符:
o=-f f=(*.txt); prog ${o:^^f}
您可以根据您的-g
选择扩展该范围。假设有偶数个txt
文件:
o=(-g '') f=(*.txt); prog ${o:^^f}
-g
一次会传递两个文件的选项。与您得到的类似:
printf -- '-g\0%s\0%s\0' *.txt | xargs -r0 prog
答案3
使用 bash,尝试:
args=()
for f in important-files/*.txt
do
args+=(-f "$f")
done
prog "${args[@]}"
请注意,这不仅适用于包含空格的文件名,也适用于包含单引号或双引号甚至换行符的文件名。
轻松互动使用
为了方便交互使用,定义一个函数:
progf() { args=(); for f in "$@"; do args+=(-f "$f"); done; prog "${args[@]}"; }
该函数可以如下使用:
progf important-files/*.txt
要使函数定义永久存在,请将其放入~/.bashrc
文件中。
答案4
一。方法是使用 GNU find/xargs duo:
cd important-files && \
find . -name '*.txt' -printf '-f\0%p\0' | xargs -0 -r prog