У меня есть несколько таких файлов в одном каталоге
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 варианта. Основные идеи таковы:
grep | xargs mv
grep | parallel mv
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