Перенаправление stdout с помощью find -exec и без создания новой оболочки
У меня есть один скрипт, который только записывает данные в stdout
. Мне нужно запустить его для нескольких файлов и создать другой выходной файл для каждого входного файла, и мне было интересно, как использовать для этого find -exec
. Поэтому я в основном пробовал несколько вариантов этого (я заменил скрипт cat
только для целей проверки):
find * -type f -exec cat "{}" > "{}.stdout" \;
но не смог заставить его работать, поскольку все данные записывались в файл, буквально названный {}.stdout
.
В конце концов, я мог бы работать с:
find * -type f -exec sh -c "cat {} > {}.stdout" \;
Но в то время как эта последняя форма хорошо работает с cat
, мой сценарий требует, чтобы переменные среды загружались через несколько сценариев инициализации, таким образом, я получаю:
find * -type f -exec sh -c "initscript1; initscript2; ...; myscript {} > {}.stdout" \;
Что кажется пустой тратой, потому что у меня есть все, что уже было инициализировано в моей текущей оболочке.
Есть ли лучший способ сделать это с find
? Другие однострочные линии приветствуются.
Ответы
Ответ 1
Простым решением было бы разместить обертку вокруг вашего script:
#!/bin/sh
myscript "$1" > "$1.stdout"
Назовите его myscript2
и вызовите его с помощью find:
find . -type f -exec myscript2 {} \;
Обратите внимание, что, хотя большинство реализаций find позволяют делать то, что вы сделали, технически поведение find не указано, если вы используете {}
более одного раза в списке аргументов -exec
.
Ответ 2
Вы можете сделать это с помощью eval. Это может быть уродливо, но для этого нужно сделать оболочку script. Кроме того, все это на одной линии.
Например
find -type f -exec bash -c "eval md5sum {} > {}.sum " \;
Ответ 3
Если вы экспортируете переменные среды, они уже будут присутствовать в дочерней оболочке (если вы используете bash -c
вместо sh -c
, а ваша родительская оболочка сама является bash, то вы также можете экспортировать функции в родительскую оболочку и использовать их в дочернем элементе, см. export -f
).
Кроме того, используя -exec ... {} +
, вы можете ограничить количество оболочек до наименьшего возможного числа, необходимого для передачи всех аргументов в командной строке:
set -a # turn on automatic export of all variables
source initscript1
source initscript2
# pass as many filenames as possible to each sh -c, iterating over them directly
find * -name '*.stdout' -prune -o -type f \
-exec sh -c 'for arg; do myscript "$arg" > "${arg}.stdout"' _ {} +
В качестве альтернативы вы можете просто выполнить выполнение в текущей оболочке напрямую:
while IFS= read -r -d '' filename; do
myscript "$filename" >"${filename}.out"
done < <(find * -name '*.stdout' -prune -o -type f -print0)
См. UsingFind обсуждение безопасного и правильного выполнения массовых действий через find
; и BashFAQ # 24 обсуждать использование подстановки процесса (синтаксис <(...)
), чтобы гарантировать, что операции выполняются в родительской оболочке.