Возможно, это комплексное решение.
Я ищу простой оператор типа " → ", но для добавления.
Я боюсь, что этого не существует. Мне нужно сделать что-то вроде
mv myfile tmp cat myheader tmp > myfile
Что-нибудь умнее?
Возможно, это комплексное решение.
Я ищу простой оператор типа " → ", но для добавления.
Я боюсь, что этого не существует. Мне нужно сделать что-то вроде
mv myfile tmp cat myheader tmp > myfile
Что-нибудь умнее?
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
Это все еще использует временный файл, но по крайней мере он находится в одной строке:
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
John Mee: ваш метод не гарантированно работает, и, вероятно, он потерпит неудачу, если вы добавите более 4096 байт материала (по крайней мере, то, что происходит с gnu awk, но я полагаю, что другие реализации будут иметь схожие ограничения). В этом случае он не только потерпит неудачу, но и войдет в бесконечный цикл, где он будет читать свой собственный вывод, тем самым увеличив файл до тех пор, пока не будет заполнено все свободное пространство.
Попробуйте сами:
exec 3<>myfile && awk 'BEGIN{for(i=1;i<=1100;i++)print i}{print}' myfile >&3
(предупреждение: убить его через некоторое время или заполнить файловую систему)
Кроме того, очень опасно редактировать файлы таким образом, и это очень плохой совет, как будто что-то происходит, когда файл редактируется (сбой, полный диск), вы почти гарантированно остаетесь с файлом в непоследовательной состояние.
Возможно, стоит отметить, что часто это хорошая идея для безопасного создания временного файла с помощью утилиты, такой как mktemp, по крайней мере, если script будет когда-либо выполняются с привилегиями root. Например, вы можете сделать следующее (снова в bash):
(tmpfile=`mktemp` && { echo "prepended text" | cat - yourfile > $tmpfile && mv $tmpfile yourfile; } )
Невозможно без временного файла, но здесь идет oneliner
{ echo foo; cat oldfile; } > newfile && mv newfile oldfile
Вы можете использовать другие инструменты, такие как ed или perl, чтобы сделать это без временных файлов.
Если вам это нужно на компьютерах, которыми вы управляете, установите пакет "moreutils" и используйте "губку". Затем вы можете сделать:
cat header myfile | sponge myfile
Когда вы начинаете пытаться делать вещи, которые становятся трудными в 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?) способом.
РЕДАКТОР: Это нарушено. Смотрите странное поведение при добавлении файла с кошкой и телом
Обходной путь к проблеме перезаписи заключается в использовании tee
:
cat header main | tee main > /dev/null
Предполагая, что файл, который вы хотите изменить, - 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.
Как предлагает Даниэль Велков, используйте тройник.
Для меня это простое интеллектуальное решение:
{ echo foo; cat bar; } | tee bar > /dev/null
Используя bash heredoc, вы можете избежать необходимости в файле tmp:
cat <<-EOF > myfile
$(echo this is prepended)
$(cat myfile)
EOF
Это работает, потому что $(cat myfile) оценивается, когда оценивается bash script, прежде чем выполняется кошка с перенаправлением.
Тот, который я использую. Это позволяет указать порядок, дополнительные символы и т.д. Так, как вам нравится:
echo -e "TEXTFIRSt\n$(< header)\n$(< my.txt)" > my.txt
P.S: только он не работает, если файлы содержат текст с обратным слэшем, потому что он интерпретируется как escape-символы
В основном для гольфа с потерей/раковиной, но
ex -c '0r myheader|x' myfile
сделает трюк, и нет конвейеров или перенаправления. Конечно, vi/ex не предназначен для неинтерактивного использования, поэтому vi будет кратковременно мигать.
Вариант решения 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
ПРЕДУПРЕЖДЕНИЕ: для этого требуется немного больше работы для удовлетворения потребностей 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, о котором предупреждали анонимные пользователи.
Почему бы просто не использовать команду 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
Все это можно было бы поместить в одну строку, конечно.
sed -i -e '1rmyheader' -e '1{h;d}' -e '2{x;G}' myfile
С помощью $(команда) вы можете записать вывод команды в переменную. Поэтому я сделал это в трех командах в одной строке и без временного файла.
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 разрешит оценку кода в подстановке.
Если вы создаете сценарий в BASH, на самом деле, вы можете просто выпустить:
cat - yourfile /tmp/out && mv /tmp/out yourfile
Что на самом деле в сложном примере вы сами разместили в своем собственном вопросе.
Ба! Никто не хотел упоминать о 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/>
current=`cat my_file` && echo 'my_string' > my_file && echo $current >> my_file
где "my_file" - это файл, который добавляет "my_string" к.
sed -i -e "1s/^/new first line\n/" old_file.txt
Мне нравится @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'
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
Вот что я обнаружил:
echo -e "header \n$(cat file)" >file
Я думаю, что это самая чистая вариация 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