如何在正则表达式操作中确定当前工作文件的名称?

如何在正则表达式操作中确定当前工作文件的名称?

如果我尝试对如下文件运行正则表达式操作(示例):

$ 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.firmtest2.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.binf

请参阅手册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 在她的回答中所解释的那样,*.extshell 会将其扩展为当前工作目录中以字符串结尾的所有文件.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而不是mvfirst。

我并不是在寻找类似重命名命令的东西,因为它必须适用于所有事情,而不仅仅是重命名文件。

再次强调,*就 shell 而言,它处理当前目录中的文件。这与正则表达式不同。您的方法必须根据您使用的工具进行量身定制。

相关内容