尝试重命名多个文件名的小写部分时,重命名返回“不允许使用密码”


12

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

a1.dat
b1.DAT

我想重命名b1.DAT为,b1.dat因此该文件夹中将包含以下文件:

a1.dat
b1.dat

我尝试了(失败):

$ 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).

$ 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).

搜索此结果没有有意义的解决方案...


您可能还想了解mmv
PlasmaHH

Answers:


14

该错误似乎来自Perl rename。您需要使用引号,但只需要指定要更改的部分,即搜索和替换样式。语法如下:

rename -n 's/\.DAT/\.dat/' *

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

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

shopt -s dotglob

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

shopt -s globstar
rename -n 's/\.DAT/\.dat/' **

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

rename -n 's/\.DAT/\.dat/' **/*.DAT

如果您的文件具有不以结尾的各种不同名称,则速度会更快.DAT[1]

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

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

find -type f -name "*.DAT" -exec rename -n -- 's/\.DAT/\.dat/' {} \;

或更好

find -type f -name "*.DAT" -exec rename -n -- 's/\.DAT/\.dat/' {} +

使用find ... -execwith +的速度比using更快,\;因为with 会从找到的文件中构造一个参数列表。最初,我认为您将无法使用它,因为您提到自己遇到了argument list too long问题,但是现在我知道,该列表还将根据需要巧妙地分解为多个命令调用,以避免出现此问题[2]

由于rename将以相同的方式处理每个文件名,因此参数列表有多长都无所谓,因为它可以安全地分配给多个调用。如果您正在使用的命令-exec不接受多个参数或要求其参数采用特定顺序,或者由于任何其他原因而拆分参数列表将导致某些不必要的事情发生,则可以使用\;,这会使命令被调用一次对于找到的每个文件(如果参数列表对于其他方法而言太长,则将花费很长时间!)。


非常感谢Eliah Kagan提供了非常有用的建议来改善此答案:

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


谢谢。如何递归执行(对于所有子文件夹中的所有文件)?
萨摩(Samo)

@Samo,请参阅我的编辑内容:)
Zanna

不起作用:$重命名's / \。DAT / \。dat /'** bash:/ usr / bin / rename:参数列表过长
Samo

1
@Samo,您需要-n在测试后从重命名中删除-只是为了显示要更改的内容
Zanna

1
在Centos和Arch上,重命名不会表现出这种现象。我是从WSL上使用Debian来的。此语法有效。
xtian

6

你可以做:

rename -n 's/DAT$/\L$&/' *.DAT

放置-n以进行实际的重命名。

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

  • rename的替换中,末尾DAT$匹配DAT

  • \L$&使整个比赛变小写;$&指整个比赛

如果您只想为b1.DAT

rename -n 's/DAT$/\L$&/' b1.DAT

例:

% rename -n 's/DAT$/\L$&/' *.DAT
rename(b1.DAT, b1.dat)

4

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

答案的第一部分是关于renamePerl与Perl 之间的关系,以及如何rename使用传递的第一个命令行参数(即它的代码参数)。第二部分是关于Shell如何执行扩展(尤其是遍历)以构造参数列表的。第三部分介绍Perl代码中发生的错误,该错误给出了“不允许使用裸字”错误。最后,第四部分总结了输入命令和获取错误之间发生的所有步骤。

1.当出现rename奇怪的错误消息时,在搜索中添加“ Perl”。

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

使用该rename命令时,您不仅在运行别人编写的Perl代码。您还正在编写自己的Perl代码并告诉rename其运行。这是因为传递给命令的第一个命令行参数(rename除了诸如option之类的参数之外-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代码确实包含非常普遍这样的字符,如果我要更改该代码,我可能不记得检查是否需要引用。相反,如果您希望外壳扩展一个glob,则不能将其引号引起来。

3. Perl解释器的“不允许使用裸词”的含义

您在问题中显示的错误消息表明,运行时rename *.DAT *.dat,shell扩展*.DAT为一个或多个文件名的列表,而这些文件名中的第一个b1.DAT。所有后续参数-无论是任何其他人扩展*.DAT,并从任何扩大*.dat--came这样的说法后,所以他们将被解释为路径名。

因为实际运行的是类似的东西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).

在外壳程序中,我们引用字符串以防止它们受到意外的外壳扩展的影响,否则这些扩展会自动将其转换为其他字符串(请参见上文)。外壳程序是专用程序设计语言,其工作原理与通用语言完全不同(它们的语法和语义很奇怪,反映了这一点)。但是Perl是一种通用编程语言,并且像大多数通用编程语言一样,在Perl中引用的主要目的不是保护字符串,而是根本不提及字符串。这实际上是大多数编程语言自然语言相似的方式。用英语(假设您有一只狗),“您的狗”是两个单词的短语,而您的狗是狗。同样,在Perl中,'$foo'是一个字符串,而$foo名称是$foo

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

Perl中的变量通常以称为sigil的标点符号开头,该符号指定变量的广义类型。例如,$意味着scalar@意味着array,并且%意味着hash。(还有其他的。)不要,如果你觉得还不清楚(或无聊的)担心,因为我只是把它说,当一个有效的名字出现在一个Perl程序,但没有一个印记之前,这个名字据说是一个裸词

关键字有多种用途,但是它们通常表示内置功能或已在程序(或程序使用的模块)中定义用户定义的子例程。Perl没有称为b1或的内置函数DAT,因此当Perl解释器看到代码时b1.DAT,它将尝试将b1DAT视为子例程的名称。假设尚未定义此类子例程,则此操作将失败。然后,如果未启用限制,它将限制视为字符串。这将起作用,尽管您是否真的打算发生这种情况是所有人的猜测。Perl的.运算符连接字符串,因此b1.DAT求值字符串b1DAT。也就是说,这是b1.DAT编写类似'b1' . 'DAT'或的不好的方法"b1" . "DAT"

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

但是现在,尝试use strict;在之前使用Perl脚本编写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.

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

perl -E 'use strict "subs"; say b1.DAT'

但是通常Perl程序员只会编写use strict;,从而启用了subs限制和另外两个。use strict;通常建议您这样做。因此,该rename命令会对您的代码执行此操作。这就是为什么您收到该错误消息的原因。

4.总之,这是发生了什么:

  1. 您的外壳程序b1.DAT作为第一个命令行参数传递,该命令行参数rename被视为Perl代码以针对每个路径名参数循环运行。
  2. 这被认为是故意的,b1DAT.操作员联系在一起。
  3. b1并且DAT没有以sigils作为前缀,因此将它们视为裸词。
  4. 那两个裸词将被当作内置函数的名称或任何用户定义的子例程的名称,但是这些名称都没有。
  5. 如果未启用“ strict subs”,则它们将被视为字符串表达式'b1''DAT'并串联在一起。这与您的预期相去甚远,这说明了此功能通常无济于事。
  6. 但“严格潜艇”已启用,因为rename使所有的限制varsrefs,和subs)。因此,您得到一个错误。
  7. rename由于此错误而退出。由于此类错误是较早发生的,因此即使您没有通过,也不会尝试重命名文件-n。这是一件好事,它通常可以保护用户避免意外的文件名更改,甚至有时甚至防止实际的数据丢失。

感谢Zanna,他帮助我在更简单的答案中解决了几个重要的缺点。没有她,这个答案将变得毫无意义,并且可能根本不会发布。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.