Наверное, это сложное решение .
Я ищу простой оператор, такой как ">>", но для добавления.
Боюсь, этого не существует. Мне нужно сделать что-то вроде
mv myfile tmp кошка myheader tmp> мой файл
Что-нибудь умнее?
Наверное, это сложное решение .
Я ищу простой оператор, такой как ">>", но для добавления.
Боюсь, этого не существует. Мне нужно сделать что-то вроде
mv myfile tmp кошка myheader tmp> мой файл
Что-нибудь умнее?
Ответы:
Приведенный ниже хакер был быстрым неожиданным ответом, который сработал и получил множество положительных отзывов. Затем, когда вопрос стал более популярным и прошло больше времени, возмущенные люди начали сообщать, что это вроде как работает, но могут происходить странные вещи, или это просто не работает вообще, поэтому какое-то время яростно отвергали его. Так весело.
Решение использует точную реализацию файловых дескрипторов в вашей системе, и, поскольку реализация значительно различается в зависимости от nix, его успех полностью зависит от системы, однозначно непереносим и не следует полагаться ни на что, даже неопределенно важное.
Итак, со всем этим ответом был:
Создание другого файлового дескриптора для file ( exec 3<> yourfile), а затем запись в this ( >&3), похоже, преодолевает дилемму чтения / записи для одного и того же файла. У меня работает с файлами 600K с 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
-после cat?
echo -n "text"
yourfileэто символическая ссылка, это не будет делать то, что вы хотите.
echo '0a
your text here
.
w' | ed some_file
ed - стандартный редактор! http://www.gnu.org/fun/jokes/ed.msg.html
0r header.file
echo -e '0a\nyour text here\n.\nw' | ed some_file
Джон Ми: ваш метод не гарантированно работает, и, вероятно, выйдет из строя, если вы добавите более 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, чтобы сделать это без временных файлов.
Может быть , стоит отметить , что часто является хорошей идеей , чтобы безопасно генерировать временный файл , используя утилиту вроде Mktemp , по крайней мере , если сценарий когда - либо будет выполняться с привилегиями суперпользователя. Например, вы можете сделать следующее (снова в bash):
(tmpfile=`mktemp` && { echo "prepended text" | cat - yourfile > $tmpfile && mv $tmpfile yourfile; } )
Если вам это нужно на компьютерах, которыми вы управляете, установите пакет «moreutils» и используйте «sponge». Тогда вы можете сделать:
cat header myfile | sponge myfile
{ echo "prepended text"; cat 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.
this is the headerв my.txt осталось две строки . Даже после того, как я обновил Bash, 4.3.42(1)-releaseя получил тот же результат.
<(...)) , поэтому нет гарантии, что оно my.txtбудет прочитано полностью заранее , без чего этот метод не будет работать.
Когда вы начинаете пытаться делать вещи, которые становятся сложными в сценарии оболочки, я настоятельно рекомендую переписать сценарий на «правильном» языке сценариев (Python / Perl / Ruby / и т. Д.)
Что касается добавления строки к файлу, это невозможно сделать через конвейер, поскольку, когда вы делаете что-либо подобное cat blah.txt | grep something > blah.txt, он непреднамеренно очищает файл. Есть небольшая служебная команда, которую spongeвы можете установить (вы делаетеcat blah.txt | grep something | sponge blah.txt и она буферизует содержимое файла, а затем записывает его в файл). Он похож на временный файл, но вам не нужно делать это явно. но я бы сказал, что это «худшее» требование, чем, скажем, Perl.
Может быть способ сделать это через awk или аналогичный, но если вам нужно использовать shell-скрипт, я думаю, что временный файл - это, безусловно, самый простой (только /?) Способ ..
Как предлагает Даниэль Велков, используйте тройник.
Для меня это простое умное решение:
{ echo foo; cat bar; } | tee bar > /dev/null
РЕДАКТИРОВАТЬ: это сломано. См. Странное поведение при добавлении к файлу с помощью cat и tee
Решение проблемы перезаписи tee:
cat header main | tee main > /dev/null
Тот, который я использую. Это позволяет вам указывать порядок, дополнительные символы и т. Д. Так, как вам нравится:
echo -e "TEXTFIRSt\n$(< header)\n$(< my.txt)" > my.txt
PS: только он не работает, если файлы содержат текст с обратной косой чертой, потому что он интерпретируется как 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
Еще один обходной путь - использовать дескрипторы открытых файлов, как предложил Юрген Хётцель в выводе Redirect из 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 исходный файл потерян (думал, что не должен, но это так). Это можно исправить, написав свое решение как: echo "текст для добавления" | кошка - file_to_be_modified | cat> tmp_file; mv tmp_file file_to_be_modified
Вот что я обнаружил:
echo -e "header \n$(cat file)" >file
ВНИМАНИЕ: для удовлетворения потребностей OP требуется немного больше работы.
Должен быть способ заставить работать sed с помощью @shixilun, несмотря на его опасения. Должна быть команда bash для экранирования пробелов при чтении файла в заменяющую строку sed (например, заменять символы новой строки на '\ n'. Команды оболочки visи catмогут работать с непечатаемыми символами, но не с пробелами, поэтому это не решит OP проблема:
sed -i -e "1s/^/$(cat file_with_header.txt)/" file_to_be_prepended.txt
не выполняется из-за необработанных символов новой строки в сценарии замены, которые должны быть добавлены с символом продолжения строки () и, возможно, за ним следует &, чтобы оболочка и sed были счастливы, как этот ответ SO
sed имеет ограничение по размеру 40 КБ для неглобальных команд поиска-замены (без завершающего символа / g после шаблона), поэтому, вероятно, можно избежать пугающих проблем с переполнением буфера awk, о которых предупреждал аноним.
sed -i -e "1s/^/new first line\n/" old_file.txt
Решение с 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}"
Но в этом случае вы должны быть уверены, что ничего нет %, включая содержимое целевого файла, так как это может быть интерпретировано и испортить ваши результаты.
echoкажется более безопасным вариантом. echo "my new line\n$(cat my/file.txt)" > my/file.txt
${new_line}, а не о целевом файле
Вы можете использовать командную строку 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 разрешает оценку кода при замене.
Я любя @ fluffle в ред подхода к лучшему. В конце концов, переключатели командной строки любого инструмента и команды редактора сценариев здесь по сути одно и то же; не видя, что "чистота" решения для редактора сценариев становится меньше или еще много чего.
Вот мой однострочник, добавленный к добавлению файла в .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, потому что это оставит пустую строку, готовую к записи, поверх файла из исходного шаблона. Не помещайте пустую строку поверх слова .gitmessagethen, в результате вы получите две пустые строки.-sподавляет вывод диагностической информации изд.
В связи с этим я обнаружил, что для vim-buffs также хорошо иметь:
[core]
editor = vim -c ':normal gg'
Думаю, это самый чистый вариант 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 - ваш файл / tmp / out && mv / tmp / out ваш файл
Это на самом деле в сложном примере, который вы сами опубликовали в своем собственном вопросе.
cat - yourfile <<<"text" > /tmp/out && mv /tmp/out yourfile, однако это существенно отличается от моего ответа, и это должен быть собственный ответ.
ИМХО нет (и никогда не будет) оболочки, которая работала бы последовательно и надежно независимо от размеров двух файлов myheaderи файлов myfile. Причина в том, что если вы хотите сделать это, не возвращаясь к временному файлу (и не позволяя оболочке молча возвращаться к временному файлу, например, через такие конструкции, как exec 3<>myfile, трубопровод кtee и т. Д.
«Настоящее» решение, которое вы ищете, должно возиться с файловой системой, поэтому оно недоступно в пользовательском пространстве и будет зависеть от платформы: вы просите изменить указатель файловой системы, который используется, myfileна текущее значение указателя файловой системы. for myheaderи замените в файловой системе EOFof myheaderна связанную ссылку на текущий адрес файловой системы, на который указывает myfile. Это нетривиально и, очевидно, не может быть сделано не суперпользователем, и, вероятно, не суперпользователем ... Играйте с inode и т. Д.
Однако вы можете более или менее подделать это с помощью петлевых устройств. См., Например, эту тему SO .
mktemp? Вы всегда можете очистить временный файл после этого ...