find -exec с несколькими командами
Описание проблемы:
Я пытаюсь использовать команду find
с опцией -exec
для выполнения нескольких команд, но пока не могу добиться успешного выполнения. Могу ли я использовать такие команды, как приведенная ниже?
find *.txt -exec echo "$(tail -1 '{}'),$(ls '{}')" \;
В общем, я хочу вывести последнюю строку каждого текстового файла в текущем каталоге и добавить в конец этой строки запятую, за которой следует имя файла. Кто-нибудь знает, как это правильно сделать?
5 ответ(ов)
Команда find
принимает несколько частей -exec
для выполнения указанной команды. Например:
find . -name "*.txt" -exec echo {} \; -exec grep banana {} \;
Обратите внимание, что во втором случае вторая команда будет выполнена только в том случае, если первая завершится успешно, как упомянул @Caleb. Если вы хотите, чтобы обе команды выполнялись независимо от их успешности или ошибки, вы можете использовать следующую конструкцию:
find . -name "*.txt" \( -exec echo {} \; -o -exec true \; \) -exec grep banana {} \;
Таким образом, команда grep
будет выполнена для каждого найденного файла, независимо от результата выполнения echo
.
Команда, которую вы привели, выполняет поиск всех каталогов в текущем и подкаталогах, затем для каждого найденного каталога выводит его имя дважды, разделенное строкой " x ". Давайте разберем ее по частям:
find .
— ищет в текущем каталоге (.
) и всех вложенных.-type d
— указывает, что нужно искать только каталоги.-exec sh -c "..." \;
— для каждого найденного каталога выполняет команду, указанную в кавычках.
Команда внутри sh -c
делает следующее:
echo -n {}
— выводит имя каталога без добавления новой строки.echo -n ' x '
— выводит строку " x " без перехода на новую строку.echo {}
— затем снова выводит имя каталога, но уже с переходом на новую строку.
Таким образом, результатом выполнения будет строка вида:
имя_каталога x имя_каталога
Если у вас возникли дополнительные вопросы касаемо данной команды или поведения find
, не стесняйтесь задавать!
Каждый из предложенных вариантов команд выполняет схожую задачу — извлечение последней строки из файлов с расширением .txt
и вывод её в формате строка,имя_файла
. Давайте рассмотрим каждую команду подробнее:
Первая команда:
find *.txt -exec awk 'END {print $0 "," FILENAME}' {} \;
Эта команда использует
awk
для выбора последней строки каждого найденного текстового файла. Она выводит последнюю строку и имя файла, разделенные запятой. Однако данная команда не сработает, посколькуfind
не обрабатывает шаблон*.txt
корректно. Лучше использовать:find . -name "*.txt" -exec awk 'END {print $0 "," FILENAME}' {} \;
Вторая команда:
find *.txt -exec sh -c 'echo "$(tail -n 1 "$1"),$1"' _ {} \;
Этот вариант использует
tail
для получения последней строки из каждого файла. Он также правильно выводит строку с именем файла, разделенных запятой. Обратите внимание, что та же проблема сfind
(использование*.txt
) сохраняется. Следует заменить её на:find . -name "*.txt" -exec sh -c 'echo "$(tail -n 1 "$1"),$1"' _ {} \;
Третья команда:
find *.txt -exec sh -c 'echo "$(sed -n "\$p" "$1"),$1"' _ {} \;
Эта команда использует
sed
для извлечения последней строки файла. Как и в предыдущих случаях, необходимо изменить использованиеfind
. Например:find . -name "*.txt" -exec sh -c 'echo "$(sed -n "\$p" "$1"),$1"' _ {} \;
Каждый из этих вариантов имеет свои преимущества, и выбор можно сделать в зависимости от предпочтений к утилите (awk, tail или sed). Убедитесь, что используете правильный синтаксис для find
, чтобы избежать ошибок в поиске файлов.
Ещё один способ сделать это выглядит следующим образом:
multiple_cmd() {
tail -n1 "$1"
ls "$1"
}
export -f multiple_cmd
find *.txt -exec bash -c 'multiple_cmd "$0"' {} \;
В одну строку это можно записать так:
multiple_cmd() { tail -1 "$1"; ls "$1"; }; export -f multiple_cmd; find *.txt -exec bash -c 'multiple_cmd "$0"' {} \;
multiple_cmd()
— это функция.export -f multiple_cmd
— выполняет экспорт функции, чтобы другая подсистема могла ее видеть.find *.txt -exec bash -c 'multiple_cmd "$0"' {} \;
— команда find, которая будет выполнять функцию для ваших файлов.
Таким образом, функция multiple_cmd
может быть такой длинной и сложной, как вам нужно.
Есть более простой способ:
find ... | while read -r file; do
echo "посмотрите на мой $file, мой $file потрясающий";
done
Либо можно использовать:
while read -r file; do
echo "посмотрите на мой $file, мой $file потрясающий";
done <<< "$(find ...)"
Таким образом, вы сможете обрабатывать файлы, найденные командой find
, более компактным и элегантным способом.
Как исключить все сообщения "permission denied" из команды "find"?
Извлечение имени файла и расширения в Bash
Как сделать паузу в shell-скрипте на одну секунду перед продолжением?
Как работает "cat << EOF" в bash?
Создание переменной временной метки в bash-скрипте