Come trovare i file nei sottodir e ordinarli per nome file in un singolo comando?


9

Risultato di una normale ricerca utilizzando find . ! -path "./build*" -name "*.txt":

./tool/001-sub.txt
./tool/000-main.txt
./zo/001-int.txt
./zo/id/002-and.txt
./as/002-mod.txt

e se ordinati con sort -n:

./as/002-mod.txt
./tool/000-main.txt
./tool/001-sub.txt
./zo/001-int.txt
./zo/id/002-and.txt

tuttavia l'output desiderato è:

./tool/000-main.txt
./zo/001-int.txt
./tool/001-sub.txt
./zo/id/002-and.txt
./as/002-mod.txt

ciò significa che l'output è ordinato solo in base al nome file , ma le informazioni sulla cartella devono essere mantenute come parte dell'output.

Modifica : rende l'esempio più complicato in quanto la struttura della sottodirectory può includere più di un livello.


2
Vedere questa domanda ho chiesto su SO: stackoverflow.com/questions/3222810/...
CAMH

@camh - se possibile, vorrei usare solo i comandi unix. In ogni caso la mia domanda è praticamente una tua copia. Riesci a trasferire la soluzione migliore a questo thread (mantieni comunque un link all'originale) in modo che io possa contrassegnare come soluzione?
unode,

Se @Shawn apporta le modifiche che ho suggerito nel mio commento (usa -printfinvece di awk), penso che sia la soluzione migliore. Ho rielaborato la mia implementazione originale per utilizzare questo metodo.
Camh,

Risposte:


9

È necessario ordinare in base all'ultimo campo (considerando /come un separatore di campo). Sfortunatamente, non riesco a pensare a uno strumento in grado di farlo quando il numero di campi varia (se solo sort -kpotesse assumere valori negativi).

Per ovviare a questo, dovrai fare un decoro-decoro-decorato. Cioè, prendi il nome del file e mettilo all'inizio seguito da un separatore di campo, quindi fai un ordinamento, quindi rimuovi la prima colonna e il separatore di campo.

find . ! -path "./build*" -name "*.txt" |\
    awk -vFS=/ -vOFS=/ '{ print $NF,$0 }' |\
    sort -n -t / |\
    cut -f2- -d/

Questo awkcomando dice che il separatore di campo FS è impostato su /; questo influenza il modo in cui legge i campi. Anche il separatore del campo di output OFS è impostato su /; questo influisce sul modo in cui stampa i record. L'istruzione successiva dice stampare l'ultima colonna ( NFè il numero di campi nel record, quindi capita anche che sia l'indice dell'ultimo campo) così come l'intero record ( $0è l'intero record); li stamperà con l'OFS tra loro. Quindi l'elenco viene modificato sort, trattandosi /come il separatore di campo: poiché abbiamo prima il nome file nel record, ordinerà per quello. Quindi cutstampa solo i campi 2 fino alla fine, trattando nuovamente /come separatore di campo.


3
Dal momento che questo è con find (1), puoi saltare la parte awk e usare-printf '%f/%p\n'
camh

infatti la nostra configurazione è leggermente più complicata. Include profondità variabili sottodirectory. Modificato la domanda per riflettere questo fatto. Mi scuso per non averlo incluso all'inizio.
unode,

1
@Unode: la soluzione di Shawn gestisce bene la profondità variabile, è la soluzione canonica a questo problema (fino a variazioni minori).
Gilles 'SO- smetti di essere cattivo'

4

Vorrei usare i file '-printf' per generare nome e percorso, ordinare per nome e tagliare il nome in un ultimo passaggio. '###' è solo un marcatore, per aiutare a tagliare.

find -name "*.txt" -printf "%f###%p\n" | sort -n | sed 's/.*###//'

% f stampa il nome file,% p l'intero percorso.

Ho semplificato il comando find per inserirlo in una riga, ovviamente lasceresti la ! -path "./build*"parte.


3

In zsh ≥4.10.10:

print -l -- **/*.txt~build*(oe\''REPLY=${REPLY:t}'\')
  • **/*.txtcorrisponde *.txtin modo ricorsivo nella directory corrente e nelle sue sottodirectory .
  • ~build* esclude le corrispondenze il cui testo inizia con build*(mi piace ! -path './build*'). (È necessario setopt extended_globprima.)
  • (oe\''…'\')è un qualificatore glob di ordinamento . REPLY=…costruisce la stringa da ordinare dalla stringa da restituire.
  • ${REPLY:t}è il nome di base ("coda") del percorso.

Un sacco di magia concatenata. Interessante ma siamo limitati alla sintassi sh. +1
unode,
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.