Команда Unix для додавання тексту до файлу


126

Чи є команда Unix для додавання деяких рядкових даних до текстового файлу?

Щось на зразок:

prepend "to be prepended" text.txt

Ви шукаєте одну команду, або це невеликий сценарій / псевдонім команда в порядку?
Левон

2
Якщо ви поєднаєте всі відповіді, це, мабуть, найбільш компактний спосіб:<<(echo "to be prepended") < text.txt | sponge text.txt
Шрідхар Сарнобат

Це питання справді виявило творчість у багатьох людей!
Річард Джессоп

Відповіді:


111
sed -i.old '1s;^;to be prepended;' inFile
  • -iзаписує зміну місця і приймає резервну копію, якщо надано якесь розширення. (У цьому випадку .old)
  • 1s;^;to be prepended;замінює початок першого рядка заданим рядком заміни, використовуючи ;як роздільник команди.

19
Якщо ви можете додати якесь пояснення за допомогою команди, було б корисно для інших, не так знайомих з роботою sed. ОП може захотіти вставити \nпісля передбавки; залежно від їх потреб. Охайний розчин.
Левон

Так, пояснення було б чудово. Чи можна inFileвключати каталоги в його шлях?
zakdances

3
@Levon я повністю хотів вставити \n. Слід спочатку прочитати коментарі. Блін.
чишаку

У моєму випадку я хочу мати '1s;^;my-prepended-line\;\n;'
напівкрапку в кінці заздалегідь запланованої

5
Зауважте, що '\ n' працює лише для GNU sed, а не BSD (наприклад, OSX, FreeBSD тощо). Щоб бути більш стерпним з тими, см: stackoverflow.com/questions/1421478 / ...
JWD

164
printf '%s\n%s\n' "to be prepended" "$(cat text.txt)" >text.txt

2
В одному рядку та в оригінальному текстовому файлі! Це правильна проста відповідь. Я пропоную додатковий новий рядок \ n echo -e "бути попередньо \ n $ (котячий текст.txt)"> text.txt
VincentPerrin.com

32
У цьому рішенні є проблеми ... він перетворює входження "\ n" у фактичні нові рядки ... проблематично, якщо ви готуєтесь до файлу коду з рядками, що містять "\ n".
Пол Іди

1
У мене це було у моєму файлі, git config --get-regex $arg | sed -r 's/\t{3}/\\n/g';і це заплуталося в міру перетворення \tі \n.
hIpPy

8
Це (1) завантажує весь файл у пам'ять, і (2) вставляє весь файл в один аргумент командного рядка. Штраф за прості речі, але остерігайтеся обмежень.
jwd

2
Останнє, що я спробував, я вважаю, що це порушиться на дійсно великих файлах, де кішка не зможе прочитати весь вміст text.txt перед тим, як його перезаписати. Якщо варіант -e в ехо є проблемою, ви можете процитувати новий рядок у bash або зробитиecho "to be prepended"$'\n'"$(cat text.txt)"
jbo5112

44

Процес заміни

Я здивований, що ніхто про це не згадував.

cat <(echo "before") text.txt > newfile.txt

що, мабуть, природніше, ніж прийнята відповідь (друкувати щось та передати це команді заміщення є лексикографічно протиінтуїтивно зрозумілим).

... і викрадаючи те, що сказав Райан вище, spongeвам не потрібен тимчасовий файл:

sudo apt-get install moreutils
<<(echo "to be prepended") < text.txt | sponge text.txt

EDIT: Схоже, це не працює в Bourne Shell /bin/sh


Тут рядок (лише zsh)

Використовуючи рядок тут - <<<, ви можете:

<<< "to be prepended" < text.txt | sponge text.txt

3
Ця відповідь для мене є переможцем. Простий і досяжний тільки з вбудованими. Гарна робота!
PiersyP

1
@PiersyP Я згоден! Це дуже допомогло, дякую Шрідхар-Сарнобат.

Проблема в тому, що є 1 файл, а не 2 файли. Отже, перший рядок відповіді насправді не працює. Останній рядок може працювати (але для цього потрібна губка).
ВасильНовіков

1
Що робить губка?
Геманта

1
Ваші <<(echo "to be prepended") < text.txtта <<< "to be prepended" < text.txtконструкції не працюють в баш; вони вимагають zsh.
Anders Kaseorg

