我有一个需要以下语法参数的程序:
prog [-f filename | -g filename1 filename2] ...
Run Code Online (Sandbox Code Playgroud)
每个文件名都必须以-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
Run Code Online (Sandbox Code Playgroud)
……但以下不是:
prog -f a.txt b.txt
prog -f a.txt -g b.txt
prog a.txt
Run Code Online (Sandbox Code Playgroud)
就我而言,我只关心-f
选项。
我在一个目录中有很多文件,所有文件都以.txt
. 它们看起来像这样:
important-files/
??? a.txt
??? b.txt
??? c.txt
??? d.txt
??? filename with spaces.txt
Run Code Online (Sandbox Code Playgroud)
我想避免需要一一列出每个文件。通常,我会为此使用一个简单的 glob:
$ prog important-files/*.txt
Run Code Online (Sandbox Code Playgroud)
但这不起作用,因为它产生以下无效调用:
$ prog important-files/a.txt important-files/b.txt important-files/c.txt important-files/d.txt 'important-files/filename with spaces.txt'
Run Code Online (Sandbox Code Playgroud)
…当我真的想要这个调用时:
$ 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'
Run Code Online (Sandbox Code Playgroud)
......因为每个文件名必须为前缀-f
,以便prog
了解他们不应该这样解释-g
。
使用 glob 并在它扩展到的每个文件前面加上一个标志的最短方法是什么?
mur*_*uru 14
使用printf
和xargs
支持空分隔的输入:
printf -- '-f\0%s\0' important-files/*.txt | xargs -0 prog
Run Code Online (Sandbox Code Playgroud)
printf
在参数上循环格式字符串,因此对于 glob 扩展中的每个文件名,它将打印-f
并以空字符分隔文件名。xargs
然后读取它并将其转换为 的参数prog
。在--
因为一些实现需要printf
有领先的麻烦-
格式字符串。或者,-
可以替换为\055
,这是标准的。
原理与Rakesh's answer相同,但直接使用通配符扩展。
Sté*_*las 10
对于旧版本,您始终可以使用:
f=(*.txt)
prog -f$^f
Run Code Online (Sandbox Code Playgroud)
$^f
启用扩展rcexpandparam
选项$f
,其中数组以类似于rc
shell的样式扩展。
在rc
(或zsh -o rcexpandparam
):
f=(*.txt)
prog -f$f
Run Code Online (Sandbox Code Playgroud)
扩展为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
Run Code Online (Sandbox Code Playgroud)
在 中bash
,您可以模拟它:
f=(*.txt)
prog "${f[@]/#/-f}"
Run Code Online (Sandbox Code Playgroud)
与ksh93
:
f=(*.txt)
prog "${f[@]/*/-f\0}"
Run Code Online (Sandbox Code Playgroud)
For-f
和相应的file.txt
s 作为单独的参数传递,zsh
使用其数组压缩运算符的另一个选项:
o=-f f=(*.txt); prog ${o:^^f}
Run Code Online (Sandbox Code Playgroud)
您可以根据自己的-g
选择扩展它。假设有偶数个txt
文件:
o=(-g '') f=(*.txt); prog ${o:^^f}
Run Code Online (Sandbox Code Playgroud)
将一次传递-g
两个文件的选项。类似于你会得到的:
printf -- '-g\0%s\0%s\0' *.txt | xargs -r0 prog
Run Code Online (Sandbox Code Playgroud)
使用 bash,尝试:
args=()
for f in important-files/*.txt
do
args+=(-f "$f")
done
prog "${args[@]}"
Run Code Online (Sandbox Code Playgroud)
请注意,这不仅适用于包含空格的文件名,还适用于包含单引号或双引号甚至换行符的文件名。
为了便于交互使用,定义一个函数:
progf() { args=(); for f in "$@"; do args+=(-f "$f"); done; prog "${args[@]}"; }
Run Code Online (Sandbox Code Playgroud)
该函数可以如下使用:
progf important-files/*.txt
Run Code Online (Sandbox Code Playgroud)
要使函数定义永久化,请将其放在您的~/.bashrc
文件中。
正如 thrig 在评论中已经提到的那样:
zsh -c 'prog *.txt(P:-f:)'
Run Code Online (Sandbox Code Playgroud)
这使用glob 限定符 P
,它为每个 glob 匹配添加一个单独的单词。像几乎所有 zsh 的 glob 限定符一样,bash 没有类似的东西。
如果要完成prog
,请以zsh
交互方式运行,然后键入prog *.txt(P:-f:)
。第一次以交互方式运行 zsh 时,它会提示您设置配置。如果您不关心 zsh,请选择将选项设置为空白.zshrc
(但是您会错过一些需要明确激活的 zsh 功能)或选择1
并浏览菜单以激活推荐的默认值。
一。这样做的方法是使用 GNU find/xargs duo:
cd important-files && \
find . -name '*.txt' -printf '-f\0%p\0' | xargs -0 -r prog
Run Code Online (Sandbox Code Playgroud)