Оболочка одного вкладыша для добавления к файлу

StackOverflow https://stackoverflow.com/questions/54365

  •  09-06-2019
  •  | 
  •  

Вопрос

Это, вероятно, сложное решение.

Я ищу простой оператор типа «>>», но для добавления в начало.

Боюсь, его не существует.мне придется сделать что-то вроде

 mv myfile tmp
 cat myheader tmp > myfile

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

Это было полезно?

Решение

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

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

Теперь, несмотря на все это, ответ был:


Создание другого файлового дескриптора для файла (exec 3<> yourfile) затем написав об этом (>&3), кажется, преодолевает дилемму чтения/записи одного и того же файла.У меня работает с файлами размером 600 КБ с помощью awk.Однако попытка того же трюка с использованием «кошки» не удалась.

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

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

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

Другие советы

Здесь по-прежнему используется временный файл, но, по крайней мере, он находится в одной строке:

echo "text" | cat - yourfile > /tmp/out && mv /tmp/out yourfile

Кредит: БАШ:Добавить текст/строки в начало файла

echo '0a
your text here
.
w' | ed some_file

ed — стандартный редактор! http://www.gnu.org/fun/jokes/ed.msg.html

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

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

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

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

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

Без временного файла это невозможно, но здесь идет строка

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

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

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

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

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

cat header myfile | sponge myfile

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

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

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

предполагая, что файл, который вы хотите редактировать, — это 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».

Когда вы начнете пытаться делать что-то, что становится трудным в сценарии оболочки, я настоятельно рекомендую переписать сценарий на «правильном» языке сценариев (Python/Perl/Ruby/etc).

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

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

РЕДАКТИРОВАТЬ:Сломано.Видеть Странное поведение при добавлении к файлу слова «кошка и тройник»

Обходной путь решения проблемы перезаписи заключается в использовании tee:

cat header main | tee main > /dev/null

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

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

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

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

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

В основном для развлечения/гольфа, но

ex -c '0r myheader|x' myfile

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

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

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

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

Вариант решения cb0 для «нет временного файла» для добавления фиксированного текста:

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

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

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

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

echo -e "header \n$(cat file)" >file
sed -i -e '1rmyheader' -e '1{h;d}' -e '2{x;G}' myfile

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

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

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

завершается сбоем из-за необработанных символов новой строки в заменяющем сценарии, перед которым необходимо поставить символ продолжения строки () и, возможно, за ним следует знак &, чтобы оболочка и sed оставались работоспособными, например это ТАК ответ

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

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

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

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

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

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

Решение с 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}"

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

Вы можете использовать командную строку 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 позволит оценить код при замене.

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

где «my_file» — это файл, к которому нужно добавить «my_string».

мне нравится @fluffle's Эд подход лучшее.В конце концов, переключатели командной строки любого инструмента и команды скриптового редактора здесь, по сути, одно и то же;не вижу, чтобы «чистота» решения для скриптового редактора была меньшей или что-то в этом роде.

Вот моя строка, добавленная к .git/hooks/prepare-commit-msg добавить в репозиторий .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 также хорошо иметь:

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

переменные, фу?

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

Я думаю, что это самый чистый вариант 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

Если вы пишете скрипты в BASH, вы можете просто ввести:

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

На самом деле это в сложном примере, который вы сами опубликовали в своем вопросе.

ИМХО, нет решения оболочки (и никогда не будет), которое работало бы последовательно и надежно независимо от размеров двух файлов. myheader и myfile.Причина в том, что если вы хотите сделать это, не возвращаясь к временному файлу (и не позволяя оболочке автоматически возвращаться к временному файлу, напримерс помощью таких конструкций, как exec 3<>myfile, трубопровод к tee, и т. д.

«Настоящее» решение, которое вы ищете, требует работы с файловой системой, поэтому оно недоступно в пользовательском пространстве и будет зависеть от платформы:вы просите изменить указатель файловой системы, используемый myfile к текущему значению указателя файловой системы для myheader и замените в файловой системе EOF из myheader с цепочкой ссылок на текущий адрес файловой системы, на который указывает myfile.Это нетривиально и, очевидно, не может быть сделано человеком, не являющимся суперпользователем, и, вероятно, не суперпользователем тоже...Поиграйтесь с инодами и т.д.

Однако вы можете более или менее имитировать это, используя петлевые устройства.См. например эта ТАК-тема.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top