Использование zcat и sed в find -exec

Мне нужно создать большой текстовый файл, используя содержимое нескольких gzip-файлов с определенным шаблоном имени. Для этого я использовал:

find . -name '*dna.toplevel.txt.gz' -exec zcat {} >> all.txt \;

и это сработало просто отлично. Проблема в том, что теперь мне нужно редактировать текст на лету, чтобы заменить определенный символ ">" на ">имя файла|". Мне удалось приготовить это:

find . -name '*dna.toplevel.txt.gz' -exec zcat {} | sed 's/>/>{}|/g' >> all.txt \;

Но я получаю следующие ошибки:

  • sed: не могу прочитать ;: Нет такого файла или каталога
  • find: отсутствует аргумент для `-exec'

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


person user3537026    schedule 07.07.2016    source источник


Ответы (2)


-exec принимает простую команду и ее аргументы; он вообще не обрабатывает конструкции оболочки, такие как каналы или перенаправления. Ваша исходная команда идентична

find . -name '*dna.toplevel.txt.gz' -exec zcat {} \; >> all.txt

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

Поскольку sed требует имя файла из find как часть своей команды, вам нужно будет запустить оболочку, которая принимает конвейер в качестве аргумента через параметр -c.

find . -name '*dna.toplevel.txt.gz' -exec \
  sh -c "zcat {} | sed 's/>/>{}|/g'" \; >> all.txt

Есть несколько проблем с этим подходом; их исправление требует немного более сложной команды sh. Если вы используете bash 4 или более позднюю версию, я бы рекомендовал вообще отказаться от find и использовать цикл оболочки вместе с glob **:

shopt -s globstar
for f in ./**/*dna.toplevel.txt.gz; do
    zcat "$f" | sed "s|>|>$f|g"
done >> all.txt

Если эта команда создает all.txt, вы можете просто использовать > вместо >>. Это также предполагает, что $f не будет содержать | символов; если это так, вам нужно будет выбрать другой разделитель.

person chepner    schedule 07.07.2016
comment
Спасибо, думаю, воспользуюсь этим подходом. - person user3537026; 08.07.2016

Попробуйте заключить аргумент -exec в кавычки (").

find . -name '*dna.toplevel.txt.gz' -exec "zcat {} | sed 's/>/>{}|/g'" >> all.txt \;

Вам нужно выйти из конвейера:

find . -name '*dna.toplevel.txt.gz' -exec zcat {} \| sed 's/>/>{}|/g' >> all.txt \;
person randomdude999    schedule 07.07.2016
comment
Спасибо, к сожалению, это вызвало следующие ошибки: bash: /: Is a directory find: missing argument to `-exec' - person user3537026; 07.07.2016