Добавление новых строк в несколько файлов
Я пытаюсь добавить новые строки в несколько файлов с помощью следующей команды:
find -name *.ovpn -exec sh echo "line to append" >> {} \;
перед этим я запустил другую команду, чтобы убедиться, что она будет работать так, как я ожидал:
find -name *.ovpn -exec sh echo "hello" \;
но все, что это делает, это распечатывает "sh: 0: Can't open echo" для каждого найденного файла.
2 ответа
Есть несколько вопросов.
>>
в вашей первой команде ваша текущая оболочка будет интерпретироваться как перенаправление в файл с буквальным именем {}
, если это не указано.
*.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
без параметров и получаемой строки является статической, так что все должно быть в порядке.
Мой скрипт для сохранения многострочного файла в.csv
файл:
#! /bin/bash
while [ 1 ]
do
sleep 1s
str="\""$(date)"\""
echo ${str} >>333.csv
# begin add next row
echo ",\"" >>333.csv
# append multiline result
ps >>333.csv
# end add next row
echo "\"" >>333.csv
# begin add next row
echo ",\"" >>333.csv
# append multiline result
df -h >>333.csv
# end add next row
echo "\"" >>333.csv
sed -i ":a;N;s/\"\n\,/\"\,/g;ta" ./333.csv
done