Быстрый способ удаления файлов с строками меньше x

Что такое быстрый и не слишком сложный способ удаления всех файлов в каталоге, длина которого составляет х строк, в bash?

Предположим, что реализация find , поддерживающая предикат -readable (если ваш find не поддерживает его, просто удалите его, вы просто получите сообщения об ошибках для нечитаемых файлов или замените на -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 {} + 

Удалите echo если он счастлив.

Это не особенно эффективно, поскольку он подсчитывает все строки в каждом файле, в то время как он должен останавливаться только на x м, и он запускает одну команду wc (и потенциально одну rm ) для каждого файла.

С GNU awk вы можете сделать его намного более эффективным:

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

(опять же, удалите echo когда будете счастливы).

То же самое с perl :

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

Замените print с unlink если он счастлив.

Вот решение POSIX, которое должно быть довольно простым:

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

Как и в ответе Стефана , удалите echo когда будете довольны тем, что будет удалено.


Объяснения, написанные для совершенно новых для Unix / Linux:

Точка . представляет текущий каталог. find файлов и каталогов рекурсивно внутри . , и может делать с ними что-то.

-type – это одно из -type find ; это тест, который будет выполняться для каждого файла и каталога, который рекурсивно найден (внутри . ), а остальные праймериз в строке оцениваются только в том случае, если это приводит к «истинному».

В этом конкретном случае мы продолжаем, только если мы имеем дело с обычным файлом , а не с каталогом или чем-то другим (например, блочным устройством).


Первоначальная ( find ) -exec вызывает внешнюю команду и переходит к следующей первичной, если внешняя команда успешно завершена (статус выхода «0»). {} Заменяется именем файла, «считающимся» командой find . Таким образом, первый -exec вызов эквивалентен следующей команде оболочки, выполняемой для каждого файла по очереди:

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

Awk – это целый язык сам по себе, предназначенный для обработки текстовых файлов с разделителями, таких как CSV. Для каждой строки текстового файла выполняются условные обозначения и команды Awk (которые заключаются между одинарными кавычками и начинаются с букв NR ). (Неявный цикл.)

Чтобы полностью изучить Awk, я настоятельно рекомендую учебник Grymoire , но я объясню функции Awk, используемые в приведенной выше команде.


Флаг -v для Awk позволяет нам установить переменную Awk (один раз) до выполнения команд Awk (для каждой строки файла.) В этом случае мы устанавливаем x на 10 .


NR – это специальная переменная Awk, относящаяся к « N umber текущего R ecord». Другими словами, это номер строки, на которую мы смотрим в любом конкретном проходе через цикл.

(Обратите внимание, что возможно, хотя и необычно, использовать другой « R ecord S eparator», чем по умолчанию символ новой строки, путем установки RS . Вот пример игры с разделителями записей. )


Сценарии Awk в целом состоят из условий (за пределами фигурных скобок) в сочетании с действиями (внутри фигурных скобок). Могут быть сложные условия и составные действия, и есть условие по умолчанию (true) и действие по умолчанию (печать), но нам нужно Не беспокойтесь об этом.

Условие здесь: «Это 10-я линия?» Если это так, мы выходим с ненулевым статусом выхода, который в сценариях оболочки означает «безуспешное завершение команды».

Таким образом, единственный способ, которым эта команда Awk будет успешно завершена, – это то, что конец файла достигнут до достижения 10-й строки.

Поэтому, если сценарий Awk успешно завершен, значит, у вас есть файл длиной менее десяти строк.


Следующий -exec вызов (если вы удалите echo ) удалит каждый файл (который так далеко оценивает праймериз find ), запустив:

 rm -f ./somefilename 

Для полноты, помимо AWK, вы также можете использовать GNU sed для достижения того же результата:

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

Это приводит к более сжатию командной строки.

объяснение

 11 - is the address, ie "the eleventh line" q - is for _q_uit (abort the execution) 1 - is the exit code parameter for q (GNU sed extension)