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

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

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
Другие вопросы по тегам