Чи завжди зірка Баша * wildcard створює (зростаючий) відсортований список?


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*об'єднання всіх моїх файлів журналу в алфавітному порядку?


1
@ADDB Порядок сортування за замовчуванням такий sortсамий, як і для оболонки, коли вона розширює шаблони назви файлів.
Кусалаланда

9
Це жахлива практика іменування файлів. Чому ви починаєте свій запуск з log (0) = - infty?
EP

14
@EP Наша файлова система - це складний 7-мірний гіпертороїд із сюрреалістичною нумерацією індектів. Це було на
урочистому заході

1
Ви можете уникнути catз grep -h pattern /tmp/logs/log*придушити випереджаючи імена файлів на матчі. (Принаймні, з GNU grep, я не перевірив POSIX або busbox.)
Пітер Кордес

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' - це один елемент, що збігається, а '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параметр не встановлено, Баш сканує кожне слово для символів *, ?і [. Якщо з’являється один із цих символів, то це слово вважається візерунком і замінюється алфавітно відсортованим списком імен файлів, що відповідають шаблону […].


1
Щойно знайшов цікаву помилку або в шпаклівці, або manв текстовому візуалізації ... якщо текст, який я шукаю, отримує "обгорнуте слово", тоді команда / search не знайде його. Просто максимізував мій термінал і ось він :)
Wossname

2
Ви накрили bash. Tho OP також цікавився "zsh etc."
Kusalananda

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
fpmurphy

@ fpmurphy1 Так, але на сортування розширення символів глобулювання файлів не впливає posixрежим Баша . Див. Gnu.org/software/bash/manual/html_node/Bash-POSIX-Mode.html Це змушує мене повірити (сподіваюсь, швидше), що сортування сумісне з POSIX.
Kusalananda

1

Якщо основна мета - сортування вхідних файлів за їх віком, найперше, ви можете написати

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

Якщо також залучаються обертові та стислі журнали:

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

4
Зазначалося, що часовим позначкам файлів не слід довіряти.
Kusalananda

3
@Kusalananda, саме так, наш системний час, як правило, вважається генератором випадкових чисел :)
Wossname
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.