Як я можу визначити вміст файлів, знайдених за допомогою пошуку, в один файл?


11

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

Однак мені вдалося використати testdiskта photorecвідновити більшість даних. Тож тепер у мене всі ці дані поширюються майже на 25 000 каталогів. Більшість файлів - це файли .txt, решта - файли зображень. У кожному каталозі є більше 300 файлів .txt.

Я можу grepабо використовувати findдля отримання певних рядків з файлів .txt та виведення їх у файл. Наприклад, ось рядок, який я використав для перевірки наявності моїх даних у відновлених файлах:

find ./recup*/ -name '*.txt' -print | xargs grep -i "searchPattern"

Я можу вивести "searchPattern" у файл, але це просто дає мені цей шаблон. Ось що я хотів би зробити:

Пройдіть усі файли та знайдіть певний рядок. Якщо цей рядок знайдений у файлі, переведіть ВСЕ вміст цього файлу у вихідний файл. Якщо шаблон знайдено у більш ніж одному файлі, додайте вміст наступних файлів до цього вихідного файлу. Зауважте, що я просто не хочу виводити шаблон, який я шукаю, але ВСЕ вміст файлу, в якому знайдено шаблони.

Я думаю, що це можливо, але я просто не знаю, як схопити весь вміст файлу після стискання певного шаблону з нього.


Отже, з поданою вами командою вона дає результати, які ви шукаєте, але ви хочете перенаправити вихідний текст у текстовий файл?
ryekayo

Прочитавши моє запитання, той абзац, що починається з "Перейти ...", звучить так само, як псуедокод. Можливо, я можу отримати його код за допомогою декількох рядків for / if Python. Я зроблю це, коли я чекаю більш усвідомленої відповіді
Амі,

Це, безумовно, psuedocode, і я впевнений, що ви можете знайти спосіб це зробити і в баші.
ryekayo

@ryekayo, Так, це дає мені вихід, але це лише для того, щоб знайти, в якому файлі знаходиться певний тип даних, який підказує мені, що більше цих даних є у цьому файлі. Тому я хочу захопити все у цьому файлі та записати їх до іншого файлу.
Амі

Ви, ймовірно, можете зафіксувати цю команду в якомусь операторі if чи навіть у випадку переключення, який може викликати функцію, яка може вивести вміст на основі регістру або результатів оператора if
ryekayo,

Відповіді:


10

Якщо я правильно розумію вашу мету, наступне зробить те, що ви хочете:

find ./recup*/ -name '*.txt' -exec grep -qi "searchPattern" {} \; -exec cat {} \; > outputfile.txt

Це буде шукати всі *.txtфайли в ./recup*/, протестуйте кожен з них searchPattern, якщо він відповідає catфайлу. Вихід з усіх cated файлів буде спрямований у outputfile.txt.

Повторіть для кожного шаблону та вихідного файлу.


Якщо у вас дуже велика кількість каталогів ./recup*, які можуть збігатися , ви можете закінчити argument list too long error. Простий спосіб цього зробити замість цього:

find ./ -mindepth 2 -path './recup*.txt' -exec grep -qi "searchPattern" {} \; -exec cat {} \; > outputfile.txt

Це відповідатиме повним шляхом. Так ./recup01234/foo/bar.txtбуде відповідати. Це -mindepth 2так, що воно не збігається ./recup.txt, або ./recup0.txt.


Так, я думаю, що це вдасться. І це дає мені базу для роботи. Оскільки я буду шукати декілька рядків, я думаю, що для / якщо біт коду з декількома elif's допоможе мені автоматизувати завдання. Дякую
Амі,

Це навіть краще, ніж те, про що я думав,
хаха

Це, здається, не спрацювало. Отримав цю помилку: "не вдається виконати / usr / bin / find: список аргументів занадто довгий"
Ami

@Ami оновив відповідь для вирішення цього питання.
Патрік

2
@Ami Якщо ви використовуєте кілька рядків, можливо, буде простіше просто зберегти всі позитивні імена файлів в інший файл ( grep -l), потім |sort|uniqі catзі списку файлів.
Sparhawk

3

Замість того, щоб виводити шаблон, виведіть ім'я файлу, використовуючи "-l" на grep, а потім використовуйте це як вхід для cat.

find ./recup*/ -name '*.txt' -print | xargs grep -li "searchPattern" | xargs cat

або

cat $( find ./recup*/ -name '*.txt' -print | xargs grep -li "searchPattern")

Я підозрюю, що ви можете заповнити решту даних. BTW, якщо у назви файлів можуть бути пробіли чи інші непарні символи (навряд чи в цьому конкретному випадку, але для майбутніх цілей), використовуйте -print0 у знаходженні та -Z на grep, у поєднанні з опцією -0 на xargs для використання нульові байти між іменами файлів, а не новими рядками.

find ./recup*/ -name '*.txt' -print0 | xargs -0 grep -Zli "searchPattern" | xargs -0 cat

