其他 答案已经解决了该问题的两个主要方面之一:如何成功执行所需的重命名操作。该答案的目的是解释您的命令为何不起作用,包括该rename
命令上下文中奇怪的“不允许禁止使用字样”错误消息的含义。
答案的第一部分是关于rename
Perl与Perl 之间的关系,以及如何rename
使用传递的第一个命令行参数(即它的代码参数)。第二部分是关于Shell如何执行扩展(尤其是遍历)以构造参数列表的。第三部分介绍Perl代码中发生的错误,该错误给出了“不允许使用裸字”错误。最后,第四部分总结了输入命令和获取错误之间发生的所有步骤。
1.当出现rename
奇怪的错误消息时,在搜索中添加“ Perl”。
在Debian和Ubuntu中,该rename
命令是执行文件重命名的Perl脚本。在较早的发行版(包括在撰写本文时仍受支持的 14.04 LTS)上,它是(间接)指向命令的符号链接。在较新的发行版中,它指向更新的命令。这两个Perl重命名命令的工作方式大致相同,在本答案的其余部分中,我将只引用这两个命令。prename
file-rename
rename
使用该rename
命令时,您不仅在运行别人编写的Perl代码。您还正在编写自己的Perl代码并告诉rename
其运行。这是因为传递给命令的第一个命令行参数(rename
除了诸如option之类的参数之外-n
)均由实际的Perl代码组成。该rename
命令使用此代码对作为后续命令行参数传递的每个路径名进行操作。(如果不传递任何路径名参数,则改为rename
从标准输入中读取路径名,每行一个。)
该代码在一个循环中运行,该循环每个路径名重复一次。在循环的每次迭代的顶部,在代码运行之前,都会为特殊$_
变量分配当前正在处理的路径名。如果您的代码使的值$_
更改为其他值,则该文件将重命名为具有新名称。
当没有给其他表达式用作操作数时,Perl中的许多表达式对$_
变量进行隐式操作。例如,替换表达式将变量所保存的字符串中$str =~ s/foo/bar/
第一次出现的更改为,如果不包含,则将其foo
保留不变。如果你只是写没有明确使用的运营商,那么它的运作上。这就是说的缩写。$str
bar
foo
s/foo/bar/
=~
$_
s/foo/bar/
$_ =~ s/foo/bar/
这是常见的通过一个s///
表达式来rename
作为代码的参数(即,第一个命令行参数),但你不必。您可以给它提供任何希望它在循环内运行的Perl代码,以检查$_
和(有条件地)修改它的每个值。
这会带来很多很酷和有用的后果,但是绝大多数后果都超出了本问答的范围。我在这里提出这个问题的主要原因(实际上,我决定发布此答案的主要原因)是为了指出这一点,因为第一个参数rename
实际上是Perl代码,所以只要您收到奇怪的错误消息并且您通过搜索查找有关此信息的麻烦,您可以在搜索字符串中添加“ Perl”(有时甚至将“ rename”替换为“ Perl”),并且通常会找到答案。
2.有了rename *.DAT *.dat
,rename
命令就没看过*.DAT
!
像rename s/foo/bar/ *.txt
这样*.txt
的命令通常不会作为命令行参数传递给rename
程序,并且您不希望它传递给,除非您有一个文件名叫字面量的文件*.txt
,希望您不要。
rename
不解释glob模式一样*.txt
,*.DAT
,*.dat
,x*
,*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
,它将尝试将b1
和DAT
视为子例程的名称。假设尚未定义此类子例程,则此操作将失败。然后,如果未启用限制,它将限制视为字符串。这将起作用,尽管您是否真的打算发生这种情况是所有人的猜测。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
作为单个命令行参数;否则,该空间将导致say
和b1.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.总之,这是发生了什么:
- 您的外壳程序
b1.DAT
作为第一个命令行参数传递,该命令行参数rename
被视为Perl代码以针对每个路径名参数循环运行。
- 这被认为是故意的,
b1
并DAT
与.
操作员联系在一起。
b1
并且DAT
没有以sigils作为前缀,因此将它们视为裸词。
- 那两个裸词将被当作内置函数的名称或任何用户定义的子例程的名称,但是这些名称都没有。
- 如果未启用“ strict subs”,则它们将被视为字符串表达式
'b1'
和'DAT
'并串联在一起。这与您的预期相去甚远,这说明了此功能通常无济于事。
- 但“严格潜艇”已启用,因为
rename
使所有的限制(vars
,refs
,和subs
)。因此,您得到一个错误。
rename
由于此错误而退出。由于此类错误是较早发生的,因此即使您没有通过,也不会尝试重命名文件-n
。这是一件好事,它通常可以保护用户避免意外的文件名更改,甚至有时甚至防止实际的数据丢失。
感谢Zanna,他帮助我在更简单的答案中解决了几个重要的缺点。没有她,这个答案将变得毫无意义,并且可能根本不会发布。