1

Я пытаюсь добавить новые строки в несколько файлов с помощью следующей команды:

find -name *.ovpn -exec sh echo "line to append" >> {} \;

перед этим я запустил другую команду, чтобы убедиться, что она будет работать так, как я ожидал:

find -name *.ovpn -exec sh echo "hello" \;

но все, что это делает, это распечатывает "sh: 0: Can't open echo" для каждого найденного файла.

1 ответ1

3

Есть несколько вопросов.

>> Ваша первая команда будет интерпретироваться вашей текущей оболочкой как перенаправление в файл с буквальным именем {} , если он не заключен в кавычки.

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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .