Shell one liner для добавления к файлу

Возможно, это комплексное решение.

Я ищу простой оператор типа " → ", но для добавления.

Я боюсь, что этого не существует. Мне нужно сделать что-то вроде

 mv myfile tmp
 cat myheader tmp > myfile

Что-нибудь умнее?

Ответ 1

hack ниже был быстрый ответ от манжеты, который работал и получил много upvotes. Затем, когда вопрос стал более популярным, и больше времени прошло, возмущенные люди начали сообщать, что он сорта работал, но странные вещи могут произойти, или это просто не сработало, так что это было яростно приостановлено на время. Такая забава.

Решение использует точную реализацию файловых дескрипторов в вашей системе и, поскольку реализация существенно различается между nixes, успех полностью зависящий от системы, окончательно не переносимый, и на него нельзя полагаться ни на что, даже смутно важное.

Теперь со всем, что было в ответе:


Создание другого файлового дескриптора для файла (exec 3<> yourfile), поэтому запись на него (>&3), похоже, преодолевает проблему чтения/записи в одном и том же файле. Работает для меня на 600K файлах с awk. Однако попытка использовать тот же трюк с использованием "cat" не удалась.

Передача preendage как переменной в awk (-v TEXT="$text") преодолевает литеральную проблему с кавычками, которая предотвращает выполнение этого трюка с помощью команды sed.

#!/bin/bash
text="Hello world
What up?"

exec 3<> yourfile && awk -v TEXT="$text" 'BEGIN {print TEXT}{print}' yourfile >&3

Ответ 4

John Mee: ваш метод не гарантированно работает, и, вероятно, он потерпит неудачу, если вы добавите более 4096 байт материала (по крайней мере, то, что происходит с gnu awk, но я полагаю, что другие реализации будут иметь схожие ограничения). В этом случае он не только потерпит неудачу, но и войдет в бесконечный цикл, где он будет читать свой собственный вывод, тем самым увеличив файл до тех пор, пока не будет заполнено все свободное пространство.

Попробуйте сами:

exec 3<>myfile && awk 'BEGIN{for(i=1;i<=1100;i++)print i}{print}' myfile >&3

(предупреждение: убить его через некоторое время или заполнить файловую систему)

Кроме того, очень опасно редактировать файлы таким образом, и это очень плохой совет, как будто что-то происходит, когда файл редактируется (сбой, полный диск), вы почти гарантированно остаетесь с файлом в непоследовательной состояние.

Ответ 5

Возможно, стоит отметить, что часто это хорошая идея для безопасного создания временного файла с помощью утилиты, такой как mktemp, по крайней мере, если script будет когда-либо выполняются с привилегиями root. Например, вы можете сделать следующее (снова в bash):

(tmpfile=`mktemp` && { echo "prepended text" | cat - yourfile > $tmpfile && mv $tmpfile yourfile; } )

Ответ 6

Невозможно без временного файла, но здесь идет oneliner

{ echo foo; cat oldfile; } > newfile && mv newfile oldfile

Вы можете использовать другие инструменты, такие как ed или perl, чтобы сделать это без временных файлов.

Ответ 7

Если вам это нужно на компьютерах, которыми вы управляете, установите пакет "moreutils" и используйте "губку". Затем вы можете сделать:

cat header myfile | sponge myfile

Ответ 8

Когда вы начинаете пытаться делать вещи, которые становятся трудными в shell- script, я настоятельно рекомендую изучить переписывание script на "правильном" языке сценариев (Python/Perl/Ruby/etc)

Что касается добавления строки к файлу, это невозможно сделать с помощью конвейера, так как когда вы делаете что-либо вроде cat blah.txt | grep something > blah.txt, он непреднамеренно заполняет файл. Существует небольшая команда утилиты sponge, которую вы можете установить (вы делаете cat blah.txt | grep something | sponge blah.txt и буферизуете содержимое файла, а затем записываете его в файл). Он похож на временный файл, но вам не нужно делать это явно. но я бы сказал, что это "худшее" требование, чем, скажем, Perl.

Возможно, есть способ сделать это с помощью awk или аналогичного, но если вам нужно использовать shell- script, я думаю, что временный файл является самым простым (/only?) способом.

Ответ 10

Предполагая, что файл, который вы хотите изменить, - my.txt

$cat my.txt    
this is the regular file

И файл, который вы хотите добавить, - это заголовок

$ cat header
this is the header

Обязательно введите конечную пустую строку в файле заголовка.
Теперь вы можете добавить его с помощью

$cat header <(cat my.txt) > my.txt

В итоге вы получите

$ cat my.txt
this is the header
this is the regular file

Насколько я знаю, это работает только в bash.

Ответ 11

Как предлагает Даниэль Велков, используйте тройник.
Для меня это простое интеллектуальное решение:

{ echo foo; cat bar; } | tee bar > /dev/null

Ответ 12

Используя bash heredoc, вы можете избежать необходимости в файле tmp:

cat <<-EOF > myfile
  $(echo this is prepended)
  $(cat myfile)
EOF

Это работает, потому что $(cat myfile) оценивается, когда оценивается bash script, прежде чем выполняется кошка с перенаправлением.

Ответ 13

Тот, который я использую. Это позволяет указать порядок, дополнительные символы и т.д. Так, как вам нравится:

echo -e "TEXTFIRSt\n$(< header)\n$(< my.txt)" > my.txt

P.S: только он не работает, если файлы содержат текст с обратным слэшем, потому что он интерпретируется как escape-символы

Ответ 14

В основном для гольфа с потерей/раковиной, но

ex -c '0r myheader|x' myfile

сделает трюк, и нет конвейеров или перенаправления. Конечно, vi/ex не предназначен для неинтерактивного использования, поэтому vi будет кратковременно мигать.

Ответ 15

Вариант решения cb0 для "no temp file" для добавления фиксированного текста:

echo "text to prepend" | cat - file_to_be_modified | ( cat > file_to_be_modified ) 

Опять же, это зависит от выполнения суб-оболочки - (..) - чтобы кошка отказалась иметь один и тот же файл для ввода и вывода.

Примечание: понравилось это решение. Однако на моем Mac исходный файл потерян (подумал, что он не должен, но он это делает). Это можно исправить, написав свое решение как: echo "text to prepend" | cat - file_to_be_modified | cat > tmp_file; mv tmp_file file_to_be_modified

Ответ 16

ПРЕДУПРЕЖДЕНИЕ: для этого требуется немного больше работы для удовлетворения потребностей OP.

Должен быть способ сделать подход sed by @shixilun работать, несмотря на его опасения. Для чтения пробела при чтении файла в строку замены sed должна быть команда bash (например, заменить символы новой строки на "\n". Команды оболочки vis и cat могут обрабатывать непечатаемые символы, но не пробелы, поэтому это не решит проблему OP:

sed -i -e "1s/^/$(cat file_with_header.txt)/" file_to_be_prepended.txt

не удается из-за исходных строк в замене script, которые должны быть добавлены с символом продолжения строки() и, возможно, сопровождаться символом &, чтобы поддерживать оболочку и sed счастливыми, например этот ответ SO

sed имеет ограничение по размеру 40K для неглобальных команд замены на поиск (без трейлинга /g после шаблона), поэтому вероятно избежит проблем с переполнением страшного буфера awk, о котором предупреждали анонимные пользователи.

Ответ 17

Почему бы просто не использовать команду ed (как уже было предложено пудрой здесь)?

ed считывает весь файл в память и автоматически выполняет редактирование файла на месте!

Итак, если ваш файл не такой огромный...

# cf. "Editing files with the ed text editor from scripts.",
# http://wiki.bash-hackers.org/doku.php?id=howto:edit-ed

prepend() {
   printf '%s\n' H 1i "${1}" . wq | ed -s "${2}"
}

echo 'Hello, world!' > myfile
prepend 'line to prepend' myfile

Еще одним обходным решением будет использование открытых файловых дескрипторов, предложенных Юргеном Хётцелем в Перенаправить вывод из файла sed 's/c/d/' myFile в myFile

echo cat > manipulate.txt
exec 3<manipulate.txt
# Prevent open file from being truncated:
rm manipulate.txt
sed 's/cat/dog/' <&3 > manipulate.txt

Все это можно было бы поместить в одну строку, конечно.

Ответ 18

sed -i -e '1rmyheader' -e '1{h;d}' -e '2{x;G}' myfile

Ответ 19

С помощью $(команда) вы можете записать вывод команды в переменную. Поэтому я сделал это в трех командах в одной строке и без временного файла.

originalContent=$(cat targetfile) && echo "text to prepend" > targetfile && echo "$originalContent" >> targetfile

Ответ 20

Если у вас есть большой файл (несколько сотен килобайт в моем случае) и доступ к python, это намного быстрее, чем cat решения для труб:

python -c 'f = "filename"; t = open(f).read(); open(f, "w").write("text to prepend " + t)'

Ответ 21

Решение с printf:

new_line='the line you want to add'
target_file='/file you/want to/write to'

printf "%s\n$(cat ${target_file})" "${new_line}" > "${target_file}"

Вы также можете сделать:

printf "${new_line}\n$(cat ${target_file})" > "${target_file}"

Но в этом случае вы должны быть уверены, что в любом месте % где-нибудь, включая содержимое целевого файла, не будет, поскольку это можно интерпретировать и испортить ваши результаты.

Ответ 22

Вы можете использовать командную строку perl:

perl -i -0777 -pe 's/^/my_header/' tmp

Где -i создаст встроенную замену файла и -0777 удалит весь файл и сделает ^ совпадающим только с началом. -pe будет печатать все строки

Или, если my_header - это файл:

perl -i -0777 -pe 's/^/`cat my_header`/e' tmp

Где/e разрешит оценку кода в подстановке.

Ответ 23

Если вы создаете сценарий в BASH, на самом деле, вы можете просто выпустить:

cat - yourfile  /tmp/out && mv /tmp/out yourfile

Что на самом деле в сложном примере вы сами разместили в своем собственном вопросе.

Ответ 24

Ба! Никто не хотел упоминать о tac.

[email protected] ~ $ tac --help
Usage: tac [OPTION]... [FILE]...
Write each FILE to standard output, last line first.
With no FILE, or when FILE is -, read standard input.

Mandatory arguments to long options are mandatory for short options too.
  -b, --before             attach the separator before instead of after
  -r, --regex              interpret the separator as a regular expression
  -s, --separator=STRING   use STRING as the separator instead of newline
      --help     display this help and exit
      --version  output version information and exit

Report tac bugs to [email protected]
GNU coreutils home page: <http://www.gnu.org/software/coreutils/>
General help using GNU software: <http://www.gnu.org/gethelp/>
Report tac translation bugs to <http://translationproject.org/team/>

Ответ 25

current=`cat my_file` && echo 'my_string' > my_file && echo $current >> my_file

где "my_file" - это файл, который добавляет "my_string" к.

Ответ 26

sed -i -e "1s/^/new first line\n/" old_file.txt

Ответ 27

Мне нравится @fluffle ed подход. В конце концов, любые команды командной строки переключаются на скриптовые команды редактора, по сути, здесь одно и то же; не видя, что сценаристское решение редактора "чистота" меньше или меньше.

Здесь мой однострочный файл добавлен к .git/hooks/prepare-commit-msg для добавления файла in-repo .gitmessage для фиксации сообщений:

echo -e "1r $PWD/.gitmessage\n.\nw" | ed -s "$1"

Пример .gitmessage:

# Commit message formatting samples:
#       runlevels: boot +consolekit -zfs-fuse
#

Я делаю 1r вместо 0r, потому что это оставит пустую строку с готовой записью поверх файла из исходного шаблона. Не помещайте пустую строку поверх вашего .gitmessage, тогда вы получите две пустые строки. -s подавляет вывод диагностической информации ed.

В связи с этим я обнаружил, что для vim-buffs также хорошо иметь:

[core]
        editor = vim -c ':normal gg'

Ответ 28

ftw?

NEWFILE=$(echo deb http://mirror.csesoc.unsw.edu.au/ubuntu/ $(lsb_release -cs) main universe restricted multiverse && cat /etc/apt/sources.list)
echo "$NEWFILE" | sudo tee /etc/apt/sources.list

Ответ 29

Вот что я обнаружил:

echo -e "header \n$(cat file)" >file

Ответ 30

Я думаю, что это самая чистая вариация ed:

cat myheader | { echo '0a'; cat ; echo -e ".\nw";} | ed myfile

как функция:

function prepend() { { echo '0a'; cat ; echo -e ".\nw";} | ed $1; }

cat myheader | prepend myfile