Наверное, это сложное решение .
Я ищу простой оператор, такой как ">>", но для добавления.
Боюсь, этого не существует. Мне нужно сделать что-то вроде
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
, потому что это оставит пустую строку, готовую к записи, поверх файла из исходного шаблона. Не помещайте пустую строку поверх слова .gitmessage
then, в результате вы получите две пустые строки.-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
и замените в файловой системе EOF
of myheader
на связанную ссылку на текущий адрес файловой системы, на который указывает myfile
. Это нетривиально и, очевидно, не может быть сделано не суперпользователем, и, вероятно, не суперпользователем ... Играйте с inode и т. Д.
Однако вы можете более или менее подделать это с помощью петлевых устройств. См., Например, эту тему SO .
mktemp
? Вы всегда можете очистить временный файл после этого ...