将文件名通过管道传递给 zmv

将文件名通过管道传递给 zmv

我发现在管道中使用 find 然后使用 grep -v 来过滤文件而不是开发复杂的正则表达式模式要容易得多。但是,当我像这样通过管道将某些内容传递给 zmv 时:

find | grep -v TFLM | zmv "(*)" "TFLM \$1"

它只是忽略输入并继续将转换应用于所有文件。有没有办法告诉它使用管道输入?我想我可以移走过滤掉的文件,然后使用 zmv,但这并不是真正的解决方案。

答案1

zmv不从标准输入读取。

我可能会在这里使用findwith mv,或者zmv使用zsh文件名 glob ,但不会两者都使用,并且grep根本不涉及。grep应该对分成行的文本而不是文件名(可能包括嵌入的换行符)进行使用。

使用文件名 glob(仅作用于当前目录,并且仅作用于非隐藏文件):

zmv '^TFLM *' 'TFLM $f'

递归地,不重命名目录,包括隐藏文件和隐藏目录中的文件,如下find所示:

zmv '(**/)(^TFLM *)(#qD^/)' '${1}TFLM $2'

使用find(但没有 的冲突处理zmv,因此添加一个-i安全选项):

find . ! -type d ! -name 'TFLM *' -exec sh -c '
    for pathname do
       mv -i "$pathname" "${pathname%/*}/TFLM ${pathname##*/}"
    done' sh {} +

bash(仅适用于当前目录,不包括隐藏文件):

shopt -s extglob
for name in !(TFLM *); do
    mv -i -- "$name" "TFLM $name"
done

或者,bash使用 Perlrename实用程序:

shopt -s extglob
rename 's|/|/TFLM |' ./!(TFLM *)

(如果没有./,如果文件名以 开头,某些变体将失败-。并非所有变体都支持--标记选项结尾)。

答案2

你所做的并不是如何工作findgrep而是zmv如何工作。首先,您用于find搜索文件,然后grep搜索模式;这没有任何意义。该命令find具有内置模式匹配,例如GNU find从基本开始-name-iname-path等等-regex。如果您愿意,您甚至可以更改正则表达式的语法,使用-regextype.这不仅是你做的事情不快或者涉及太多命令,最糟糕的是你的命令很容易出错,例如如果文件内部有空间。

更好的是纯粹find使用-exec选项后跟外部命令,如mv.只要小心谨慎,这个解决方案就可以在不同的系统之间非常方便地移植。

但是,既然您正在使用它zsh,那么它就需要使用它的所有荣耀,所以只需添加-vn选项zmv并尝试不同的模式,很可能您想要

zmv -vn '(^(*TFLM*))' 'TFLM $1'

-v意味着详细并-n阻止执行,仅打印将要执行的操作(这对于测试非常有用)。

答案3

我的工作方式与你类似,我更喜欢将grep命令分层到最后。很难抗拒这种冲动,但在这种情况下,你必须这样做,或者在心里记住,当你想开始按照清单采取行动时,find | ...你必须开始拉进来xargs。当您到达这一点时,就该切换到find <regex> -exec ....

在您的场景中,您可能会考虑在此处使用的模式如下所示:

$ find . ! -name "*TFLM*" -exec zmv "{}" "TFLM {}" \;

但这是行不通的,根据 @StephaneChazelas 的评论:

由于zmv是zsh函数,所以不能通过find直接执行。即使有人围绕 zmv 制作了一个独立的脚本包装器,将其称为 -exec zmv "{}"...也没有多大意义(完全违背了 zmv 的目的),并且会引入命令注入漏洞

因此,您只剩下更传统的选择,即使用 U&L 问答中所示的方法之一,标题为:批量重命名文件

或者zmv直接使用本身进行重命名。既然您正在使用zsh,很可能您甚至根本zmv不需要寻求帮助。find

$ zmv "(^*TFLM*)" "TFLM \$1"

笔记:请对我的建议持保留态度zmvzsh我实际上并不使用 Zsh,我通常整天都在 Bash 中。

参考

相关内容