Il carattere jolly Bash star * produce sempre un elenco (crescente) ordinato?


53

Ho una directory piena di file con nomi come logXXdove XX è un numero esadecimale maiuscolo a due caratteri, con riempimento zero, come:

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

Generalmente ci saranno meno di 20 o 30 file in totale. La data e l'ora sul mio sistema particolare non sono qualcosa su cui si può fare affidamento (un sistema incorporato senza fonti di tempo GPS o NTP affidabili). Tuttavia, i nomi dei file aumenteranno in modo affidabile come mostrato sopra.

Desidero esaminare greptutti i file per la singola voce di registro più recente di un certo tipo, sperando che cati file insieme come ...

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

Tuttavia mi è venuto in mente che versioni diverse di basho sho zshecc. Potrebbero avere idee diverse su come *viene espanso.

La man bashpagina non dice se l'espansione di *sarebbe un elenco alfabetico decisamente crescente di nomi di file corrispondenti. Sembra che stia ascendendo ogni volta che l'ho provato su tutti i sistemi che ho a disposizione - ma è un comportamento DEFINITO o solo una specifica implementazione?

In altre parole, posso fare assolutamente affidamento cat /tmp/logs/log*per concatenare tutti i miei file di registro in ordine alfabetico?


1
@ADDB L'ordinamento predefinito per sortè lo stesso di quello della shell quando si sta espandendo un modello globbing di nome file.
Kusalananda

9
È una pratica terribile di denominazione dei file. Perché inizi la corsa con log (0) = - infty?
EP

14
@EP Il nostro filesystem è un iper-toroide a 7 dimensioni complesso con numerazione surreale degli inode. Era nonno con qualche ramo oscuro di busybox e ora siamo bloccati con lo spirito :)
Wossname

1
È possibile evitare catcon grep -h pattern /tmp/logs/log*per sopprimere anteporre nomi di file alle partite. (Almeno con GNU grep, non ho controllato POSIX o busybox.)
Peter Cordes,

1
@Kusalananda Hai sentito parlare di un uso inutile cat, questo è un uso inutilesort
cat

Risposte:


52

In tutte le shell, i globs sono ordinati per impostazione predefinita. Erano già /etc/globdall'aiutante chiamato dalla shell di Ken Thompson per espandere i globs nella prima versione di Unix nei primi anni '70 (e che ha dato loro il nome).

Perché shPOSIX richiede che vengano ordinati per mezzo di questo strcoll(), cioè utilizzando l'ordinamento nella locale dell'utente, come lsse alcuni lo fanno ancora tramite strcmp(), basato solo su valori byte.

$ 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

Si può notare in precedenza che per quelle shell che eseguono l'ordinamento in base alla locale, qui su un sistema GNU con una en_GB.UTF-8locale, i -nomi dei file nei file vengono ignorati per l'ordinamento (la maggior parte dei caratteri di punteggiatura). Il óè ordinato in modo più atteso (almeno per gli inglesi), ed il caso viene ignorato (tranne quando si tratta di decidere legami).

Tuttavia, noterai alcune incongruenze per log① log②. Questo perché l'ordinamento di ① e ② non è definito nelle localizzazioni GNU (attualmente; si spera che verrà risolto un giorno). Ordinano lo stesso, in modo da ottenere risultati casuali.

La modifica delle impostazioni internazionali influirà sull'ordinamento. È possibile impostare le impostazioni internazionali su C per ottenere un strcmp()ordinamento simile:

$ 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

Si noti che alcune impostazioni locali possono causare confusioni anche per le stringhe all-alnum all-ASCII. Come quelli cechi (almeno sui sistemi GNU) in cui chè presente un elemento di confronto che ordina h:

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

Oppure, come sottolineato da @ninjalj, anche quelli più strani nelle località ungheresi:

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

In zsh, puoi scegliere l'ordinamento con i qualificatori glob . Per esempio:

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.

L'ordinamento numerico di echo *(n)può anche essere abilitato a livello globale con l' numericglobsortopzione:

$ 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

Se tu (come ero) sei confuso da quell'ordine in quel particolare caso (qui usando il mio locale britannico), vedi qui per i dettagli.