32

Це одна з можливостей:

(echo "to be prepended"; cat text.txt) > newfile.txt

напевно, ви не зможете легко обійти проміжний файл

Альтернативи (можуть бути громіздкими з втечею оболонки):

sed -i '0,/^/s//to be prepended/' text.txt

Стратегія 1) - це найінтуїтивніша з будь-яких відповідей тут. І невелика зміна: cat <(echo "to be prepended") text.txt > newfile.txt . Подумайте про це, я не впевнений, що моє пов’язане, тому я розміщую окрему відповідь.
Шрідхар Сарнобат

1
Перший метод не збереже права доступу до файлів
XLSX

Єдиний спосіб зберегти дозволи - використовувати губку
Шрідхар Сарнобат

20

Це допоможе сформувати вихід. - означає стандартний вхід, який забезпечується через трубу від луни.

echo -e "to be prepended \n another line" | cat - text.txt

Щоб перезаписати файл, потрібен тимчасовий файл, оскільки він не може передаватися назад у вхідний файл.

echo "to be prepended" | cat - text.txt > text.txt.tmp
mv text.txt.tmp text.txt

2
це не додає текст до файлу, text.txtхоч як вимагається, але відображає його у stdout. Вихід може бути переведений в інший файл, якщо це працює для ОП
Левон

1
це, безумовно, надрукує "бути попередньо", а потім вміст "text.txt". Але я хотів би, щоб текст був попередньо поданий до файлу
Один Два Три

1
а що робити, якщо у мене є багаторядковий текст Як я можу вставити символ нового рядка туди?
Один два три

Це було б круто, але bash це не любить: $ echo RewriteBase / | cat - web / .htaccess> web / .htaccess cat: web / .htaccess: вхідний файл - вихідний файл
geek-merlin

14

Віддавайте перевагу відповіді Адама

Ми можемо полегшити використання губки . Тепер нам не потрібно створювати тимчасовий файл і перейменовувати його за

echo -e "to be prepended \n another line" | cat - text.txt | sponge text.txt

це для мене було єдиним рішенням. це досить смішно, що ім'я мого сервера - губка.
Ömer An

10

Якщо прийнятно замінити вхідний файл:

Примітка:

  • Це може мати несподівані побічні ефекти, зокрема потенційно замінити символьне посилання звичайним файлом, закінчившись різними дозволами на файл та змінивши дату створення (народження) файлу.

  • sed -i, як у відповіді принца Джона Веслі , намагається принаймні відновити початкові дозволи, але застосовуються й інші обмеження.