2
Мені також подобається варіант "два -exec" Патріка, за винятком того, що він спричинить новий форк (ну, клон ()) та exec для кожного файлу. Зазвичай ви можете використовувати, \+а не \;уникати цієї проблеми, але я не знаю, як це працює з парою -exec аргументів (я підозрюю, що "погано"). Використовуючи пару xargs, у вас з'явиться лише пара нових процесів, які мають бути швидшими з великою кількістю файлів.
dannysauer

Це теж добре виглядає. Дякую. Одне запитання нуб: Кішка після останніх xargs повинна виводити файл, правда?
Амі

Коли я вперше прочитав це, я не думав, що в питанні було вказано, куди має рухатися вміст файлу. Всі три з цих команд помістити вміст файлу (ів) на STDOUT, так що ви б просто додати (до самого кінця) >afileабо |acommandчи то , що підходить для вашої ситуації. :)
dannysauer

Хороша відповідь, мені потрібно було прописати pg_hba.conf sudo find /* -name pg_hba.conf | xargs sudo cat
робота над додатком

Це трохи поза темою, але я вважаю за краще використовувати sudo xargsзамість цього xargs sudo. Під час запуску xargs sudoвін будує командний рядок, припускаючи, що команда є sudo cat args. Але кіт знаходиться в / біні, тож тут біжить судо /bin/cat args. Якщо ваша команда знаходиться у більш тривалому каталозі, наприклад / usr / local / bin, тоді команда sudo насправді запускається, це може призвести до занадто довгого командного рядка та помилки, яку важко відстежити. На додаток до цього, sudo xargsпросто записуйте, що ви запустили xargs, тоді як xargs sudoзаписує команду з усіма аргументами - в результаті виходять деякі довгі рядки журналу sudo. :)
dannysauer

1

Це не зовсім оптимальний код, але це дуже просто і буде добре працювати, якщо ефективність не буде проблемою. Проблема полягає в тому, що вона буде проглядати файли кілька разів, навіть якщо рядок у них уже знайдено.

По-перше, знайдіть свої рядки і запишіть відповідні файли до списку.

find ./recup*/ -name '*.txt' -execdir grep -il "searchPattern" {} >> /tmp/file_list \;

Повторіть цей крок, замінивши searchPatternза необхідності. Це створює список відповідних файлів на /tmp/file_list.

Проблема полягає в тому, що у цьому файлі можуть бути дублікати. Отже, ми можемо замінити дублікати на |sort|uniq. sortЧастина поміщає дубльовані поруч один з одним, так що uniqїх можна видалити. Потім ви можете catці файли разом, використовуючи xargs(з кожним ім'ям файлу, відокремленим новим рядком \n). Отже,

</tmp/file_list sort | uniq | xargs -d "\n" cat > final_file.txt

На відміну від інших відповідей, у цьому є два кроки та тимчасовий файл, тому я дійсно рекомендую його, лише якщо у вас є кілька шаблонів.


0

Залежно від вашої оболонки та оточення, ви можете зробити щось подібне (in bash)

while IFS= read -r -d '' file; do
  if grep -qim1 'searchPattern1\|searchPattern2\|searchPattern3' "$file"; then
    cat "$file" >> some/other/file
  fi
done < <(find ./recup*/ -name '*.txt' -print0)

Якщо ви хочете розділити результати відповідно до шаблону, ви можете змінити їх на щось подібне

while IFS= read -r -d '' file; do
  if grep -qim1 'searchPattern1' "$file"; then
    cat "$file" >> some/other/file1
  elif grep -qim1 'searchPattern2' "$file"; then
    cat "$file" >> some/other/file2
  elif grep -qim1 'searchPattern3' "$file"; then
    cat "$file" >> some/other/file3
  fi
done < <(find ./recup*/ -name '*.txt' -print0)

Що робить біт після "зробленого"? Що мені насправді подобається, - це змінити, якщо блокувати, щоб файли, що містять узгоджений візерунок, записувались на інше.
Амі

Він просто перераховує знайдені файли '.txt', кожен з яких закінчується нульовим символом (так що це безпечно для імен файлів, що містять пробіли та інші символи). Потім whileцикл зчитує цей список і виконує grep/ умовну catчастину.
steeldriver

Коли я намагаюся запустити код, я отримую цю помилку: ./recoverData.sh: Синтаксична помилка: "(" несподівано. Це надходить з дужок навколо команди find
Ami,

Яку оболонку ви використовуєте? синтаксис заміщення процесу є специфічним для bash - звідси і моя кваліфікація "Залежно від вашої оболонки та оточення"
steeldriver

1
Ви можете або виконати команди (команди) безпосередньо в інтерактивній оболонці bash, або помістити їх у файл, перший рядок якого містить шебанг #!/bin/bash, зробити його виконуваним chmod +x recoverData.shі виконати за допомогою ./recoverData.sh. Як НЕ використовувати , sh recoverData.shтак як /bin/sh, швидше за все, dashоболонка .
steeldriver
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.