Оболочка одного вкладыша для добавления к файлу
Вопрос
Это, вероятно, сложное решение.
Я ищу простой оператор типа «>>», но для добавления в начало.
Боюсь, его не существует.мне придется сделать что-то вроде
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
.Это нетривиально и, очевидно, не может быть сделано человеком, не являющимся суперпользователем, и, вероятно, не суперпользователем тоже...Поиграйтесь с инодами и т.д.
Однако вы можете более или менее имитировать это, используя петлевые устройства.См. например эта ТАК-тема.