尝试小写多个文件名的部分时,重命名返回“不允许裸词”

Sam*_*amo 20 command-line files rename batch-rename

我的 Ubuntu 16.04 上的文件夹中有两个文件:

a1.dat
b1.DAT
Run Code Online (Sandbox Code Playgroud)

我想重命名b1.DAT为,b1.dat因此文件夹中会有以下文件:

a1.dat
b1.dat
Run Code Online (Sandbox Code Playgroud)

我试过(不成功):

$ rename *.DAT *.dat
Bareword "b1" not allowed while "strict subs" in use at (user-supplied code).
Bareword "DAT" not allowed while "strict subs" in use at (user-supplied code).
Run Code Online (Sandbox Code Playgroud)

$ find . -iname "*.DAT" -exec rename DAT dat '{}' \;
Bareword "DAT" not allowed while "strict subs" in use at (user-supplied code).
Bareword "DAT" not allowed while "strict subs" in use at (user-supplied code).
Run Code Online (Sandbox Code Playgroud)

搜索此导致没有有意义的解决方案......

Zan*_*nna 23

该错误看起来像是来自 Perl rename。您需要使用引用,但您只需要指定要更改的部分,搜索和替换样式。语法是这样的:

rename -n 's/\.DAT/\.dat/' *
Run Code Online (Sandbox Code Playgroud)

-n测试后删除以实际重命名文件。

要包含隐藏文件,请在运行命令之前更改 glob 设置:

shopt -s dotglob
Run Code Online (Sandbox Code Playgroud)

如果要递归重命名文件,可以使用

shopt -s globstar
rename -n 's/\.DAT/\.dat/' **
Run Code Online (Sandbox Code Playgroud)

或者,如果当前目录中或下方有许多不以 结尾的.DAT路径,则最好在第二个命令中指定这些路径:

