Есть несколько вопросов.
>>
ваша первая команда будет интерпретироваться вашей текущей оболочкой как перенаправление в файл с буквальным именем {}
, если он не заключен в кавычки.
*.ovpn
может быть расширен с помощью оболочки, прежде чем find
когда-либо работает. Это произойдет, если у вас есть хотя бы один объект в текущем каталоге, который соответствует шаблону. Вы хотите процитировать это. Сравните этот вопрос .
Вы получаете, Can't open echo
потому что на самом деле вы говорите, sh
чтобы открыть echo
. Для выполнения команды вам нужно sh -c
.
find
без указания пути не является переносимым (сравните этот вопрос ). Хотя вам это может сойти с рук, я упомяну эту проблему, чтобы сделать ответ более полезным для других пользователей.
Это улучшенная версия вашей первой команды, которая вроде работает (не запускайте ее, продолжайте читать):
find . -name '*.ovpn' -exec sh -c 'echo "line to append" >> "{}"' \;
Обратите внимание, я должен был заключить двойные кавычки {}
в одинарные кавычки Эти двойные кавычки "видны" sh
и заставляют имена файлов с пробелами и т. Д. Работать как цели перенаправления. Без кавычек вы можете в конечном итоге с подобным, echo "line to append" >> foo bar.ovpn
что эквивалентно echo "line to append" bar.ovpn >> foo
. Цитирование делает это echo "line to append" >> "foo bar.ovpn"
вместо этого.
К сожалению, содержащие имена файлов "
нарушат этот синтаксис.
Правильный путь, чтобы пройти {}
к sh
не включить его в командной строке, но передать его содержание как отдельный аргумент:
find . -name '*.ovpn' -exec sh -c 'echo "line to append" >> "$0"' {} \;
$0
внутри командной строки раскрывается первый аргумент, который мы sh
получаем после -c '…'
. Теперь даже "
в имени файла не будет нарушен синтаксис.
Обычно (как в скрипте) ссылается на первый аргумент, который вы используете $1
. По этой причине некоторые пользователи предпочитают использовать фиктивный аргумент $0
, например:
find . -name '*.ovpn' -exec sh -c 'echo "line to append" >> "$1"' dummy {} \;
Если бы это был сценарий, $0
расширился бы до его имени. Вот почему нередко видеть, что это на dummy
самом деле sh
(или bash
, если кто-то звонит bash -c …
и т. Д.):
find . -name '*.ovpn' -exec sh -c 'echo "line to append" >> "$1"' sh {} \;
Но ждать! find
вызывает отдельный sh
для каждого файла. Я не ожидаю, что у вас будет тысячи .ovpn
файлов, но в целом вы можете обрабатывать много файлов, не вызывая ненужных процессов. Мы можем оптимизировать подход с тем, tee -a
что можно записать несколько файлов как один процесс:
find . -name '*.ovpn' -exec sh -c 'echo "line to append" | tee -a "$@" >/dev/null' sh {} +
Обратите внимание {} +
, это проходит несколько путей одновременно. Внутри команды, которую sh -c
мы выполняем, мы получаем их с помощью "$@"
, которая расширяется до "$1" "$2" "$3" …
. В этом случае необязательный аргумент, который заполняет (не используется) $0
является обязательным.
Вообще есть и такая проблема: почему printf
лучше чем echo
? Однако в этом случае вы используете echo
без параметров и полученная строка является статической, так что все должно быть в порядке.