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

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

У меня есть каталог, заполненный файлами с именами типа logXXXX, где 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и т. д. могут иметь разные идеи о том, как *расширяется is.

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

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

решение1

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

Для 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-ASCII all-alnum. Например, чешские (по крайней мере, в системах 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

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

решение2

На странице руководства по bash указано:

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

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

решение3

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

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

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

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


В bashруководстве упоминается

LC_COLLATE

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

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

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

решение4

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

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

А если задействованы также ротированные и сжатые журналы:

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

Связанный контент