Ось проста альтернатива, що використовує тимчасовий файл (це дозволяє уникнути зчитування всього вхідного файлу в пам'яті так, як це робить рішення shime ):

{ printf 'to be prepended'; cat text.txt; } > tmp.txt && mv tmp.txt text.txt

Використання групової команди ( { ...; ...; }) є дещо ефективнішим, ніж використання підзаголовка ( (...; ...)), як у рішенні 0xC0000022L .

Перевагами є:

  • Це легко контролювати , буде новий текст повинен бути безпосередньо передують першу лінію або він повинен бути включений в якості нового рядка (s) (просто додати \nдо printfаргументу).

  • На відміну від sedрішення, воно працює, якщо вхідний файл порожній ( 0байти).


sedРішення може бути спрощена , якщо намір полягає в тому, щоб випереджати один або кілька цілих рядків в існуючий контент (припускаючи , що вхідний файл не є порожнім):

sediФункція ' вставляє цілі рядки:

З GNU sed :

# Prepends 'to be prepended' *followed by a newline*, i.e. inserts a new line.
# To prepend multiple lines, use '\n' as part of the text.
# -i.old creates a backup of the input file with extension '.old'
sed -i.old '1 i\to be prepended' inFile

Портативний варіант, який також працює з macOS / BSD sed:

# Prepends 'to be prepended' *followed by a newline*
# To prepend multiple lines, escape the ends of intermediate
# lines with '\'
sed -i.old -e '1 i\
to be prepended' inFile

Зауважимо, що потрібний буквальний новий рядок після цього \.


Якщо вхідний файл повинен бути відредагований на місці (зберігши його inode з усіма його атрибутами):

Використання поважної edутиліти POSIX :

Примітка:

  • edнезмінно спочатку читає вхідний файл у цілому в пам'ять.

Щоб додати безпосередньо до першого рядка (як і в sedцьому, це не буде працювати, якщо вхідний файл повністю порожній ( 0байти)):

ed -s text.txt <<EOF
1 s/^/to be prepended/
w
EOF
  • -sпридушені edповідомлення про статус.
  • Зверніть увагу, як команди надаються edяк багаторядковий тут-документ ( <<EOF\n...\nEOF), тобто через stdin ; За замовчуванням розкриття рядка буде виконано в таких документах (оболонки змінної інтерполяції); котирування відкриття роздільник для придушення , що (наприклад, <<'EOF').
  • 1 робить 1-й рядок поточною лінією
  • функція sвиконує підстановку рядка на основі регулярних виразів у поточному рядку, як у sed; ви можете включити буквальні нові рядки в текст підстановки, але вони повинні бути \відмічені.
  • wзаписує результат назад в файл (обов для тестування, заміни wз ,pтільки роздрукувати результат, без зміни вхідного файлу).

Щоб додати один або кілька цілих рядків :

Як і у випадку sed, iфункція незмінно додає текстовий текст, який потрібно вставити.

ed -s text.txt <<EOF
0 i
line 1
line 2
.
w
EOF
  • 0 iробить 0(початок файлу) поточний рядок і починає режим вставки ( i); зауважте, що номери рядків інакше базуються 1.
  • Наступні рядки - це текст, який потрібно вставити перед поточним рядком, закінчений .власним рядком.

7

Можливо, нічого вбудованого, але ви могли написати своє досить легко, як-от так:

#!/bin/bash
echo -n "$1" > /tmp/tmpfile.$$
cat "$2" >> /tmp/tmpfile.$$
mv /tmp/tmpfile.$$ "$2"

Щось подібне хоча б ...


6
+1 за використання $$ (поточний PID) як частини назви файлу temp. Якщо створити сценарій для загального використання, це не дозволить іншому користувачеві перезаписати тимчасовий файл, запустивши той самий сценарій одночасно.
Е Браун

3
$$Однак використання недостатньо для виробничого коду (атака google symlink); але це, звичайно, краще, ніж статичне ім'я файлу.
трійчатка

1
просто використовуйтеmktemp
mewa

7

За певних обставин, попередньо створений текст може бути доступний лише від stdin. Тоді ця комбінація спрацює.

echo "to be prepended" | cat - text.txt | tee text.txt

Якщо ви хочете опустити teeвихід, додайте > /dev/null.


Мені дуже подобається ця відповідь. Це дуже чистий і зрозумілий спосіб зробити це. Використання трійника - це природне рішення проблеми спроби вивести кіт на вхідний файл. Також ніяких хакерів з перейменуванням. Краще, ніж рішення, яке найбільше проголосувало зараз, оскільки воно не створює новий файл. Це в основному найближче, що ви дістанетесь до >> заздалегідь, ніж додавати.
DC2

Це далеко найкраща і найчистіша відповідь.
nerdoc

6
Чи є гарантія, що teeне вилазить text.txtперед тим, catяк прочитати її? Я думаю, що ні - що зробило б це рішення небезпечним для здоров'я файлу.
Джонатан Леффлер

3
@JonathanLeffler, мабуть, ні. Я спробував цей код для перевірки: lenA=1000000; yes a | head -c $lenA > a.txt; lenB=10000; b=$(yes b | head -c $lenB); echo "$b" | cat - a.txt | tee a.txt > /dev/null. Якщо lenA1000000 (файл 1Mb) і lenB10000 (10Kb текст наперед), то файл "a.txt" перезаписується 20 кб літер "b". Це повністю порушено. Тепер, якщо ви використовуєте 1Mb a.txt та 1Mb текст для підготовки, teeпереходить у цикл, що генерує файл 7Gb +, мені довелося зупинити команду. Отже, очевидно, що результат непередбачуваний для великих розмірів. У мене немає інформації, чи має це працювати на невеликих розмірах.
ВасильНовіков

3
Якщо говорити концептуально: Ця команда призведе до втрати даних, якщо вхідний файл буде більшим за розмір буфера конвеєра вашої системи , який, як правило, становить сьогодні 64 КБ.
mklement0

7

Рішення:

printf '%s\n%s' 'text to prepend' "$(cat file.txt)" > file.txt

Зауважте, що це безпечно для всіх видів входів, оскільки немає розширень. Наприклад, якщо ви хочете зробити передоплату !@#$%^&*()ugly text\n\t\n, це просто спрацює:

printf '%s\n%s' '!@#$%^&*()ugly text\n\t\n' "$(cat file.txt)" > file.txt

Остання частина, що залишилася для розгляду, - це видалення пробілів у кінці файлу під час заміни команди "$(cat file.txt)". Усі напрямки роботи для цього відносно складні. Якщо ви хочете зберегти нові рядки в кінці file.txt, дивіться це: https://stackoverflow.com/a/22607352/1091436


Любіть це. Супер портативний і не потрібно генерувати новий файл. Дякую!
піб

1
Єдина проблема з цим виникає, якщо вміст file.txtбільше, ніж може бути вписаний до списку аргументів printf.
Джонатан Леффлер

6

Інший спосіб використання sed:

sed -i.old '1 {i to be prepended
}' inFile

