Добавление новых строк в несколько файлов

382
Andrew Davis

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

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

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

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

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

1

1 ответ на вопрос

2
Kamil Maciorowski

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

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

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

Похожие вопросы