1
Il caso 'ch' può essere ancora più strano: alcuni locali possono decidere che 'ch', 'Ch' e 'CH' sono 1 elemento di confronto ciascuno, mentre 'cH' sono due elementi di confronto. Vedi: unicode.org/cldr/trac/ticket/889 L'attuale CLDR non sembra essere del tutto coerente: l'attuale ungherese ( unicode.org/cldr/trac/browser/trunk/common/collation/hu.xml ) ha regole come &C<cs<<<Cs<<<CS, mentre &C<cs<<<cS<<<Cs<<<CSè contrassegnato come bozza sperimentale proposta. A giudicare da alcuni dati più vecchi importati in CLDR, AIX e MS precedenti sembravano preferire la vista "lettere minuscole e maiuscole sono 2 elementi di confronto diversi".
ninjalj,

E ho visto sistemi in cui non funzionava comunque. :(
Joshua,

38

La pagina man di bash specifica:

Espansione percorso

Dopo la suddivisione in parole, a meno che l' -fopzione è stata impostata, bash scandisce ogni parola per i personaggi *, ?e [. Se appare uno di questi caratteri, la parola viene considerata come un motivo e sostituita con un elenco in ordine alfabetico di nomi di file che corrispondono al motivo […].


1
Ho appena trovato un bug interessante nel manrendering del testo di putty o ... se il testo che sto cercando viene "racchiuso tra parole", un comando / search non lo troverà. Ho appena ingrandito il mio terminale ed
eccolo

2
Hai coperto bash. Anche l'OP era interessato a "zsh ecc."
Kusalananda

29

A meno che non si attivi alcune opzioni di shell molto specifiche in alcune shell, l'output è garantito lo stesso.

L'ordine è specificato nello standard POSIX :

Se il modello corrisponde a nomi di file o percorsi esistenti, il modello deve essere sostituito con tali nomi di file e percorsi, ordinati in base alla sequenza di confronto in vigore nella locale corrente . Se questa sequenza di fascicolazione non ha un ordinamento totale di tutti i caratteri (vedere XBD LC_COLLATE), tutti i nomi di file o percorsi che si fascicano in modo uguale dovrebbero essere ulteriormente confrontati byte per byte utilizzando la sequenza di fascicolazione per la locale POSIX.

Vedi anche la categoria LC_COLLATE nella locale POSIX , che in breve dice che se LC_COLLATE=C, allora le cose sono ordinate in ordine ASCII.


Il bashmanuale menziona

LC_COLLATE

Questa variabile determina l'ordine di confronto utilizzato durante l'ordinamento dei risultati dell'espansione del percorso e determina il comportamento delle espressioni di intervallo, delle classi di equivalenza e delle sequenze di confronto all'interno dell'espansione del percorso e della corrispondenza del modello.

ksh93e zshha una formulazione simile, che mi porta a credere che seguano lo standard POSIX a questo proposito.

Altre shell, come pdkshe dashnon dicono nulla sull'ordinamento dei nomi dei file risultanti dal globbing dei nomi dei file. Sono tentato di credere che ciò significhi che aderiscono ancora allo stesso standard, almeno quando si utilizzano le impostazioni internazionali POSIX. Nella mia esperienza, non mi sono imbattuto in una shell che esegue ordinamenti apertamente "strani" di nomi di file ASCII.


2
Vedere l' numericglobsortopzione in zshche influenzerebbe l'ordinamento. Anche se preferirei abilitarlo su una base per-glob come echo *(n)attivare l'opzione a livello globale.
Stéphane Chazelas,

Un pazzo. Bash, in modalità predefinita, NON è conforme a Posix.
fpmurphy

@ fpmurphy1 Altre informazioni.
Kusalananda

@Kusalananda. Bash non è mai stato certificato come reclamo POSIX. Per ottenere la "conformità POSIX" in Bash, è necessario invocare Bash con l' --posixopzione della riga di comando o eseguireset -o posix
fpmurphy

@ fpmurphy1 Sì, ma l'ordinamento dell'espansione dei caratteri globbing del nome file non è influenzato dalla posixmodalità Bash . Vedi gnu.org/software/bash/manual/html_node/Bash-POSIX-Mode.html Questo mi porta a credere (spero, piuttosto) che l'ordinamento sia conforme a POSIX.
Kusalananda

1

Se l'obiettivo principale è ordinare i file di input in base alla loro età, prima i più vecchi, potresti scrivere

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

E se sono coinvolti anche registri ruotati e compressi:

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

4
È stato menzionato che i timestamp sui file non erano affidabili.
Kusalananda

3
@Kusalananda, esatto, il nostro tempo di sistema è generalmente considerato un generatore di numeri casuali :)
Wossname
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.