Есть несколько вопросов.
>>
Ваша первая команда будет интерпретироваться вашей текущей оболочкой как перенаправление в файл с буквальным именем {}
, если он не заключен в кавычки.
*.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
без параметров, и полученная строка является статической, так что все должно быть в порядке.