2010-08-16 19 views
6

Sono nuovo nello script di shell, quindi ho bisogno di aiuto per capire come risolvere questo problema.Ottieni il file più recente basato sul timestamp

Ho una directory che contiene i file nel seguente formato. I file sono in un diretory chiamato/incoming/esterno/dati

AA_20100806.dat 
AA_20100807.dat 
AA_20100808.dat 
AA_20100809.dat 
AA_20100810.dat 
AA_20100811.dat 
AA_20100812.dat 

Come si può vedere il nome del file include un timestamp. cioè [RANGE] _ [YYYYMMDD] .dat

Quello che devo fare è scoprire quale di questi file ha la data più recente utilizzando il timestamp sul nome del file non sul timestamp del sistema e memorizzare il nome del file in una variabile e spostarlo in un'altra directory e spostare il resto in un'altra directory.

+0

È possibile che [RANGE] sia una combinazione di due caratteri? Questo fa una grande differenza, come probabilmente noterai dalle risposte già date. –

+0

Sì, possono essere diversi. E anche la stessa cartella conterrà altri tipi di file con nomi diversi da quelli mostrati sopra. – ziggy

risposta

17

Per coloro che vogliono solo una risposta, qui è:

ls | sort -n -t _ -k 2 | tail -1 

Ecco il processo di pensiero che mi ha portato qui.

Ho intenzione di assumere che la parte [RANGE] potrebbe essere qualsiasi cosa.

Inizia con quello che sappiamo. Directory

  • di lavoro:// esterni/dati in arrivo
  • formato dei file: [RANGE] _ [AAAAMMGG] .dat

Abbiamo bisogno di trovare il file più recente [AAAAMMGG] in la directory e abbiamo bisogno di memorizzare quel nome di file.

strumenti disponibili (Sono solo che elenca gli strumenti rilevanti per questo problema ... identificandoli diventa più facile con la pratica):

Suppongo che non abbiamo bisogno di sed, dal momento che possiamo lavorare con l'intero output del comando ls.Usando ls, awk, ordinare, e la coda possiamo ottenere il file corretto in questo modo (tenere a mente che dovrete controllare la sintassi contro ciò che accetterà il vostro sistema operativo):

NEWESTFILE=`ls | awk -F_ '{print $1 $2}' | sort -n -k 2,2 | tail -1` 

Poi è solo una questione di inserire il carattere di sottolineatura, che non dovrebbe essere troppo difficile.

EDIT: Ho avuto un po 'di tempo, quindi sono riuscito a correggere il comando, almeno per l'uso in Solaris.

Ecco il primo passaggio convoluto (ciò presuppone che TUTTI i file nella directory siano nello stesso formato: [RANGE] _ [yyyymmdd] .dat). Scommetto che ci sono modi migliori per fare questo, ma questo funziona con i miei dati di test (in realtà, ho trovato un modo migliore in questo momento; vedi sotto):

ls | awk -F_ '{print $1 " " $2}' | sort -n -k 2 | tail -1 | sed 's/ /_/' 

... durante la scrittura questo fuori , Ho scoperto che puoi fare solo questo:

ls | sort -n -t _ -k 2 | tail -1 

Scomporlo in parti.

ls 

Abbastanza semplice ... ottiene l'elenco delle directory, solo i nomi dei file. Ora posso collegarlo al prossimo comando.

awk -F_ '{print $1 " " $2}' 

Questo è il comando AWK. ti consente di prendere una riga di input e modificarla in un modo specifico. Qui, tutto quello che sto facendo è specificare che awk dovrebbe interrompere l'input ovunque ci sia un underscord (_). Lo faccio con l'opzione -F. Questo mi dà due metà di ciascun nome di file. Dico quindi awk di emettere la prima metà ($ 1), seguita da uno spazio ("") , seguito dalla seconda metà ($ 2). Si noti che lo spazio era la parte che mancava dal mio suggerimento iniziale. Inoltre, questo non è necessario, dal momento che è possibile specificare un separatore nel comando di ordinamento sottostante.

Ora l'uscita è divisa in [RANGE] [aaaammgg] .dat su ogni riga. Ora possiamo ordinare:

