Szybki sposób usuwania plików zawierających mniej niż x linii

10

Jaki jest szybki i niezbyt skomplikowany sposób usunięcia wszystkich plików w katalogu o długości poniżej x linii, w bash?

durrrutti
źródło

Odpowiedzi:

10

Oto rozwiązanie POSIX, które powinno być łatwe do zrozumienia:

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

Jak w odpowiedzi Stephane'a , usuń, echokiedy będziesz zadowolony z tego, co zostanie usunięte.


Objaśnienia, napisane dla tych, którzy są zupełnie nowi w systemach Unix / Linux:

Kropka .oznacza bieżący katalog. findznajduje pliki i katalogi rekurencyjnie wewnątrz .i może z nimi robić różne rzeczy.

-typejest jedną z find„S barw podstawowych ; jest to test, który zostanie przeprowadzony dla każdego pliku i katalogu, który zostanie znaleziony rekurencyjnie (wewnątrz .), a reszta liczb podstawowych w linii zostanie oceniona tylko wtedy, gdy da to wynik „prawda”.

W tym konkretnym przypadku kontynuujemy tylko wtedy, gdy mamy do czynienia ze zwykłym plikiem , a nie katalogiem lub czymś innym (np. Urządzeniem blokowym).


-execPodstawowej (z find) wywołuje polecenie zewnętrzne, i przechodzi do następnego jedynie podstawowy jeśli polecenie zakończyło się pomyślnie zewnętrzny (stan wyjścia „0”). {}Zostaje zastąpiona nazwą pliku jest „za” przez findkomendę. Pierwsze -execwywołanie jest więc równoważne z następującą komendą powłoki, wykonywaną kolejno dla każdego pliku:

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

Awk to cały język sam w sobie, zaprojektowany do obsługi plików tekstowych z ogranicznikami, takich jak CSV. Warunki i polecenia Awk (zawarte między pojedynczymi cudzysłowami i rozpoczynające się od liter NR) są wykonywane dla każdego wiersza pliku tekstowego. (Implicit looping.)

Aby w pełni nauczyć się Awk, bardzo polecam Samouczek Grymoire , ale wyjaśnię funkcje Awk użyte w powyższym poleceniu.


-vFlagę Awk pozwala nam ustawić zmienną awk (raz) przed polecenia awk są wykonywane (dla każdej linii pliku). W tym przypadku mamy ustawiony xna 10.


NRJest to specjalna zmienna awk odnosi się do „ N umbra obecnego R ECORD”. Innymi słowy, jest to numer linii, na który patrzymy w konkretnym przejściu przez pętlę.

(Należy zauważyć, że jest możliwe, choć nietypowe, użycie innego „ separatora R ecord S ” niż domyślny znak nowej linii, poprzez ustawienie RS. Oto przykład gry z separatorami rekordów. )


Skrypty Awk ogólnie składają się z warunków (poza nawiasami klamrowymi) połączonych z akcjami (wewnątrz nawiasów klamrowych). Mogą istnieć warunki złożone i akcje złożone, a także warunek domyślny (true) i akcja domyślna (print), ale potrzebujemy nie zawracaj sobie tym głowy.

Warunek tutaj jest: „Czy to jest linia 10-ci?” W takim przypadku wychodzimy z niezerowym statusem wyjścia, co w skrypcie powłoki oznacza „nieudane zakończenie polecenia”.

Dlatego jedynym sposobem na pomyślne zamknięcie tego polecenia Awk jest osiągnięcie końca pliku przed osiągnięciem 10. linii.

Więc jeśli skrypt Awk zakończy się pomyślnie, oznacza to, że masz plik krótszy niż dziesięć linii.


Następne -execwywołanie (jeśli usuniesz echo) usunie każdy plik (który jest tak daleko w ocenie podstawowych find), uruchamiając:

rm -f ./somefilename
Dzika karta
źródło
5

Zakładając findimplementację, która obsługuje -readablepredykat (jeśli findgo nie obsługujesz, po prostu usuń go, otrzymasz tylko komunikaty o błędach dla plików nieczytelnych lub zamień na -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 {} +

Usuń echojeśli szczęśliwy.

To nie jest szczególnie skuteczny w tym, że zlicza wszystkie linie w każdym pliku, potrzebuje tylko zatrzymać się na xth jeden i działa jeden wc(i potencjalnie jeden rm) polecenie dla każdego pliku.

Dzięki GNU awkmożesz zwiększyć wydajność dzięki:

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

(ponownie usuń, echogdy będzie szczęśliwy).

To samo z perl:

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

Wymień printsię unlinkjeśli szczęśliwy.

Stéphane Chazelas
źródło
1. Po co ostatni sh? 2. Czy jest wc -l < "$file"szybszy niż wc -l "$file"? 3. Skąd sh zna wartość $x, która jest zdefiniowana w wywołującej powłoki Bash?
3
@tomas, ostatnia shjest zawarta w tym wbudowanym skrypcie $0, na przykład do komunikatów o błędach. wc -l "$file"wypisałby nazwę pliku, której tutaj nie chcemy i działałby wcnawet, jeśli pliku nie można otworzyć. $xjest eksportowany do find( x=10 find...), który sam przekazuje go do sh.
Stéphane Chazelas,
Dzięki! Ale zgaduję, że ten błąd, który pojawia się w systemie OSX, oznacza, że ​​moja wersja Bash nie obsługuje flagi -czytelnej? find: -readable: unknown primary or operator.
durrrutti,
1
@drrrutti, to nie wszystko bash. bashto tylko interpreter wiersza poleceń, ale findimplementacji. -readableJest to rozszerzenie GNU, nie jest dostępna w systemie OS / X find. Służy tylko do ograniczenia plików, które są czytelne (nie będziesz w stanie uzyskać liczby wierszy dla plików nieczytelnych). Możesz pominąć to dla pierwszego, wtedy po prostu otrzymujesz komunikaty o błędach podczas otwierania plików dla wcplików, których nie można odczytać.
Stéphane Chazelas,
@ StéphaneChazelas, ta odpowiedź jest tak trudna, że ​​zastanawiam się: czy tęskniłem za przypadkami na krawędzi mojej odpowiedzi? :)
Wildcard,
2

Dla kompletności, poza AWK możesz również użyć GNU sed, aby osiągnąć ten sam wynik:

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

Co powoduje, że wiersz poleceń jest nieco bardziej zwięzły.

Wyjaśnienie

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) 
zepelin
źródło