1.grep | xargs mv

1.grep | xargs mv

У меня есть несколько таких файлов в одном каталоге

00.01.02 0000 some filename 1.pdf
00.01.02 0000 some filename 1.txt
02.03.07 0163 another filename 2.pdf
02.03.07 0163 another filename 2.txt

и мне нужно найти все txt-файлы, содержащие определенную строку, а затем переместить этот txt-файл и соответствующий ему pdf-файл в другой каталог.

Как лучше всего это сделать?

решение1

У меня для вас есть 3 варианта. Основные идеи таковы:

  1. grep | xargs mv
  2. grep | parallel mv
  3. find -exec grep -q -exec mv

1.grep | xargs mv

Используйте grepи xargs:

grep -FlZ "some string" *.txt \
| xargs -0 -I{} sh -c 'echo mv "$1" "${1%.*}.pdf" some/other/directory' xargs-sh {}

grep:

  • -Fсоответствует полной строке, а не шаблону
  • -lвыводить только соответствующие имена файлов
  • -Zвывод нулевого разделителя между именами файлов (это важно, поскольку имена файлов могут иметь переносы строк, поэтому нельзя полагаться на перенос строки в качестве разделителя)

xargs:

  • -0считывает данные, разделенные нулем

2.grep | parallel mv

С помощью xargsвам нужно вызвать подоболочку, sh -cчтобы получить соответствующее имя файла PDF.

Вместо этого вы также можете воспользоваться GNU parallelкрутыми функциями!!

grep -FlZ "some string" *.txt \
| parallel -0 -j1 echo mv {} {.}.pdf some/other/directory
  • -j1только одна работа за раз
  • {}имя файла
  • {.}имя файла без расширения

3.find -exec grep -q -exec mv

И еще один вариант, с использованием find:

find . -maxdepth 1 -name "*.txt" \
-exec grep -Fq "some string" {} \; \
-exec sh -c 'echo mv "$1" "${1%.*}.pdf" some/other/location' find-sh {} \;
  • -maxdepth 1не рекурсировать
  • grep -qчтобы получить только код ошибки вместо имен файлов, второй вариант -execбудет запущен только в случае успешного выполнения первого варианта.
  • Второй вариант -execпо сути такой же, как и xargsпервый.
  • нам не нужно делать никаких действий с выводом, разделенным нулем, поскольку мы не передаем никаких имен файлов, findоб этом позаботится сам.

Для каждого варианта удалите значок , echoесли вы удовлетворены результатом.

решение2

Разбейте проблему на части.

«Мне нужно найти все текстовые файлы, содержащие заданную строку в текущем каталоге».

grep -F "text string just as written (no regex)" *.txt

"Я хочуимена файловиз этих файлов"

grep -l -F "text string" *.txt

«Для каждого из этих файлов...»

for...each вы делаете с помощью while/do/doneконструкции, в которую вы передадите список файлов. Для каждого файла вы можете запуститьчто-нибудь, который будет иметь доступ к $fileпеременной:

grep -l -F "text string" *.txt \
| while IFS= read -r file ; do
...something...
done

«Что-то» — это

«...переместите файл txt и соответствующий файл pdf...»

Поэтому вам нужно базовое имя, без «.txt»

BASE=$( basename ${file} .txt )

И наконец, собираем все воедино:

grep -l -F "text string" *.txt \
| while IFS= read -r file ; do \
    BASE=$( basename ${file} .txt )
    mv ${BASE}.txt /some/other/dir
    mv ${BASE}.pdf /some/other/dir
done

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

решение3

Если у вас нет parallelкоманды, сделайте с двумя, xargsкак показано ниже

grep -l 'search string' *.txt | xargs -I {} basename {} .txt | xargs -I {} cp {}.pdf /destination-directory

Связанный контент