Всегда ли подстановочный знак Bash star * создает отсортированный список (по возрастанию)?

53

У меня есть каталог, заполненный файлами с именами, например, logXXгде XX - это двухсимвольное шестнадцатеричное число, набранное нулями, например:

log00
log01
log02
...
log0A
log0B
log0C
...
log4E
log4F
log50
...

Обычно будет меньше, чем, скажем, 20 или 30 файлов. На дату и время моей конкретной системы нельзя положиться (встроенная система без надежных источников времени NTP или GPS). Однако имена файлов будут надежно увеличиваться, как показано выше.

Я хочу grepпросмотреть все файлы для самой последней записи в журнале определенного типа, я надеялся, catчто файлы вместе, такие как ...

cat /tmp/logs/log* | grep 'WARNING 07 -' | tail -n1

Однако мне пришло в голову, что разные версии bashили shи zshт. Д. Могут иметь разные представления о том, как *расширяется.

На man bashстранице не указано, будет ли расширение *списка определенно восходящим алфавитным списком совпадающих имен файлов. Кажется, он поднимается каждый раз, когда я пробовал его на всех доступных мне системах - но это ОПРЕДЕЛЕННОЕ поведение или просто конкретная реализация?

Другими словами, могу ли я полностью полагаться на cat /tmp/logs/log*объединение всех моих файлов журнала в алфавитном порядке?

Wossname
источник
1
@ADDB Порядок сортировки по умолчанию для sortтакой же, как и для оболочки, когда она расширяет шаблон выделения имени файла.
Кусалананда
9
Это ужасная практика именования файлов. Почему вы запускаете свой запуск с log (0) = - infty?
EP
14
@EP Наша файловая система представляет собой сложный 7-мерный гипертороид с сюрреалистической нумерацией инодов. Это было связано с какой-то непонятной ветвью busybox, и мы застряли с этим сейчас :)
Wossname
1
Вы можете избежать catс , grep -h pattern /tmp/logs/log*чтобы подавить предваряя имена файлов на матчи. (По крайней мере с GNU grep я не проверял POSIX или busybox.)
Питер Кордес
1
@Kusalananda Вы слышали о бесполезном использовании cat, это бесполезное использованиеsort
кот

Ответы:

52

Во всех оболочках глобусы сортируются по умолчанию. Они уже были /etc/globпомощником, вызванным оболочкой Кена Томпсона, чтобы расширить глобусы в первой версии Unix в начале 70-х (и которая дала глобусам их имя).

Поскольку shPOSIX требует, чтобы они сортировались в порядке strcoll(), то есть с использованием порядка сортировки в локали пользователя, например, lsхотя некоторые все еще делают это через strcmp(), что основано только на байтовых значениях.

$ dash -c 'echo *'
Log01B log-0D log00 log01 log02 log0A log0B log0C log4E log4F log50 log log lóg01
$ bash -c 'echo *'
log log log00 log01 lóg01 Log01B log02 log0A log0B log0C log-0D log4E log4F log50
$ zsh -c 'echo *'
log log log00 log01 lóg01 Log01B log02 log0A log0B log0C log-0D log4E log4F log50
$ ls
log  log  log00  log01  lóg01  Log01B  log02  log0A  log0B  log0C  log-0D  log4E  log4F  log50
$ ls | sort
log
log
log00
log01
lóg01
Log01B
log02
log0A
log0B
log0C
log-0D
log4E
log4F
log50

Вы можете заметить выше, что для тех оболочек, которые выполняют сортировку на основе локали, здесь, в системе GNU с en_GB.UTF-8локалью, -в именах файлов игнорируется для сортировки (большинство знаков препинания будет). Они óсортируются более ожидаемым образом (по крайней мере, для британцев), и случай игнорируется (за исключением случаев, когда речь идет о решении связей).

Однако вы заметите некоторые несоответствия для log① log①. Это потому, что порядок сортировки ① и ② не определен в локалях GNU (в настоящее время; надеюсь, он когда-нибудь будет исправлен). Они сортируют одинаково, так что вы получите случайные результаты.

Изменение языкового стандарта повлияет на порядок сортировки. Вы можете установить локаль в C, чтобы получить strcmp()подобную сортировку:

$ bash -c 'echo *'
log log log00 log01 lóg01 Log01B log02 log0.2 log0A log0B log0C log-0D log4E log4F log50
$ bash -c 'LC_ALL=C; echo *'
Log01B log-0D log0.2 log00 log01 log02 log0A log0B log0C log4E log4F log50 log log lóg01

Обратите внимание, что некоторые локали могут вызвать некоторую путаницу даже для строк all-alnum all-ASCII. Как Чешские из них (в системах GNU , по крайней мере) , где chпредставляет собой упорядочивающий элемент , который сортирует после того, как h:

$ LC_ALL=cs_CZ.UTF-8 bash -c 'echo *'
log0Ah log0Bh log0Dh log0Ch

Или, как отметил @ninjalj, даже более странные в венгерских регионах:

$ LC_ALL=hu_HU.UTF-8 bash -c 'echo *'
logX LOGx LOGX logZ LOGz LOGZ logY LOGY LOGy

В zsh, вы можете выбрать сортировку с квалификаторами glob . Например:

echo *(om) # to sort by modification time
echo *(oL) # to sort by size
echo *(On) # for a *reverse* sort by name
echo *(o+myfunction) # sort using a user-defined function
echo *(N)  # to NOT sort
echo *(n)  # sort by name, but numerically, and so on.

Числовой вид echo *(n)также можно включить глобально с помощью numericglobsortопции:

$ zsh -c 'echo *'
log log log00 log01 lóg01 Log01B log02 log0.2 log0A log0B log0C log-0D log4E log4F log50
$ zsh -o numericglobsort -c 'echo *'
log log log00 lóg01 Log01B log0.2 log0A log0B log0C log01 log02 log-0D log4E log4F log50

Если вы (как и я) были озадачены этим порядком в данном конкретном случае (здесь используется моя британская локаль), см. Подробности здесь .

Стефан Шазелас
источник
1
Случай 'ch' может быть еще более странным: некоторые локали могут решить, что 'ch', 'Ch' и 'CH' - это 1 элемент сопоставления каждый, в то время как 'cH' - два элемента сопоставления. См: unicode.org/cldr/trac/ticket/889 Текущий CLDR не кажется, полностью соответствует: ток Hungarian ( unicode.org/cldr/trac/browser/trunk/common/collation/hu.xml ) имеет такие правила &C<cs<<<Cs<<<CS, в то время &C<cs<<<cS<<<Cs<<<CSкак помечен как предлагаемый экспериментальный проект. Судя по некоторым более старым данным, импортированным в CLDR, старые AIX и MS, по-видимому, предпочитали представление «строчные и прописные два разных элемента сопоставления».
ниндзя
И я видел системы, где это все равно не работало. :(
Джошуа
38

Страница man для bash указывает:

Расширение пути

После слов расщепления, если -fпараметр не установлен, Баш сканирует каждое слово для символов *, ?и [. Если появляется один из этих символов, слово считается шаблоном и заменяется отсортированным по алфавиту списком имен файлов, соответствующих шаблону […].

user4556274
источник
1
Только что обнаружил интересную ошибку в замазке или в manтекстовом рендеринге ... если текст, который я ищу, получит "завернутый в слово", то команда / search не найдет его. Просто развернул мой терминал и вот он :)
Wossname
2
Вы покрыты bash. Tho OP также интересовался "zsh и т. Д."
Кусалананда
29

Если вы не активируете некоторые специфические параметры оболочки в некоторых оболочках, выходные данные гарантированно будут одинаковыми.

Порядок указан в стандарте POSIX :

Если шаблон соответствует каким-либо существующим именам файлов или путям, шаблон должен быть заменен этими именами файлов и путями, отсортированными в соответствии с последовательностью сортировки, действующей в текущей локали . Если эта последовательность упорядочения не имеет общего порядка всех символов (см. XBD LC_COLLATE), любые имена файлов или пути, которые сопоставляются одинаково, должны дополнительно побайтно сравниваться с использованием последовательности упорядочения для языкового стандарта POSIX.

См. Также категорию LC_COLLATE в локали POSIX , которая вкратце говорит, что если LC_COLLATE=C, то все упорядочено в порядке ASCII.


bashРуководство упоминает

LC_COLLATE

Эта переменная определяет порядок сопоставления, используемый при сортировке результатов раскрытия имени пути, и определяет поведение выражений диапазона, классов эквивалентности и последовательностей сопоставления в раскрытии имени пути и сопоставлении с образцом.

ksh93и zshимеет аналогичную формулировку, которая заставляет меня полагать, что они следуют стандарту POSIX в этом отношении.

Другие оболочки, такие как pdkshи dashничего не говорят о сортировке имен файлов, возникающих в результате подстановки имен файлов. Я испытываю желание поверить, что это означает, что они по-прежнему придерживаются того же стандарта, по крайней мере, при использовании языкового стандарта POSIX. По моему опыту, я не сталкивался с оболочкой, которая выполняет какую-либо явно «странную» сортировку имен файлов ASCII.

Кусалананда
источник
2
Смотрите numericglobsortопцию zsh, которая повлияет на сортировку. Хотя я бы предпочел включить его отдельно для каждого глобуса, echo *(n)чем включить глобальную опцию.
Стефан
Придира Bash в режиме по умолчанию НЕ является Posix-совместимым.
fpmurphy
@ fpmurphy1 Скажи больше.
Кусалананда
@Kusalananda. Bash никогда не был сертифицирован как жалоба POSIX. Чтобы получить «соответствие POSIX» в Bash, вы должны вызвать Bash с параметром --posixкомандной строки или выполнить set -o posix
команду
@ fpmurphy1 Да, но posixрежим Bash не влияет на сортировку по расширению символов-заглушек . См. Gnu.org/software/bash/manual/html_node/Bash-POSIX-Mode.html. Это наводит меня на мысль (скорее, скорее), что сортировка соответствует POSIX.
Кусалананда
1

Если основной целью является сортировка входных файлов по возрасту, сначала по возрасту, вы можете написать

(cd /tmp/logs; cat `ls -rt log*`) | grep whatever

И если повернутые и сжатые журналы также участвуют:

(cd /tmp/logs; zcat -f `ls -rt log*`) | grep whatever
sultansofswing
источник
4
Было упомянуто, что временные метки на файлах нельзя доверять.
Кусалананда
3
@Kusalananda, это верно, наше системное время обычно считается генератором случайных чисел :)
Wossname