linux 为什么组合的find和rename命令在bash shell下不能正常工作?

jgwigjjp  于 2023-04-29  发布在  Linux
关注(0)|答案(2)|浏览(293)

我有一个文件夹的文本文件格式为四位数字,空格,字母数字字符串。例如:

$ touch '0001 jko0001.txt'  '0002 jko0002.txt'  '0003 jko0003.txt'

$ ls
'0001 jko0001.txt'  '0002 jko0002.txt'  '0003 jko0003.txt'

我想重命名的文件,以便前导数字和空间被删除。因为我有很多文件,所以我使用find将文件名传递给rename。我尝试使用以下命令执行此操作:
find . -type f -name '*.txt' -print0 | xargs -0 rename -n -- 's/^\d{4}\s+//' {} +
然而,这失败了。(是的,-n只是打印出更改而不重命名文件。即使我删除它也会失败)。
有趣的是,如果我将命令拆分为多个部分,它确实可以工作:

$ find . -type f -name '*.txt'
./0003 jko0003.txt
./0002 jko0002.txt
./0001 jko0001.txt

$ rename -n -- 's/^[0-9]{4}\s+//' *.txt
0001 jko0001.txt -> jko0001.txt
0002 jko0002.txt -> jko0002.txt
0003 jko0003.txt -> jko0003.txt

$ bash --version
GNU bash, version 5.1.16(1)-release (x86_64-pc-linux-gnu)

但是当与xargs结合使用时,它就失败了。为什么?
此外,我甚至不能让它与find-execdir一起工作:
find . -type f -name '*.txt' -execdir rename -n -- 's/^\d{4} //' {} \;
这些都不管用

find . -type f -name '*.txt' -print0 | xargs -0 rename -n -- 's/^\d{4}\s+//' "{}" +
find . -type f -name '*.txt' -print0 | xargs -0 rename -n -- 's/^[0-9]{4}\s+//' "{}" +
find . -type f -name '*.txt' -execdir rename -n -- 's/^\d{4} //' {} \;
find . -type f -name '*.txt' -execdir rename -n -- 's/^\d{4} //' '{}' \;

先谢谢你了!

bksxznpy

bksxznpy1#

这里至少有两个问题。首先,find传递的是文件的路径,而不仅仅是文件名(是的,即使是-execdir)。因此,将-d选项添加到rename,告诉它只处理文件名,而不是完整路径。
其次,您将find -exec语法与xargs语法混合在一起。具体来说,命令末尾的{} +是您在find -exec中使用的,* 而不是在xargs中使用的(注意:在某些模式下,xargs像这样使用{},但它从不使用+)。要修复它,请删除{} +并使用标准xargs语法:

$ find . -type f -name '*.txt' -print0 | xargs -0 rename -n -d -- 's/^\d{4}\s+//'
rename(./0003 jko0003.txt, ./jko0003.txt)
rename(./0001 jko0001.txt, ./jko0001.txt)
rename(./0002 jko0002.txt, ./jko0002.txt)

或者跳过xargs,直接使用find -exec(这次 * 使用 * {} +):

$ find . -type f -name '*.txt' -exec rename -n -d -- 's/^\d{4}\s+//' {} +
rename(./0003 jko0003.txt, ./jko0003.txt)
rename(./0001 jko0001.txt, ./jko0001.txt)
rename(./0002 jko0002.txt, ./jko0002.txt)

顺便说一句,在解决这样的问题时,有时将echo放在problem命令的前面会很有帮助,以了解传递给它的参数是什么:

$ find . -type f -name '*.txt' -print0 | xargs -0 echo rename -n -- 's/^\d{4}\s+//' {} +
rename -n -- s/^\d{4}\s+// {} + ./0003 jko0003.txt ./0001 jko0001.txt ./0002 jko0002.txt
                           ^^^^ this is the problem

但这有时会误导人,因为(除其他外)它失去了参数内的空格和参数之间的空格之间的区别。用printf '%q\n'替换echo有时会更好(尽管它有其他问题,并且并非所有外部printf实现都支持%q)。

dauxcl2d

dauxcl2d2#

像这样,使用Perl的rename只要你使用当前目录,就不需要find | xargs rename

$ rename -n 's|^\./\d{4}\s+||' ./*.txt
rename(./0001 jko0001.txt, jko0001.txt)
rename(./0002 jko0002.txt, jko0002.txt)
rename(./0003 jko0003.txt, jko0003.txt)

请注意,每个文件上都有前导./
删除-n开关,又名 * 干运行 * 当您的尝试是令人满意的重命名为真实的

    • 递归**方式,不带find
shopt -s globstar # needed for bash, not zsh
rename -n 's|^\./\d{4}\s+||' ./**/*.txt

相关问题