在每个文件名之前插入带有标志的扩展 glob

Ale*_*ing 12 bash wildcards

我有一个需要以下语法参数的程序:

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

使用printfxargs支持空分隔的输入:

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相同,但直接使用通配符扩展。

  • 如果文件名包含引号或反斜杠,则很难适当地转义它们。文件名中不允许使用空字符,因此它通常是用于分隔任意(但有效)文件名的最安全字符。 (4认同)
  • 我的理解是 `xargs` 将使用其输入将 args 传递给 `prog`。但是如果 args 太多,它会将它们拆分并多次调用 `prog`。我们是否有可能在错误的位置拆分参数,例如调用 `prog -f a.txt -f b.txt ... -f m.txt -f` 然后调用 `prog n.txt -f o.txt ...` ? (2认同)

Sté*_*las 10

zsh现在有一个Pglob 限定符

对于旧版本,您始终可以使用:

f=(*.txt)
prog -f$^f
Run Code Online (Sandbox Code Playgroud)

$^f启用扩展rcexpandparam选项$f,其中数组以类似于rcshell的样式扩展。

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.txts 作为单独的参数传递,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)


Joh*_*024 9

使用 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文件中。


Gil*_*il' 7

正如 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并浏览菜单以激活推荐的默认值。


Rak*_*rma 5

一。这样做的方法是使用 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)

  • 你为什么要改变目录?只需执行“查找重要文件...”即可。这也将避免@Kusalananda 提到的问题。 (2认同)