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

Чтобы полностью переключать контекст между пользователями, вы должны предоставить следующую команду

su my_user

После обсуждения в комментариях я решил отредактировать свой ответ.

56
03.06.2017, 23:40
4 ответа

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

Для sh POSIX требует сортировки с помощью 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, состоящих только из 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

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

57
20.08.2021, 11:38

Справочная страница для bash указывает:

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

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

36
20.08.2021, 11:38

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

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

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

(cd /tmp/logs; zcat -f `ls -rt log*`) | grep whatever
1
20.08.2021, 11:38

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

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

If the pattern matches any existing filenames or pathnames, the pattern shall be replaced with those filenames and pathnames, sorted according to the collating sequence in effect in the current locale. If this collating sequence does not have a total ordering of all characters (see XBD LC_COLLATE), any filenames or pathnames that collate equally should be further compared byte-by-byte using the collating sequence for the POSIX locale.

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


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

LC_COLLATE

This variable determines the collation order used when sorting the results of pathname expansion, and determines the behavior of range expressions, equivalence classes, and collating sequences within pathname expansion and pattern matching.

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

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

28
20.08.2021, 11:38

Теги

Похожие вопросы