Якщо попередньо попереджений рядок є багаторядковим:

sed -i.old '1 {i\ 
to be prepended\
multiline
}' inFile

Чому ні sed -i '1ito be prepended' inFile- або це дозволено лише для GNU sed?
користувач невідомий

@userunknown: У стандартне sed, то iкоманда з подальшою зворотною косою риси і нового рядка, і кожному рядку введення , за винятком останніх кінців за допомогою зворотного косою риси теж. GNU sedдозволяє скорочувати лінії, про які ви запитуєте, але це лише GNU sed. "
Джонатан Леффлер

3

Як перевірено в Bash (в Ubuntu), якщо починати з тестового файлу через;

echo "Original Line" > test_file.txt

можна виконати;

echo "$(echo "New Line"; cat test_file.txt)" > test_file.txt

або, якщо версія bash занадто стара для $ (), ви можете використовувати зворотні посилання;

echo "`echo "New Line"; cat test_file.txt`" > test_file.txt

і отримувати наступний вміст "test_file.txt";

New Line
Original Line

Немає файлу-посередника, просто баш / ехо.


2
найкраща відповідь, я написав цей однокласник, щоб обертати мій файл, використовуючи цю відповідь: для i в $ (<file2.txt); do echo "$ (echo $ i; cat file.txt)"> file.txt; зроблено;
Aurangzeb

2

Ще одне досить пряме рішення:

    $ echo -e "string\n" $(cat file)

Це працювало для мене ... лише незначна модифікація, щоб зберегти багатоколінні файли з вхідного файлу, ви повинні обернути його в лапки таким чином: echo -e "string \ n" "$ (cat ~ / file.txt) "> ~ / file.txt;
Крейг Уейн

2
Відсутність catрозширення параметрів у подвійних лапках є досить вагомою причиною зниження рівня . Цей код розбиває вміст файлу на слова і передає кожне з них як окремий аргумент echo. Ви втрачаєте оригінальні межі аргументів, ви втрачаєте нові рядки / вкладки / тощо., А рядки на зразок \tта \nв оригінальному тексті замінюються вкладками та новими рядками, а не зберігаються такими, якими вони були.
Чарльз Даффі

1

Якщо вам подобається vi / vim, це може бути більше вашим стилем.

printf '0i\n%s\n.\nwq\n' prepend-text | ed file

0
# create a file with content..
echo foo > /tmp/foo
# prepend a line containing "jim" to the file
sed -i "1s/^/jim\n/" /tmp/foo
# verify the content of the file has the new line prepened to it
cat /tmp/foo

0

Я рекомендую визначити функцію, а потім імпортувати та використовувати її там, де потрібно.

prepend_to_file() { 
    file=$1
    text=$2

    if ! [[ -f $file ]] then
        touch $file
    fi

    echo "$text" | cat - $file > $file.new

    mv -f $file.new $file
}

Потім використовуйте його так:

prepend_to_file test.txt "This is first"
prepend_to_file test.txt "This is second"

Вміст вашого файлу буде:

This is second
This is first

Я збираюся використовувати цей підхід для впровадження оновлення журналу змін.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.