Modo rapido per eliminare file con meno di x righe


10

Qual è un modo veloce e non troppo complicato per eliminare tutti i file in una directory che sono sotto le righe x, in bash?

Risposte:


10

Ecco una soluzione POSIX che dovrebbe essere piuttosto semplice da capire:

find . -type f -exec awk -v x=10 'NR==x{exit 1}' {} \; -exec echo rm -f {} \;

Come nella risposta di Stephane , rimuovi il echocontento di ciò che verrà rimosso.


Spiegazioni, scritte per quelli totalmente nuovi in ​​Unix / Linux:

Il punto .rappresenta la directory corrente. findtrova ricorsivamente file e directory .e può fare cose con essi.

-typeè uno dei find's primarie ; è un test che verrà eseguito per ogni file e directory che si trova ricorsivamente (all'interno .), e il resto delle primarie sulla riga vengono valutate solo se questo risulta in "vero".

In questo caso particolare, continuiamo solo se abbiamo a che fare con un file normale , non una directory o qualcos'altro (ad esempio un dispositivo a blocchi).


Il -execprimario (of find) chiama un comando esterno e procede al primario successivo solo se il comando esterno esce correttamente (stato di uscita "0"). Il {}è sostituito con il nome del file che viene "considerato" dal findcomando. Quindi la prima -execchiamata è equivalente al seguente comando di shell, eseguito per ogni file a sua volta:

awk -v x=10 'NR==x{exit 1}' ./somefilename

Awk è un'intera lingua in sé, progettata per gestire file di testo delimitati come CSV. I condizionali e i comandi Awk (che sono racchiusi tra virgolette singole e iniziano con le lettere NR) vengono eseguiti per ogni riga di un file di testo. (Loop implicito.)

Per imparare completamente Awk, consiglio vivamente il Tutorial di Grymoire , ma spiegherò le funzionalità di Awk utilizzate nel comando sopra.


Il -vflag su Awk ci consente di impostare una variabile Awk (una volta) prima che i comandi Awk vengano eseguiti (per ogni riga del file.) In questo caso impostiamo xsu 10.


NRè una speciale variabile Awk riferita alla " N umber dell'attuale R ecord". In altre parole, è il numero di riga che stiamo osservando in un particolare passaggio attraverso il loop.

(Si noti che è possibile, anche se inusuale, per usare un diverso " R ECORD S eparator" da quello di default di un carattere di nuova riga, con un comando RS. Ecco un esempio di giocare con separatori record. )


Gli script Awk in generale sono costituiti da condizioni (parentesi graffe esterne) combinate con azioni (all'interno di parentesi graffe). Possono esserci condizioni composte e azioni composte e sono presenti una condizione predefinita (true) e un'azione predefinita (stampa), ma non è necessario non preoccuparti di quelli.

La condizione qui è: "È questa la decima riga?" Se questo è il caso, usciamo con uno stato di uscita diverso da zero, che nello scripting della shell significa "interruzione del comando non riuscita".

Pertanto, l'unico modo in cui questo comando Awk verrà chiuso correttamente è se la fine del file viene raggiunta prima che venga raggiunta la decima riga.

Quindi, se lo script Awk viene chiuso correttamente, significa che hai un file di meno di dieci righe.


La -execchiamata successiva (se si rimuove il echo) rimuoverà ogni file (che arriva così lontano nella valutazione delle findprimarie) eseguendo:

rm -f ./somefilename

5

Supponendo findun'implementazione che supporti il -readablepredicato (se il tuo findnon lo supporta, rimuovilo, otterrai solo messaggi di errore per file non leggibili o sostituiscili con -exec test -r {} \;):

x=10 find . -type f -readable -exec sh -c '
  for file do
    lines=$(wc -l < "$file") && [ "$((lines))" -lt "$x" ] && echo rm -f "$file"
  done' sh {} +

Rimuovi il echose felice.

Questo non è particolarmente efficiente nel senso che conta tutte le righe in ogni file, mentre ha solo bisogno di fermarsi al xesimo uno e gestisce uno wc(e potenzialmente uno rm) di comando per ogni file.

Con GNU awk, puoi renderlo molto più efficiente con:

x=10
find . -type f -readable -exec awk -v x="$x" -v ORS='\0' '
  FNR == x {nextfile}
  ENDFILE {if (FNR < x) print FILENAME}' {} +|
  xargs -r0 echo rm -f

(di nuovo, rimuovilo echoquando felice).

Lo stesso con perl:

x=10 find . -type f -readable -exec perl -Tlne '
  if ($. == $ENV{x}) {close ARGV}
  elsif (eof) {print $ARGV; close ARGV}' {} +

Sostituire printcon unlinkse felice.


1. Qual è l'ultimo shper? 2. È wc -l < "$file"più veloce di wc -l "$file"? 3. Come fa sh a conoscere il valore di $x, che è definito nella shell Bash chiamante?

3
@tomas, l'ultimo shè ciò che accade in quello script in linea $0, da utilizzare ad esempio per i messaggi di errore. wc -l "$file"stamperebbe il nome del file che non vogliamo qui e funzionerebbe wcanche se il file non potesse essere aperto. $xviene esportato in find( x=10 find...) che a sua volta lo passa a sh.
Stéphane Chazelas,

Grazie! Ma immagino che questo errore che ottengo su OSX significhi che la mia versione di Bash non supporta il flag -readable? find: -readable: unknown primary or operator.
Durrrutti,

1
@durrrutti, non dipende da bash. bashè solo un interprete della riga di comando, ma finddell'implementazione. -readableè un'estensione GNU, non è disponibile in OS / X find. Viene utilizzato solo per limitare i file leggibili (non si sarebbe in grado di ottenere il conteggio delle righe per i file non leggibili). È possibile ometterlo per il primo, si otterrebbero quindi messaggi di errore quando si aprono i file wcper i file che non sono leggibili.
Stéphane Chazelas,

@ StéphaneChazelas, questa risposta è così complicata che mi viene da chiedermi: ho perso alcuni casi limite con la mia risposta? :)
Wildcard

2

Per completezza, a parte AWK puoi anche usare GNU sed per ottenere lo stesso risultato:

find . -type f -exec sed 11q1 '{}' ';' -exec echo rm -f '{}' ';'

Il risultato è una riga di comando un po 'più concisa.

Spiegazione

11 - is the address, i.e. "the eleventh line"
q - is for _q_uit (abort the execution)
1 - is the exit code parameter for q (GNU sed extension) 
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.