为什么在终端或 perl 中使用时通配符结果的顺序可能不同?

Lui*_*uis 2 shell perl wildcards

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

#!/usr/bin/perl
system("ls -U -1 dir/*");
Run Code Online (Sandbox Code Playgroud)

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

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

例子:

mkdir dir
touch dir/a_0 dir/a1_0
Run Code Online (Sandbox Code Playgroud)

终端输出:

dir/a_0
dir/a1_0
Run Code Online (Sandbox Code Playgroud)

perl 输出:

dir/a1_0
dir/a_0
Run Code Online (Sandbox Code Playgroud)

Sté*_*las 6

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

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

因此,这ls -U -1 dir/*将由系统的外壳程序解释。

它是扩展dir/*到传递给 的匹配文件列表的外壳ls,因此它的完成方式取决于外壳。

在终端中,您通常运行登录 shell,通常不是/bin/sh. 此外(如peterph指出的),由于它以交互方式运行,该 shell 通常会读取配置文件,例如~/.zshrc(如果 shell 是zsh),其中某些设置可能会影响 globbing 的完成方式。

例如:

我的壳是zsh的,我已经得到了setopt dotglob我的~/.zshrc,所以:

$ echo *
.a d é f
Run Code Online (Sandbox Code Playgroud)

无需阅读~/.zshrc

$ zsh -c 'echo *'
d é f
$ LC_ALL=C zsh -c 'echo *'
d f é
Run Code Online (Sandbox Code Playgroud)

您会注意到zsh在对列表进行排序时尊重语言环境。

$ sh -c 'echo *'
d f é
Run Code Online (Sandbox Code Playgroud)

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

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

system("zsh", "-c", "cmd");
Run Code Online (Sandbox Code Playgroud)

当传递更多的参数时,perl'ssystem()永远不会隐式调用 shell,所以在上面,它会派生一个进程,在其中运行/bin/zsh["zsh", "-c", "cmd"]作为参数。