Há uma pasta com vários arquivos e um script perl que os lista usando ls com um curinga como:
#!/usr/bin/perl
system("ls -U -1 dir/*");
Percebi que se eu executar exatamente o mesmo comando em meu terminal bash, obterei os mesmos resultados, mas eles estarão em uma ordem diferente.
Por que isso pode acontecer? O curinga é tratado de maneira diferente em ambos os contextos?
Exemplo:
mkdir dir
touch dir/a_0 dir/a1_0
saída terminal:
dir/a_0
dir/a1_0
saída perl:
dir/a1_0
dir/a_0
Responder1
A função do Perl system("cmd")
normalmente bifurca um processo e, no processo filho, executa o shell do sistema (normalmente /bin/sh
) com argumentos, ["sh", "-c", "cmd"]
, para sh
analisar e executar essa linha de comando do shell.
Como uma otimização, às vezes pode funcionar sem uma chamada de shell, se cmd
não contiver nenhum metacaractere de shell (como caracteres de citação ou caracteres globulares ou coisas como ;
, &&
...) além de espaço e tabulação, mas aqui temos shell meta -personagens, já que temos um arquivo *
.
Então, isso ls -U -1 dir/*
será interpretado pelo shell do sistema.
É o shell que se expande dir/*
para uma lista de arquivos correspondentes passados para ls
, portanto, a forma como isso é feito depende do shell.
Em um terminal, você normalmente executa seu shell de login, que geralmente não é /bin/sh
. Tambem comoanotado por Pedro), esse shell, uma vez que é executado de forma interativa, normalmente lerá arquivos de configuração como ~/.zshrc
(se o shell for zsh
), onde algumas configurações podem afetar o modo como o globbing é feito.
Por exemplo:
Meu shell é zsh
, e eu tenho um setopt dotglob
no meu ~/.zshrc
, então:
$ echo *
.a d é f
Sem ler o ~/.zshrc
:
$ zsh -c 'echo *'
d é f
$ LC_ALL=C zsh -c 'echo *'
d f é
Você notará que isso zsh
respeita a localidade ao classificar a lista.
$ sh -c 'echo *'
d f é
sh
(que no meu caso é Debian ash
) não respeita a localidade e classifica como se estivesse na localidade C.
Se você quiser perl
interpretar system()
a linha de comando com um shell específico, você pode escrevê-lo:
system("zsh", "-c", "cmd");
Quando passado mais de um argumento, perl
's system()
nunca chama implicitamente um shell; portanto, acima, ele bifurca um processo no qual é executado /bin/zsh
com ["zsh", "-c", "cmd"]
argumentos.