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

10

Какой быстрый и не слишком сложный способ удалить все файлы в каталоге длиной менее x строк в bash?

durrrutti
источник

Ответы:

10

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

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

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


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

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

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

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


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

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

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

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


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


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

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


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

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

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

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


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

rm -f ./somefilename
Wildcard
источник
5

Предполагая 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 -v x="$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если счастлив.

Стефан Шазелас
источник
1. Для чего последний sh? 2. wc -l < "$file"Быстрее чем wc -l "$file"? 3. Откуда sh знает значение $x, которое определяется в вызывающей оболочке Bash?
3
@tomas, последнее sh, что входит в этот встроенный скрипт $0, который будет использоваться, например, для сообщений об ошибках. wc -l "$file"напечатает имя файла, который нам здесь не нужен, и запустится, wcдаже если файл не может быть открыт. $xэкспортируется в find( x=10 find...), который сам передает его sh.
Стефан Шазелас
Спасибо! Но я предполагаю, что эта ошибка, которую я получаю в OSX, означает, что моя версия Bash не поддерживает флаг -readable? find: -readable: unknown primary or operator,
durrrutti
1
@durrrutti, это не до bash. bashэто просто интерпретатор командной строки, но findреализации. -readableэто расширение GNU, не доступен в OS / X find. Он используется только для ограничения файлов, которые доступны для чтения (вы не сможете получить количество строк для нечитаемых файлов). Вы можете опустить его для первого, тогда вы просто получите сообщения об ошибках при открытии файлов для wcфайлов, которые не читаются.
Стефан Шазелас
@ StéphaneChazelas, этот ответ настолько хитрый, что я задаюсь вопросом: пропустил ли я какие-либо крайние случаи с моим ответом? :)
Wildcard
2

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

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

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

объяснение

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) 
дирижабль
источник