如果我尝试对如下文件运行正则表达式操作(示例):
$ mv *.ext *.new.ext
那是行不通的。
如何确保当我批量重命名或对多个文件执行操作时,它们保留其当前名称(不是扩展名,只是名称)?
注意:我不是在寻找类似rename
命令的东西,因为这必须适用于一切,而不仅仅是重命名文件。
注 2:好的,这些答案很好,解释了我以前不太理解的事情。谢谢!
我尝试使用的命令名为firmtool
,并在 3DS 自制场景中使用。
它将 .bin 文件打包成 .firm 文件,我希望它在目录中的所有 .bin 文件上运行,并打包它们同时保留原始名称。
以下是我需要的命令语法firmtool
:
firmtool build test.firm -n 0x23F00000 -e 0 -D arm9loaderhax.bin -A 0x23F00000 -C NDMA
其中test.firm
是输出,arm9loaderhax.bin
是输入。
我想运行此命令,如果我有一个包含和test1.bin
的目录,test2.bin
它将生成test1.firm
和test2.firm
。请告诉我是否可以使用正则表达式或循环来完成此命令。感谢您的帮助!
答案1
嗯。你不能1,不是普遍存在的。
*
是通配符。shell扩展它。当你输入
*.ext
shell 将其扩展为所有匹配文件的列表,例如:
1.ext 2.ext whatever.ext
这就是传递给命令的内容,在你的情况下mv
。如果mv
接收两个以上的参数,最后一个必须是一个目录,所以
mv *.ext *.new
失败,因为*.new
不是目录,因为mv
正在接收一长串参数。
shell 本身不支持正则表达式,但 Linux 有许多实用程序支持正则表达式,例如 perl rename...
rename -n 's/(.*)\.ext/$1.new/' *
并在测试后删除-n
以真正重命名...
但是您可以捕获变量中的不同名称并使用它来循环文件......
for f in *.ext; do echo mv -v -- "$f" "${f/.ext/.new}"; done
并echo
在测试后删除以完成重命名...您几乎可以使用任何带有 的命令for
,而不仅仅是mv
。以下是根据您的问题略有不同的示例:
for f in *.bin; do
firmtool build "${f%.bin}.firm" -n 0x23F00000 -e 0 -D "$f" -A 0x23F00000 -C NDMA
done
在这种情况下将扩展为去掉后缀的${f%.bin}
变量的值(如果它具有这样的后缀;否则它将扩展为的值)。f
.bin
f
请参阅手册Shell 参数扩展对于其他扩展类型。
1等待被证明是错误的
答案2
发生了什么以及为什么 mv 命令会失败
让我们实际检查一下使用时内部发生的情况strace
。
$ touch file{1,2}.ext
$ strace -e trace=execve mv *.ext *.new.ext
execve("/bin/mv", ["mv", "file1.ext", "file2.ext", "*.new.ext"], [/* 80 vars */]) = 0
mv: target '*.new.ext' is not a directory
+++ exited with 1 +++
正如 Zanna 在她的回答中所解释的那样,*.ext
shell 会将其扩展为当前工作目录中以字符串结尾的所有文件.ext
。您在该示例中看到的是,它*.new.ext
没有被扩展,正是因为没有与这种模式匹配的文件,即任何地方都没有文件foo.new.ext
,因此 shell 直接将其作为参数传递。
该mv
命令反过来将其视为以下语法:
mv [OPTION]... SOURCE... DIRECTORY
如果你在最后一个参数中确实有一个现有目录,那么这将起作用
$ mv *.ext test_dir
$ tree
.
└── test_dir
├── file1.ext
└── file2.ext
你真正想做的事
据我了解,您最初的目标是将每个*.ext
文件重命名为带有.new.ext
结尾的相应文件。这可以通过重命名(如 Zanna 所示)或通过 shell 中的循环来完成:
$ for f in ./*.ext ; do mv "$f" "$(basename "$f")".new.ext; done
$ ls
file1.ext.new.ext file2.ext.new.ext
注意使用basename
来提取 之前的部分.ext
,并使用./*.ext
。使用 if./*
是为了防止文件名可能存在前导的问题-
,并且通常建议使用这种方法,而不是裸*
补充笔记
在您的问题中您指出:
如果我尝试对文件运行正则表达式操作
这是问题的一部分。*
在 shell 中是路径名扩展,它扩展为当前工作目录中的文件列表。它与正则表达式不同,在正则表达式中abc.*
,您将以 abc 开头的字符串与任何最后一个字符匹配零次或多次。
如何确保当我批量重命名或对多个文件执行操作时,它们保留其当前名称(不是扩展名,只是名称)?
您可以使用循环遍历文件列表for
,并使用命令主动提取文件名basename
,就像我上面展示的那样。prename
如果您在正则表达式中正确定义替换,则可以使用它。它的优点prename
是它具有试运行功能(-n
开关),其中不执行实际重命名。相同的想法可以用于迭代,其中您使用echo
而不是mv
first。
我并不是在寻找类似重命名命令的东西,因为它必须适用于所有事情,而不仅仅是重命名文件。
再次强调,*
就 shell 而言,它处理当前目录中的文件。这与正则表达式不同。您的方法必须根据您使用的工具进行量身定制。