rename -n 's/\.DAT/\.dat/' **/*.DAT
Run Code Online (Sandbox Code Playgroud)

如果您的文件具有各种不同的名称而不以.DAT. [1]

要关闭这些设置,您可以使用shopt -u,例如shopt -u globstar,但默认情况下它们是关闭的,并且会在您打开新外壳时关闭。

如果这导致参数列表过长,您可以使用,例如find

find -type f -name "*.DAT" -exec rename -n -- 's/\.DAT/\.dat/' {} \;
Run Code Online (Sandbox Code Playgroud)

或更好

find -type f -name "*.DAT" -exec rename -n -- 's/\.DAT/\.dat/' {} +
Run Code Online (Sandbox Code Playgroud)

使用find ... -execwith+比 using 更快,\;因为它根据找到的文件构造一个参数列表。本来我认为你不可能使用它,因为你提到你遇到了argument list too long问题,但现在我知道列表也会根据需要巧妙地分解为命令的多次调用以避免该问题[2] .

由于rename将以相同的方式处理每个文件名,因此参数列表的长度无关紧要,因为它可以安全地跨多个调用拆分。如果您使用的命令-exec不接受多个参数或要求其参数按特定顺序排列或出于任何其他原因拆分参数列表将导致不希望发生的事情,您可以使用\;,这会导致命令被调用一次对于找到的每个文件(如果参数列表对于其他方法来说太长,这将需要很长时间!)。


非常感谢Eliah Kagan为改进此答案提供的非常有用的建议:

[1] globbing 时指定文件名
[2] find ... -execwith+拆分参数列表


hee*_*ayl 7

你可以做:

rename -n 's/DAT$/\L$&/' *.DAT
Run Code Online (Sandbox Code Playgroud)

删除-n以进行实际重命名。

  • glob 模式*.DAT匹配.DAT以当前目录结尾的所有文件

  • rename的替换中,DAT$匹配DAT在最后

  • \L$&使整个匹配小写;$&指整场比赛

如果您只想为b1.DAT

rename -n 's/DAT$/\L$&/' b1.DAT
Run Code Online (Sandbox Code Playgroud)

例子:

% rename -n 's/DAT$/\L$&/' *.DAT
rename(b1.DAT, b1.dat)
Run Code Online (Sandbox Code Playgroud)


Eli*_*gan 6

其他 答案已经解决了这个问题的两个主要方面之一:如何成功执行您需要的重命名操作。这个答案的目的是解释为什么你的命令不起作用,包括rename命令上下文中那个奇怪的“不允许裸词”错误消息的含义。

这个答案的第一部分是关于renamePerl 和 Perl之间的关系以及如何rename使用您传递给它的第一个命令行参数,即它的代码参数。第二部分是关于 shell 如何执行扩展——特别是 globbing——来构造一个参数列表。第三部分是关于 Perl 代码中发生了什么导致“不允许裸字”错误。最后,第四部分总结了在输入命令和获取错误之间发生的所有步骤。

1. 当出现rename奇怪的错误信息时,将“Perl”添加到您的搜索中。

在 Debian 和 Ubuntu 中,该rename命令是一个执行文件重命名的Perl脚本。在较旧的版本上——包括在撰写本文时仍受支持的14.04 LTS——它是一个指向(间接)命令的符号链接。在较新的版本中,它指向较新的命令。这两个 Perl 重命名命令的工作方式大致相同,我将在本答案的其余部分中提及它们。prenamefile-renamerename

当您使用该rename命令时,您不仅仅是在运行其他人编写的 Perl 代码。您也在编写自己的 Perl 代码并告诉rename运行它。这是因为您传递给命令的第一个命令行参数rename,除了像 那样的选项参数-n由实际的 Perl 代码组成。该rename命令使用此代码对您作为后续命令行参数传递的每个路径名进行操作。(如果您不传递任何路径名参数,则改为rename标准输入读取路径名,每行一个。)

代码在循环内运行,每个路径名迭代一次。在循环的每次迭代的顶部,在您的代码运行之前,特殊$_变量被分配当前正在处理的路径名。如果您的代码导致 的值$_更改为其他值,则该文件将重命名为具有新名称。

Perl 中的许多表达式$_没有给定任何其他表达式用作操作数时隐式地对变量进行操作。例如,替换表达式将变量保存的字符串中$str =~ s/foo/bar/第一次出现的 更改为,或者如果不包含 则保持不变。如果你只是写没有明确使用运营商,那么它的运作上。这就是说的缩写。foo$str barfoos/foo/bar/=~$_s/foo/bar/$_ =~ s/foo/bar/

s///表达式rename作为代码参数(即第一个命令行参数)传递给是很常见的,但您不必这样做。你可以给它任何你希望它在循环内运行的 Perl 代码,以检查它的每个值$_并(有条件地)修改它。

这有很多很酷和有用的结果,但绝大多数都超出了这个问答的范围。我在这里提出这个问题的主要原因——事实上,我决定发布这个答案的主要原因——是为了说明这一点,因为 的第一个参数rename实际上是 Perl 代码,任何时候你得到一个奇怪的错误消息并且你通过搜索很难找到有关它的信息,您可以将“Perl”添加到搜索字符串中(有时甚至可以将“rename”替换为“Perl”),您通常会找到答案。

2. 用rename *.DAT *.datrename命令没见过*.DAT

rename s/foo/bar/ *.txt这样*.txt的命令通常不会作为命令行参数传递给rename程序,并且您不希望它传递,除非您有一个名称为字面意思的文件*.txt,希望您不会这样做。

rename不解释glob模式一样*.txt*.DAT*.datx**y,或*传递给它的路径参数时。相反,您的 shell会对它们执行路径名扩展(也称为文件名扩展,也称为 globbing)。这发生在rename实用程序运行之前。shell 将 glob 扩展为可能的多个路径名,并将它们作为单独的命令行参数全部传递给rename. 在 Ubuntu 中,您的交互式 shell 是Bash,除非您对其进行了更改,这就是我链接到上面的Bash 参考手册的原因

在一种情况下,glob 模式可以作为单个未扩展的命令行参数传递给rename: 当它与任何文件都不匹配时。在这种情况下,不同的 shell 表现出不同的默认行为,但 Bash 的默认行为是简单地按字面传递 glob。然而,你很少想要这个!如果你确实想要它,那么你应该通过引用它来确保模式没有被扩展。这适用于将参数传递给任何命令,而不仅仅是传递给.rename

引用不仅用于通配符(文件名扩展),因为您的 shell 还对未引用的文本执行其他扩展,并且对于其中一些而不是其他扩展,还对用" "引号括起来的文本执行。一般来说,任何时候你想传递一个参数,其中包含可能被 shell 特殊处理的字符,包括空格,你应该引用它,最好用' '引号

Perl 代码s/foo/bar/不包含任何由 shell 特殊处理的内容,但对我来说也引用它会是一个好主意 - 并编写's/foo/bar/'. (事实上​​,我没有这样做的唯一原因是它会让一些读者感到困惑,因为我还没有谈到引用。)我说这很好的原因是因为 Perl 代码确实包含这样的字符,如果我要更改该代码,我可能不记得检查是否需要引用。相反,如果您希望 shell 扩展一个 glob,则不能引用它。

3. Perl 解释器所说的“不允许裸字”是什么意思

您在问题中显示的错误消息表明,当您运行时rename *.DAT *.dat,您的 shell 扩展*.DAT为一个或多个文件名的列表,并且这些文件名中的第一个b1.DAT. 所有后续参数 - 任何其他扩展自*.DAT和任何扩展自- 都*.dat在该参数之后,因此它们将被解释为路径名。

因为实际运行的内容类似于rename b1.DAT ...,并且因为rename将其第一个非选项参数视为 Perl 代码,所以问题变成了:b1.DAT当您将其作为 Perl 代码运行时,为什么会产生这些“不允许裸字”的错误?

Bareword "b1" not allowed while "strict subs" in use at (user-supplied code).
Bareword "DAT" not allowed while "strict subs" in use at (user-supplied code).
Run Code Online (Sandbox Code Playgroud)

在 shell 中,我们引用我们的字符串以保护它们免受意外的shell 扩展,否则会自动将它们转换为其他字符串(请参阅上面的部分)。Shell 是专用编程语言,其工作方式与通用语言非常不同(它们非常奇怪的语法和语义反映了这一点)。但是 Perl 是一种通用编程语言,并且与大多数通用编程语言一样,在 Perl 中引用的主要目的不是为了保护字符串,而是为了提及它们。这实际上是大多数编程语言类似于自然语言的一种方式. 在英语中,假设你有一只狗,“你的狗”是一个两个词的短语,而你的狗是一只狗。类似地,在 Perl 中,'$foo'是一个字符串,而$foo是名称为 的东西$foo

然而,与几乎所有其他通用编程语言,Perl会有时解释不带引号的文本作为提的字符串-一个字符串,它是“相同的”,因为它在某种意义上说,它是由相同的字符了相同的顺序。它只会尝试以这种方式解释代码,如果它是一个裸字(没有$或其他符号,见下文),并且在它找不到任何其他含义之后。然后它将把它作为一个字符串,除非你通过启用限制告诉它不要

Perl 中的变量通常以称为sigil的标点字符开头,它指定变量的广泛类型。例如,$表示标量@表示数组%表示散列。(还有其他的。)如果您觉得这令人困惑(或无聊),请不要担心,因为我只是想说,当Perl 程序中出现有效名称但前面没有符号时,该名称据说是一个bareword

裸字有多种用途,但它们通常表示已在程序(或程序使用的模块)中定义内置函数用户定义的子程序。Perl 没有调用b1or 的内置函数DAT,因此当 Perl 解释器看到代码时b1.DAT,它会尝试将b1DAT视为子例程的名称。假设没有定义这样的子例程,这将失败。然后,如果没有启用限制,它会将它们视为字符串。这将起作用,尽管您是否真的打算发生这种情况是任何人的猜测。Perl 的.运算符连接字符串,因此b1.DAT计算结果为字符串b1DAT. 也就是说,b1.DAT写类似'b1' . 'DAT'or 的东西是一种糟糕的方式"b1" . "DAT"

您可以通过运行命令来自己测试这一点,该命令perl -E 'say b1.DAT'将简短的 Perl 脚本传递say b1.DAT给运行它的 Perl 解释器,打印b1DAT. (在该命令中,' '引号告诉shell通过say b1.DAT作为单个命令行参数;否则,该空间将导致sayb1.DAT将被解析为单独的单词perl将接收它们作为单独的参数。perl看到引号本身,在shell 删除它们。)

但是现在,尝试use strict;say. 现在它失败了,并出现了与您相同的错误rename

$ perl -E 'use strict; say b1.DAT'
Bareword "b1" not allowed while "strict subs" in use at -e line 1.
Bareword "DAT" not allowed while "strict subs" in use at -e line 1.
Execution of -e aborted due to compilation errors.
Run Code Online (Sandbox Code Playgroud)

发生这种情况是因为use strict;禁止 Perl 解释器将裸词视为字符串。要禁止此特定功能,仅启用subs限制就足够了。此命令产生与上述相同的错误:

perl -E 'use strict "subs"; say b1.DAT'
Run Code Online (Sandbox Code Playgroud)

但通常 Perl 程序员只会编写use strict;,这会启用subs限制和其他两个。use strict;通常是推荐的做法。因此,该rename命令会为您的代码执行此操作。这就是您收到该错误消息的原因。

4. 总而言之,事情是这样的:

  1. 您的 shellb1.DAT作为第一个命令行参数传递,它rename被视为 Perl 代码,为每个路径名参数在循环中运行。
  2. 这意味着b1DAT.运营商有关。
  3. b1并且DAT没有以符号为前缀,因此它们被视为裸词。
  4. 这两个裸词将被视为内置函数或任何用户定义的子例程的名称,但没有这样的东西具有这两个名称中的任何一个。
  5. 如果未启用“strict subs”,则它们将被视为字符串表达式'b1''DAT' 并连接在一起。这与您的意图相去甚远,这说明此功能通常没有帮助。
  6. 但“严格潜艇”已启用,因为rename使所有的限制varsrefs,和subs)。因此,您收到了错误消息。
  7. rename由于此错误而退出。由于此类错误发生较早,因此即使您没有通过-n. 这是一件好事,它通常可以保护用户免受无意的文件名更改,有时甚至可以防止实际数据丢失。

感谢Zanna,她帮助我解决了此答案的早期草稿中的几个重要缺陷。如果没有她,这个答案就会变得不那么有意义,并且可能根本不会发布。