sort -n -k 2 

Questo prende l'input e lo ordina in base al 2 ° campo. Il comando sort usa gli spazi bianchi come separatore per impostazione predefinita. Durante la scrittura di questo aggiornamento, ho trovato la documentazione per l'ordinamento, che consente di specificare il separatore, quindi AWK e SED non sono necessari. Prendere la ls e collegarla al seguente tipo:

sort -n -t _ -k 2 

Questo raggiunge lo stesso risultato. Ora si vuole solo l'ultimo file, in modo da:

tail -1 

Se è stato utilizzato awk per separare il file (che è solo l'aggiunta di complessità in più, in modo da non farlo imbarazzato), è possibile sostituire lo spazio con una sottolineatura di nuovo con sed:

sed 's/ /_/' 

Alcune buone informazioni qui, ma sono sicuro che la maggior parte delle persone non stanno andando a leggere fino in fondo come questo.

+0

Ho provato questo ma non ha funzionato. Potresti spiegare cosa sta facendo esattamente. grazie – ziggy

+0

Bene, ho aggiornato dopo il test. Ho dovuto aggiustare qualcosa nel mio comando awk e poi ho scoperto che non era davvero necessario. La soluzione è in cima, la spiegazione è lunga e non necessaria, ma mi è piaciuto scriverlo. –

+0

Funziona per me. Per favore, abbi un figlio. –

2

Prova:

$ ls -lr 

Speranza che aiuta.

+0

Salve, non lo ordinerebbe usando il timestamp di sistema per il file? Ero interessato al timestamp sul nome file effettivo. Grazie – ziggy

+0

No, ordina i file in base al nome in base alle impostazioni locali. Se si desidera ordinare in base al timestamp del sistema, è necessario il flag '-t'. – igor

1

Usa:

ls -r -1 AA_*.dat | head -n 1 

(assumendo non ci sono altri file che corrispondono AA_*.dat)

3

Questo dovrebbe funzionare:

newest=$(ls | sort -t _ -k 2,2 | tail -n 1) 
others=($(ls | sort -t _ -k 2,2 | head -n -1)) 

mv "$newest" newdir 
mv "${others[@]}" otherdir 

Non funzionerà se ci sono spazi nei nomi dei file anche se si potrebbe modificare la variabile IFS di influenzare questo.

+0

Ciao, a cosa servono le parentesi tonde? – ziggy

+0

@ziggy: Intendi il set esterno sulla seconda riga? Creano un array che viene utilizzato nell'ultima riga. –

+0

Ciao Dennis, Mi riferivo a entrambe le parentesi tonde interne ed esterne. Ho provato a eseguire quanto sopra, ma le parentesi causano errori di sintassi. Sto usando la shell di Bourne. Sono questi costrutti specifici della shell Korn? – ziggy

1

A causa della convenzione di denominazione dei file, l'ordine alfabetico è lo stesso dell'ordine di data. Sono abbastanza sicuro che in bash '*' si espande in ordine alfabetico (ma non riesco a trovare alcuna prova nella pagina di manuale), lo fa ls, quindi il file con la data più recente, sarebbe l'ultimo in ordine alfabetico.

Pertanto, in bash

mv $(ls | tail -1) first-directory 
mv * second-directory 

dovrebbe fare il trucco.

Se si vuole essere più preciso circa la scelta del file, quindi sostituire * con qualcos'altro - per esempio AA_*.dat

+0

Funziona anche, ma sto cercando di evitare di fare affidamento sul sistema per fare l'ordinamento per me (cioè tramite il cmd ls). Grazie – ziggy

+0

Perché non vuoi fare affidamento su 'ls' - cosa intendi con 'sistema'? – Beano

1

La mia soluzione a questo è simile agli altri, ma un po 'più semplice.

ls -tr | tail -1 

Qual è in realtà non fa altro che fare affidamento su ls per ordinare l'output, quindi utilizza la coda per ottenere l'ultimo nome del file elencato.

Questa soluzione non funzionerà se il nome file richiesto ha un punto iniziale (ad esempio .profile).

Questa soluzione funziona se il nome del file contiene uno spazio.

Problemi correlati