Швидкий спосіб видалення файлів із меншим числом x рядків


10

Який швидкий і не надто складний спосіб видалити всі файли в каталозі, розміщеному під x рядками, в bash?

Відповіді:


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 праймеріз ; це тест, який буде виконуватися для кожного файлу та каталогу, які рекурсивно знайдені (всередині .), а решта праймериз у рядку оцінюються лише у випадку, якщо це призведе до "істини".

У цьому конкретному випадку ми продовжуємо лише, якщо ми маємо справу зі звичайним файлом , а не з каталогом чи чимось іншим (наприклад, блоковим пристроєм.)


-execПервинні (з find) викликає зовнішню команду, і тільки переходить до наступного первинним , якщо зовнішня команда завершується успішно (статус виходу «0»). The {}замінюється тим, що 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 umber поточного R-запису ". Іншими словами, це номер рядка, який ми дивимося в будь-якому конкретному проході через цикл.

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


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

Тут умова така: "Це 10-й рядок?" Якщо це так, ми виходимо з ненульовим статусом виходу, що в сценарії оболонки означає "невдале завершення команди".

Таким чином, єдиний спосіб успішного виходу цієї команди Awk - це досягнення кінця файлу до досягнення 10-го рядка.

Отже, якщо сценарій Awk успішно закінчується, це означає, що у вас файл менше десяти рядків.


Наступний -execвиклик (якщо ви вилучите echo) видалить кожен файл (який так далеко оцінюється з find'праймеріз') запуском:

rm -f ./somefilename

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 не підтримує прапор-читабельний? 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) 
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.