Це, мабуть, складне рішення .
Я шукаю такого простого оператора, як ">>", але для попереднього попередження.
Боюся, його не існує. Мені доведеться зробити щось на кшталт
mv myfile tmp cat myheader tmp> myfile
Щось розумніше?
Це, мабуть, складне рішення .
Я шукаю такого простого оператора, як ">>", але для попереднього попередження.
Боюся, його не існує. Мені доведеться зробити щось на кшталт
mv myfile tmp cat myheader tmp> myfile
Щось розумніше?
Відповіді:
Хак нижче був швидкий непідготовлений відповідь , який працював і отримав багато upvotes. Потім, коли питання ставало все більш популярним та проходило все більше часу, обурені люди почали повідомляти, що це сортування спрацьовує, але дивні речі можуть статися, або це взагалі не спрацьовує, тому це було люто відхилено протягом певного часу. Така забава.
Рішення використовує точну реалізацію дескрипторів файлів у вашій системі, і оскільки реалізація значно відрізняється від ніксейсів, її успіх є повністю залежним від системи, остаточно не переносним, і на нього не слід покладатися ні на що навіть нечітко важливе.
Тепер на все, що не вийшло, відповідь була:
Створення іншого дескриптора файлу для запису файлу ( exec 3<> yourfile), після чого записується до цього ( >&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
(попередження: знищити його через деякий час або воно заповнить файлову систему)
Крім того, дуже небезпечно редагувати файли таким чином, і це дуже погана порада, так як якщо щось відбувається під час редагування файлу (збій, диск повний), ви майже гарантовано залишите файл у непослідовному стані.
Неможливо без тимчасового файлу, але тут іде oneliner
{ echo foo; cat oldfile; } > newfile && mv newfile oldfile
Ви можете використовувати інші інструменти, такі як ed або perl, щоб зробити це без тимчасових файлів.
Можливо, варто відзначити, що часто є хорошою ідеєю безпечно генерувати тимчасовий файл за допомогою утиліти типу mktemp , принаймні, якщо сценарій коли-небудь буде виконуватися з root правами. Ви можете, наприклад, зробити наступне (знову в bash):
(tmpfile=`mktemp` && { echo "prepended text" | cat - yourfile > $tmpfile && mv $tmpfile yourfile; } )
Якщо вам це потрібно на комп'ютерах, якими ви керуєте, встановіть пакет "moreutils" та використовуйте "губку". Тоді ви можете зробити:
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 до того, як буде виконана кішка з переспрямуванням.
припускаючи, що файл, який ви хочете відредагувати, - це мій.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
Наскільки я знаю, це працює лише в «баш».
this is the headerв моєму.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
EDIT: Це порушено. Дивіться дивну поведінку під час готування до файлу з котом і трійником
Для вирішення проблеми перезапису використовується tee:
cat header main | tee main > /dev/null
Той, яким я користуюся. Цей дозволяє визначати порядок, додаткові символи тощо таким чином, як вам це подобається:
echo -e "TEXTFIRSt\n$(< header)\n$(< my.txt)" > my.txt
PS: тільки він не працює, якщо файли містять текст із зворотною косою рисою, тому що його інтерпретують як символи втечі
Здебільшого для розваг / гольф-снаряд, але
ex -c '0r myheader|x' myfile
зробить трюк, а трубопроводів чи переадресацій немає. Звичайно, vi / ex насправді не для неінтерактивного використання, тому vi коротко спалахне.
Чому б не просто скористатися командою 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 для "no temp file" для додавання фіксованого тексту:
echo "text to prepend" | cat - file_to_be_modified | ( cat > file_to_be_modified )
Знову ж таки, це покладається на виконання під-оболонки - (..) - щоб уникнути відмови кішки мати один і той же файл для введення та виводу.
Примітка. Це рішення сподобалось. Однак у моєму Mac оригінальний файл втрачається (думав, що він не повинен, але це робить). Це можна виправити, написавши ваше рішення у вигляді: відлуння "текст для подачі" | cat - file_to_be_modified | кішка> tmp_file; mv tmp_file file_to_be_modified
Ось що я виявив:
echo -e "header \n$(cat file)" >file
Попередження: для задоволення потреб ОП потрібно трохи більше попрацювати.
Незважаючи на його сумніви, повинен бути спосіб зробити підхід до роботи @shixilun. Повинна бути команда bash, щоб звільнити пробіл під час читання файлу в рядок заміщення sed (наприклад, замінити символи нового рядка на '\ n'. Команди оболонки visі catможуть працювати з недрукованими символами, але не пробілами, тому це не вирішить проблема:
sed -i -e "1s/^/$(cat file_with_header.txt)/" file_to_be_prepended.txt
виходить з ладу через неочищені нові рядки в сценарії замінника, які повинні бути попередньо заздалегідь символом продовження рядка () і, можливо, слідом за &, щоб зберегти оболонку і sed щасливими, як ця відповідь ТА
sed має обмеження розміру 40K для неглобальних команд пошуку-заміни (відсутнє трейлінг / г після шаблону), тому, швидше за все, уникнути страхітливих проблем із перекриттям буфера 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файл in-repo для фіксації повідомлень:
echo -e "1r $PWD/.gitmessage\n.\nw" | ed -s "$1"
Приклад .gitmessage:
# Commit message formatting samples:
# runlevels: boot +consolekit -zfs-fuse
#
Я роблю 1rзамість цього 0r, тому що це залишить порожній рядок готового до запису зверху файла з оригінального шаблону. Не ставте порожній рядок над вашим .gitmessageтоді, ви отримаєте два порожніх рядка. -sпригнічує діагностичний вихід інформації за ред.
У зв’язку з переглядом цього питання я виявив, що для vim-buffs також добре мати:
[core]
editor = vim -c ':normal gg'
Я думаю, що це найчистіша версія редакції:
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, однак це досить відрізняється від моєї відповіді, що це має бути власна відповідь.
IMHO не існує оболонки (і ніколи не буде такої), яка б працювала послідовно та надійно незалежно від розмірів двох файлів myheaderі myfile. Причина полягає в тому, що якщо ви хочете це зробити, не повторюючи тимчасовий файл (і не дозволяючи оболонці мовчки повторюватися до тимчасового файлу, наприклад, через такі конструкції, як exec 3<>myfileпідключення до teeтощо.
"Реальне" рішення, яке ви шукаєте, повинно співпадати з файловою системою, і тому воно недоступне в просторі користувачів і буде залежати від платформи: ви просите змінити покажчик файлової системи у використанні myfileна поточне значення вказівника файлової системи для myheaderі замінити в файлової системі EOFз myheaderз ланцюгом , посиланнями на поточну адресу файлової системи зазначених вище myfile. Це не банально, і очевидно, це не може зробити не суперпользователь, і, мабуть, не і суперпользователь ... Пограйте з введеннями тощо.
Однак ви можете більш-менш підробити це за допомогою циклічних пристроїв. Дивіться, наприклад цей потік SO .
mktemp? Ви завжди можете очистити тимчасовий файл після цього ...