为什么从终端或 Perl 使用通配符结果的顺序可能不同?

为什么从终端或 Perl 使用通配符结果的顺序可能不同?

有一个文件夹,里面有一堆文件和一个 perl 脚本,使用 ls 和通配符列出它们,例如:

#!/usr/bin/perl
system("ls -U -1 dir/*");

我注意到,如果我从 bash 终端运行完全相同的命令,我会得到相同的结果,但它们的顺序不同。

为什么会这样?通配符在两种情况下的处理方式是否不同?

例子:

mkdir dir
touch dir/a_0 dir/a1_0

终端输出:

dir/a_0
dir/a1_0

珀尔输出:

dir/a1_0
dir/a_0

答案1

Perl 的system("cmd")函数通常会分叉一个进程,并在子进程中运行系统的 shell(通常为/bin/sh),并以 为参数,["sh", "-c", "cmd"]sh解析并执行该 shell 命令行。

作为一种优化,如果除了空格和制表符之外cmd不包含任何 shell 元字符(例如引用字符或通配字符或诸如;, ... 之类的东西),有时可能不需要 shell 调用,但这里我们有 shell 元字符&&- 字符,因为我们有一个*.

因此,这ls -U -1 dir/*将由系统的 shell 解释。

shell 会扩展dir/*为传递给 的匹配文件列表ls,因此其完成方式取决于 shell。

在终端中,您通常会运行登录 shell,这通常不是/bin/sh.另外(如彼得夫指出),该 shell 因为以交互方式运行,通常会读取配置文件,例如~/.zshrc(如果 shell 是zsh),其中某些设置可能会影响通配符的完成方式。

例如:

我的外壳是,并且我的 中zsh有一个,所以:setopt dotglob~/.zshrc

$ echo *
.a d é f

无需阅读~/.zshrc

$ zsh -c 'echo *'
d é f
$ LC_ALL=C zsh -c 'echo *'
d f é

您会注意到zsh在对列表进行排序时会遵循区域设置。

$ sh -c 'echo *'
d f é

sh(在我的例子中是 Debian ash)不尊重语言环境,并且像在 C 语言环境中一样排序。

如果你想perlsystem()特定的 shell 解释命令行,你可以这样写:

system("zsh", "-c", "cmd");

当传递多个参数时,perlssystem()永远不会隐式调用 shell,因此上面它会分叉一个进程,并在该进程中将其/bin/zsh作为["zsh", "-c", "cmd"]参数运行。

相关内容