Есть папка с кучей файлов и скрипт 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
Вывод perl:
dir/a1_0
dir/a_0
решение1
Функция Perl system("cmd")
обычно разветвляет процесс и в дочернем процессе запускает оболочку системы (обычно /bin/sh
) с аргументами , ["sh", "-c", "cmd"]
чтобы sh
проанализировать и выполнить эту командную строку оболочки.
В качестве оптимизации иногда можно обойтись без вызова оболочки, если cmd
не содержит никаких метасимволов оболочки (например, символов кавычек или символов подстановки или чего-то вроде ;
, &&
...), кроме пробела и табуляции, но здесь у нас есть метасимволы оболочки, поскольку у нас есть *
.
Итак, это ls -U -1 dir/*
будет интерпретировано оболочкой системы.
Это оболочка, которая расширяется dir/*
до списка соответствующих файлов, переданных в ls
, поэтому способ, которым это делается, зависит от оболочки.
В терминале вы обычно запускаете свою оболочку входа, которая, как правило, не /bin/sh
. Также (какотметил(а) peterph), эта оболочка, поскольку она работает в интерактивном режиме, обычно считывает файлы конфигурации, например ~/.zshrc
(если оболочка 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.
Если вы хотите, чтобы perl
's system()
интерпретировал командную строку с помощью определенной оболочки, вы можете написать:
system("zsh", "-c", "cmd");
При передаче более одного аргумента perl
's system()
никогда не вызывает неявно оболочку, поэтому выше он разветвляет процесс, в котором он запускается /bin/zsh
с ["zsh", "-c", "cmd"]
аргументами.