Ho molti file di testo in una directory e desidero rimuovere l'ultima riga di ogni file nella directory.
Come posso farlo?
Ho molti file di testo in una directory e desidero rimuovere l'ultima riga di ogni file nella directory.
Come posso farlo?
Risposte:
Puoi usare questo simpatico oneliner se hai GNU sed.
sed -i '$ d' ./*
Rimuoverà l'ultima riga di ogni file non nascosto nella directory corrente. Switch -iper GNU sedsignifica operare sul posto e '$ d'comanda seddi eliminare l'ultima riga (che $significa l'ultima e che dsignifica cancellare).
-iquale è un GNUismo, quindi questo è discutibile, ma perderei la mia vecchia barba se non riuscissi a sottolineare che alcune versioni precedenti di sednon ti permettono di mettere uno spazio bianco tra il $e il d(o, in generale, tra lo schema e il comando).
*(.)glob per i file regolari, non conosco altre shell.
Le altre risposte hanno tutti problemi se la directory contiene qualcosa di diverso da un normale file o un file con spazi / righe nel nome del file. Ecco qualcosa che funziona indipendentemente:
find "$dir" -type f -exec sed -i '$d' '{}' '+'
find "$dir" -type f: trova i file nella directory $dir
-type f che sono file regolari;-exec eseguire il comando su ogni file trovatosed -i: modifica i file in atto;'$d': elimina ( d) l'ultima ( $) riga.'+': dice a find di continuare ad aggiungere argomenti sed(un po 'più efficiente dell'esecuzione del comando per ogni file separatamente, grazie a @zwol).Se non vuoi scendere nelle sottodirectory, puoi aggiungere l'argomento -maxdepth 1a find.
findquesto è scritto in modo più efficiente find $dir -type f -exec sed -i '$d' '{}' '+'.)
-print0non è presente nel comando completo, perché inserirlo nella spiegazione?
-depth 0non funziona (findutils 4.4.2), dovrebbe invece essere -maxdepth 1.
-exec.
Usare GNU sed -i '$d'significa leggere l'intero file e crearne una copia senza l'ultima riga, mentre sarebbe molto più efficiente troncare il file in atto (almeno per file di grandi dimensioni).
Con GNU truncatepuoi fare:
for file in ./*; do
[ -f "$file" ] &&
length=$(tail -n 1 "$file" | wc -c) &&
[ "$length" -gt 0 ] &&
truncate -s "-$length" "$file"
done
Se i file sono relativamente piccoli, sarebbe probabilmente meno efficiente poiché esegue diversi comandi per file.
Nota che per i file che contengono byte extra dopo l'ultimo carattere di nuova riga (dopo l'ultima riga) o in altre parole se hanno un'ultima riga non delimitata , a seconda taildell'implementazione, tail -n 1verranno restituiti solo quei byte extra (come GNU tail), o l'ultima riga (correttamente delimitata) e quei byte extra.
|wc -cnella tailchiamata? (o a ${#length})
${#length}non funzionerebbe poiché conta i caratteri, non i byte e $(...)rimuove il carattere di coda nuova in modo ${#...}da essere disattivato di uno anche se tutti i caratteri fossero a byte singolo.
Un approccio più portatile:
for f in ./*
do
test -f "$f" && ed -s "$f" <<\IN
d
w
q
IN
done
Non penso che questo abbia bisogno di spiegazioni ... tranne forse che in questo caso dè lo stesso di $dpoiché edseleziona di default l'ultima riga.
Questo non cercherà ricorsivamente e non elaborerà i file nascosti (aka dotfile).
Se vuoi modificare anche quelli vedi Come abbinare * con i file nascosti all'interno di una directory
[[]]a []allora sarà completamente conforme a POSIX. ( [[ ... ]]is a Bashism.)
[[non è un bashismo )
One-liner conforme a POSIX per tutti i file a partire ricorsivamente dalla directory corrente, inclusi i file di punti:
find . -type f -exec sh -c 'for f; do printf "\$d\nx\n" | ex "$f"; done' sh {} +
Solo per .txtfile, in modo non ricorsivo:
find . -path '*/*/*' -prune -o -type f -name '*.txt' -exec sh -c 'for f; do printf "\$d\nx\n" | ex "$f"; done' sh {} +
Vedi anche: