为每个匹配项执行一次命令的通配符

Wildcard that executes command once for each match

备用标题:如何在没有循环或 xargs 的情况下循环。

最近,我转向了 zsh,因为它有很多功能。我很好奇:是否有扩展通配符的功能,使得命令对每个匹配项执行一次,而不是一次对所有匹配项执行一次。

例子

命令ebook-convert input_file output_file [options]只接受一个输入文件。当我要转换多个文件时,我必须手动多次执行命令或使用循环,例如:

for i in *.epub; do 
    ebook-convert "$i" .mobi
done

我想要一个功能类似于循环的通配符,这样我就可以节省几次击键。让所述通配符为 。命令

ebook-convert ⁂.epub .mobi

应该扩展到

ebook-convert 1stMatch.epub .mobi
ebook-convert 2ndMatch.epub .mobi
ebook-convert 3rdMatch.epub .mobi
...

仍然对其他答案感兴趣

我接受了对我有用的答案(感谢 Grisha Levit)。但是,如果您知道其他具有此类功能的 shell,比编写循环更短的替代命令,甚至是使用通配符扩展 zsh 的方法,我们将不胜感激。

您可以在 zsh 中签出 zargs

This function has a similar purpose to GNU xargs. Instead of reading lines of arguments from the standard input, it takes them from the command line

zshcontrib(1): OTHER FUNCTIONS, zargs

所以,我们可以这样写:

autoload -Uz zargs
zargs -I⁂ -- *.epub -- ebook-convert ⁂ .mobi

PS:如果您需要捕获模式的某些部分以构建命令,您会发现 zmv 很方便。

您可以自己编写一个脚本来执行此操作:

#!/bin/bash

command=""
shift
if
  [[ $# -lt 3 ]]
then
  echo "Usage: command file/blog arg1, arg2..."
  exit 1
fi

declare -a files=()
while [ "" != "--" ]
do
  [ "" ] || continue
  files+=("")
  shift
done

if
  [ "" != "--" ]
then
  echo "Separator not found : end file list with --"
  exit 1
fi
shift

for file in "${files[@]}"
do
  "$command" "$file" "$@"
done

你可以这样调用它(假设脚本被调用 apply_to)。

apply_to command /dir/* arg1, arg2...

编辑

我修改了代码以在命令开头插入文件名。

so that I can save a few keystrokes

好的,假设您输入了

ebook-convert *.epub .mobi

…现在您意识到这行不通 — 您需要编写一个循环。你通常会做什么?可能是这样的:

  • 在行尾添加; done
  • CtrlA 转到行首
  • 输入for i in
  • 等等……

这看起来很适合 readline 键盘宏:

让我们根据 readline commands 和常规按键来写出这些步骤:

end-of-line                    # (start from the end for consistency)
; done                         # type in the loop closing statement
character-search-backward *    # go back to the where the glob is
shell-backward-word            # (in case the glob is in the mid-word)
shell-kill-word                # "cut" the word with the glob
"$i"                           # type the loop variable
beginning-of-line              # go back to the start of the line
for i in                       # type the beginning of the loop opening
yank                           # "paste" the word with the glob
; do                           # type the end of the loop opening

正在创建绑定:

对于上面使用的任何没有 key-binding 的 readline 命令,我们需要创建一个。我们还需要为正在创建的新宏创建绑定。

除非您已经做了很多 readline 自定义,运行使用下面的命令将为当前 shell 设置绑定。这使用默认绑定,如 \C-eend-of-line.

bind '"\eB": shell-backward-word'
bind '"\eD": shell-kill-word'

bind '"\C-i": "\C-e; done\e\C-]*\eB\eD \"$i\"\C-afor i in\C-y; do "'

绑定也可以进入 inputrc 文件以保持持久性。

使用快捷方式:

设置后:

  1. 输入类似

    的内容
    ebook-convert *.epub .mobi
  2. CtrlI
  3. 行会变换成

    for i in *.epub; do ebook-convert "$i" .mobi; done

如果你想立即运行命令,你可以修改宏以附加一个\C-j作为最后一次按键,这将触发accept-line(与点击[相同=54=]Return).

for 循环有一个您可能喜欢的缩写形式:

for f (*.epub) ebook-convert $f .mobi