Команда 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. OP может захотеть вставить в \nпосле добавления; в зависимости от их потребностей. Аккуратное решение.
Левон

Да, объяснение было бы отличным. Можно ли inFileвключить каталоги в свой путь?
zakdances

3
@Levon Я очень хотел вставить \n. Надо было сначала прочитать комментарии. Черт.
chishaku

В моем случае я хочу иметь точку с '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
Одной строкой и в исходном текстовом файле! Это правильный простой ответ. Я предлагаю дополнительную новую строку \ п эхо -e « чтобы вставляться перед \ п $ (кот text.txt)»> text.txt
VincentPerrin.com

32
У этого решения есть проблемы ... оно преобразует вхождения "\ n" в фактические новые строки ... проблематично, если вы добавляете к файлу кода строки, содержащие "\ n".
Paul Go

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

8
Это (1) загружает весь файл в память и (2) вставляет весь файл в один аргумент командной строки. Прекрасно для простых вещей, но остерегайтесь ограничений.
jwd

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

44

Замена процесса

Я удивлен, что никто об этом не упомянул.

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

что, возможно, более естественно, чем принятый ответ (распечатать что-то и передать это в команду подстановки лексикографически нелогично).

... и захватить то, что сказал Райан выше, при этом spongeвам не нужен временный файл:

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

РЕДАКТИРОВАТЬ: Похоже, это не работает в Bourne Shell /bin/sh


Здесь String (только zsh)

Используя здесь-строку - <<<, вы можете:

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

3
Этот ответ для меня победитель. Просто и доступно только с помощью встроенных функций. Хорошая работа!
PiersyP

1
@PiersyP Согласен! Это очень помогло, спасибо Шридхар-Сарнобат.

Проблема в том, что это 1 файл, а не 2 файла. Итак, первая строка ответа на самом деле не работает. Последняя строка может работать (но для этого требуется губка).
VasiliNovikov

1
Что делает губка?
Hemanta

1
Вы <<(echo "to be prepended") < text.txtи <<< "to be prepended" < text.txtконструкция не работает в Баше; они требуют zsh.
Андерс Касеорг

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.

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. Вывод может быть направлен в другой файл, если это сработает для OP
Левон

1
это определенно напечатает "быть добавленным", а затем содержимое "text.txt". Но я бы хотел, чтобы текст был добавлен к файлу
One Two Three

1
а что если у меня многострочный текст? Как мне поместить сюда символ новой строки?
One Two Three

Это было бы круто, но 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

11

Если допустимо заменить входной файл:

Примечание:

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

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

Вот простая альтернатива, использующая временный файл (он позволяет избежать чтения всего входного файла в память, как это делает решение shime ):

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

Использование групповой команды ( { ...; ...; }) немного более эффективно, чем использование подоболочки ( (...; ...)), как в решении 0xC0000022L .

Преимущества:

  • Легко контролировать, следует ли добавлять новый текст непосредственно к первой строке или его следует вставлять как новую строку (строки) (просто добавьте \nк printfаргументу).

  • В отличие от sedрешения, работает, если входной файл пуст ( 0байтов).


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

sed«S iфункция вставки целые строки:

С 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

Обратите внимание, что буквальный перевод строки после символа \является обязательным.


Если входной файл необходимо отредактировать на месте (с сохранением его индексного дескриптора со всеми его атрибутами):

Используя почтенную edутилиту POSIX :

Примечание:

  • edвсегда сначала считывает входной файл в память целиком .

Чтобы добавить непосредственно к первой строке (как и в случае sed, это не сработает, если входной файл полностью пуст ( 0байты)):

ed -s text.txt <<EOF
1 s/^/to be prepended/
w
EOF
  • -sedсообщения о статусе пользователя suppressed .
  • Обратите внимание, как команды передаются edв виде многострочного файла here-document ( <<EOF\n...\nEOF), то есть через стандартный ввод ; По умолчанию раскрытие строки будет выполнено в таких документах (оболочки переменной интерполяции); заключите в кавычки открывающий разделитель, чтобы скрыть это (например, <<'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) как часть временного файла. При создании сценария для общего использования это предотвратит потенциальную перезапись временного файла другим пользователем, запустив тот же сценарий в то же время.
E Brown

3
Однако $$для производственного кода использования недостаточно (атака по символическим ссылкам Google); но это определенно лучше, чем статическое имя файла.
Tripleee

1
просто используйтеmktemp
mewa

7

В некоторых случаях добавленный текст может быть доступен только из stdin. Тогда эта комбинация будет работать.

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

Если вы хотите опустить teeвывод, добавьте > /dev/null.


Мне очень нравится этот ответ. Это очень чистый и понятный способ сделать это. Использование tee - естественное решение проблемы вывода вывода во входной файл. Также никаких хаков с переименованием. Лучше, чем решение, получившее наибольшее количество голосов, поскольку оно не создает новый файл. По сути, это самое близкое к >> добавление в начале вместо добавления.
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. Если lenAэто 1000000 (файл 1 lenBМб ) и 10000 (добавление текста 10 Кб), то файл «a.txt» перезаписывается 20 КБ букв «b». Это полностью сломано. Теперь, если вы используете 1 МБ a.txt и 1 МБ текста для добавления, teeпереходит в цикл, генерирующий файл размером 7 ГБ +, мне пришлось остановить команду. Итак, очевидно, что для больших размеров результат непредсказуем. У меня нет информации, должно ли это работать на небольших размерах.
VasiliNovikov

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)". Все обходные пути относительно сложны. Если вы хотите сохранить символы новой строки в конце файла .txt, см. Это: https://stackoverflow.com/a/22607352/1091436


Любить это. Супер портативный и не требует создания нового файла. Спасибо!
pyb

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?
user unknown

@userunknown: стандартно sed, за iкомандой следует обратная косая черта и новая строка , и каждая строка ввода, кроме последней, также заканчивается обратной косой чертой. GNU sedдопускает сокращения в соответствии с тем, о чем вы спрашиваете, но это sedделает только GNU » .
Джонатан Леффлер

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

Никакого промежуточного файла, просто bash / echo.


2
лучший ответ, я написал этот однострочник, чтобы повернуть мой файл, используя этот ответ: for i in $ (<file2.txt); сделать echo "$ (echo $ i; cat file.txt)"> file.txt; сделано;
Аурангзеб

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.