Как мне отследить содержимое файлов, найденных с помощью команды find, в одном файле?

11

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

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

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

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

Я могу вывести "searchPattern" в файл, но это просто дает мне этот шаблон. Вот что я действительно хотел бы достичь:

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

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

Ami
источник
Итак, с помощью предоставленной вами команды, она дает вам результаты, которые вы ищете, но вы хотите перенаправить вывод в текстовый файл?
Ryekayo
После прочтения моего вопроса тот абзац, который начинается с «Проходить ...», звучит так же, как psuedocode. Может быть, я смогу получить код с несколькими строками кода for / if Python. Дадим ему шанс, пока я жду более информированного ответа
Ами
Это, безусловно, psuedocode, и я уверен, что вы можете найти способ сделать это и в bash.
Ryekayo
@ryekayo, Да, это дает мне вывод, но это просто, чтобы найти, в каком файле находится определенный тип данных, что говорит мне, что больше этих данных находится в этом файле. Поэтому я хочу захватить все в этом файле и записать их в другой файл.
Ами
Вы, вероятно, можете обернуть эту команду в какой-то оператор if или даже в switch-case, который может вызвать функцию, которая может
отследить

Ответы:

10

Если я правильно понимаю вашу цель, следующее будет делать то, что вы хотите:

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

Это будет искать все *.txtфайлы в ./recup*/, проверить каждый из них searchPattern, если он совпадает с этим будет catфайл. Вывод всех catфайлов ed будет направлен в 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.

Патрик
источник
Да, я думаю, что это сделает. И это дает мне основу для работы. Так как я собираюсь искать несколько строк, я думаю, что фрагмент кода for / if с несколькими elif'ами поможет мне автоматизировать задачу. Спасибо
Ами
Это даже лучше, чем я думал LOL
ryekayo
Это не похоже на работу. Получил эту ошибку: «невозможно выполнить / 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")

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

find ./recup*/ -name '*.txt' -print0 | xargs -0 grep -Zli "searchPattern" | xargs -0 cat
dannysauer
источник
2
Мне также нравится опция Patrick "two -exec", за исключением того, что она вызовет новый fork (ну, clone ()) и exec для каждого файла. Обычно вы можете использовать, \+а не \;избегать этой проблемы, но я не знаю, как это работает с парой аргументов -exec (подозреваю, «плохо»). Используя пару xargs, у вас будет только пара новых процессов, которые должны быть быстрее с большим количеством файлов.
dannysauer
Это тоже хорошо выглядит. Спасибо. Один нубский вопрос: кот после последнего xargs должен выводить в файл, верно?
Ами
Когда я впервые прочитал его, я не думал, что в вопросе указано, куда должно идти содержимое файла. Все три из этих команд поместить содержимое файла (ов) на STDOUT, так что вы бы просто добавить (до самого конца) >afileили |acommandили то , что подходит для вашей ситуации. :)
dannysauer
Хороший ответ, мне нужно кота pg_hba.conf sudo find /* -name pg_hba.conf | xargs sudo cat
App Work
Это немного не по теме, но я предпочитаю использовать sudo xargsвместо xargs sudo. Когда вы запускаете xargs sudo, он строит командную строку, предполагая, что команда sudo cat args. Но cat находится в / bin, поэтому sudo запускается /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

В отличие от других ответов, здесь есть два шага и временный файл, поэтому я действительно рекомендую его, только если у вас есть несколько шаблонов для поиска.

Sparhawk
источник
0

В зависимости от вашей оболочки и среды вы можете сделать что-то вроде этого (в 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)
steeldriver
источник
Что делает бит после «готово»? Что мне действительно нравится, так это изменить этот блок if так, чтобы файлы, содержащие сопоставленный шаблон, записывались в другое.
Ами
Он просто перечисляет найденные файлы '.txt', каждый из которых заканчивается нулевым символом (так что это безопасно для имен файлов, содержащих пробелы и другие символы). Затем whileцикл читает этот список и выполняет grep/ условную catчасть.
Steeldriver
Когда я пытаюсь запустить код, я получаю эту ошибку: ./recoverData.sh: Синтаксическая ошибка: "(" неожиданно. Это исходит из скобок вокруг команды поиска
Ami
Какую оболочку вы используете? синтаксис замещения процесса специфичен для bash - отсюда и моя квалификация «В зависимости от вашей оболочки и среды»
steeldriver
1
Вы можете либо выполнить команду (и) непосредственно в интерактивной оболочке bash, либо поместить их в файл, в первой строке которого содержится шебанг #!/bin/bash, сделать его исполняемым chmod +x recoverData.shи выполнить его с помощью ./recoverData.sh. Как не использовать , sh recoverData.shтак как /bin/sh, скорее всего, dashоболочка .
стальная отвертка