Оболочка один лайнер, чтобы подготовить к файлу

110

Возможно, это комплексное решение.

Я ищу простой оператор типа " → ", но для добавления.

Я боюсь, что этого не существует. Мне нужно сделать что-то вроде

 mv myfile tmp
 cat myheader tmp > myfile

Что-нибудь умнее?

  • 0
    Что не так с mktemp ? Вы всегда можете очистить временный файл потом ...
  • 0
    Похожие страницы : stackoverflow.com/questions/9533679/…
Теги:

32 ответа

23
Лучший ответ

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
  • 3
    ВНИМАНИЕ: проверьте вывод, чтобы убедиться, что ничего не изменилось, кроме первой строки. Я использовал это решение, и, хотя оно, похоже, сработало, я заметил, что некоторые новые строки, похоже, были добавлены. Я не понимаю, откуда они взялись, поэтому я не могу быть уверен, что у этого решения действительно есть проблема, но будьте осторожны.
  • 1
    Обратите внимание, что в приведенном выше примере bash двойные кавычки охватывают возврат каретки, чтобы продемонстрировать добавление нескольких строк. Убедитесь, что ваша оболочка и текстовый редактор сотрудничают. \ r \ n приходит на ум.
Показать ещё 3 комментария
77

Это все еще использует временный файл, но по крайней мере он находится в одной строке:

echo "text" | cat - yourfile > /tmp/out && mv /tmp/out yourfile

Кредит: BASH: Подготовьте текст/строки к файлу

  • 4
    Что делает - после cat ?
  • 6
    Это значит, что кот читает из трубы.
Показать ещё 3 комментария
26
echo '0a
your text here
.
w' | ed some_file

ed - это стандартный редактор! http://www.gnu.org/fun/jokes/ed.msg.html

  • 6
    или вместо 0r header.file текста для вставки: 0r header.file
19

John Mee: ваш метод не гарантированно работает, и, вероятно, он потерпит неудачу, если вы добавите более 4096 байт материала (по крайней мере, то, что происходит с gnu awk, но я полагаю, что другие реализации будут иметь схожие ограничения). В этом случае он не только потерпит неудачу, но и войдет в бесконечный цикл, где он будет читать свой собственный вывод, тем самым увеличив файл до тех пор, пока не будет заполнено все свободное пространство.

Попробуйте сами:

exec 3<>myfile && awk 'BEGIN{for(i=1;i<=1100;i++)print i}{print}' myfile >&3

(предупреждение: убить его через некоторое время или заполнить файловую систему)

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

  • 6
    Вероятно, это должен быть комментарий, а не отдельный ответ.
  • 10
    @Ikraav: возможно, но «комментарий» очень длинный и сложный (и полезный), он не будет хорошо выглядеть и, возможно, в любом случае не будет соответствовать ограниченным возможностям комментариев StackOverflow.
15

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

(tmpfile=`mktemp` && { echo "prepended text" | cat - yourfile > $tmpfile && mv $tmpfile yourfile; } )
12

Невозможно без временного файла, но здесь идет oneliner

{ echo foo; cat oldfile; } > newfile && mv newfile oldfile

Вы можете использовать другие инструменты, такие как ed или perl, чтобы сделать это без временных файлов.

10

Если вам это нужно на компьютерах, которыми вы управляете, установите пакет "moreutils" и используйте "губку". Затем вы можете сделать:

cat header myfile | sponge myfile
9

РЕДАКТОР: Это нарушено. Смотрите странное поведение при добавлении файла с кошкой и телом

Обходной путь к проблеме перезаписи заключается в использовании tee:

cat header main | tee main > /dev/null
  • 4
    Кажется, это не работает для длинных файлов.
  • 0
    Повис на некоторое время. Завершается «tee: main: на устройстве не осталось места». Основным было несколько ГБ, хотя оба исходных текстовых файла были всего несколько КБ.
Показать ещё 2 комментария
9

Когда вы начинаете пытаться делать вещи, которые становятся трудными в 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?) способом.

  • 0
    +1 за рекомендацию правильного языка сценариев.
8

Как предлагает Даниэль Велков, используйте тройник.
Для меня это простое интеллектуальное решение:

{ echo foo; cat bar; } | tee bar > /dev/null
8

Предполагая, что файл, который вы хотите изменить, - 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.

  • 0
    почему это работает? Приводят ли круглые скобки к выполнению среднего кота в отдельной оболочке и одновременному выводу всего файла?
  • 0
    Я думаю, что <(cat my.txt) создает временный файл где-то в файловой системе. Этот файл затем читается до изменения исходного файла.
Показать ещё 4 комментария
7

Используя bash heredoc, вы можете избежать необходимости в файле tmp:

cat <<-EOF > myfile
  $(echo this is prepended)
  $(cat myfile)
EOF

Это работает, потому что $(cat myfile) оценивается, когда оценивается bash script, прежде чем выполняется кошка с перенаправлением.

3

В основном для гольфа с потерей/раковиной, но

ex -c '0r myheader|x' myfile

сделает трюк, и нет конвейеров или перенаправления. Конечно, vi/ex не предназначен для неинтерактивного использования, поэтому vi будет кратковременно мигать.

  • 0
    Лучший рецепт с моей точки зрения, поскольку он использует действующую команду и делает это простым способом.
3

Тот, который я использую. Это позволяет указать порядок, дополнительные символы и т.д. Так, как вам нравится:

echo -e "TEXTFIRSt\n$(< header)\n$(< my.txt)" > my.txt

P.S: только он не работает, если файлы содержат текст с обратным слэшем, потому что он интерпретируется как escape-символы

2
sed -i -e '1rmyheader' -e '1{h;d}' -e '2{x;G}' myfile
2

Почему бы просто не использовать команду 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

Все это можно было бы поместить в одну строку, конечно.

2

ПРЕДУПРЕЖДЕНИЕ: для этого требуется немного больше работы для удовлетворения потребностей 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, о котором предупреждали анонимные пользователи.

2

Вариант решения 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

  • 1
    Я тоже нахожу исходный файл потерянным. Убунту Люсид.
  • 0
    Решение @nemisj, кажется, работает.
1

Вы можете использовать командную строку 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 разрешит оценку кода в подстановке.

1

Решение с 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}"

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

  • 2
    Кажется, это взрывается (и усекает ваш файл!), Если в вашем целевом файле есть что-то, что printf интерпретирует как параметр форматирования.
  • 0
    echo кажется более безопасным вариантом. echo "my new line\n$(cat my/file.txt)" > my/file.txt
Показать ещё 6 комментариев
1

Если у вас есть большой файл (несколько сотен килобайт в моем случае) и доступ к python, это намного быстрее, чем cat решения для труб:

python -c 'f = "filename"; t = open(f).read(); open(f, "w").write("text to prepend " + t)'

1

С помощью $(команда) вы можете записать вывод команды в переменную. Поэтому я сделал это в трех командах в одной строке и без временного файла.

originalContent=$(cat targetfile) && echo "text to prepend" > targetfile && echo "$originalContent" >> targetfile
0

Быстро и грязно, буферизуйте все в памяти с помощью python:

$ echo two > file
$ echo one | python -c "import sys; f=open(sys.argv[1]).read(); open(sys.argv[1],'w').write(sys.stdin.read()+f)" file
$ cat file
one
two
$ # or creating a shortcut...
$ alias prepend='python -c "import sys; f=open(sys.argv[1]).read(); open(sys.argv[1],\"w\").write(sys.stdin.read()+f)"'
$ echo zero | prepend file
$ cat file
zero
one
two
0

IMHO нет решения оболочки (и никогда не будет такого), который будет работать последовательно и надежно независимо от размеров двух файлов myheader и myfile. Причина в том, что если вы хотите сделать это, не повторяясь во временном файле (и не позволяя оболочке тихо вернуться к временному файлу, например, через конструкции, такие как exec 3<>myfile, piping to tee и т.д.

"Реальное" решение, которое вы ищете, должно возиться с файловой системой, и поэтому оно недоступно в пользовательском пространстве и будет зависящим от платформы: вы просите изменить указатель файловой системы, используемый myfile на текущее значение указателя файловой системы для myheader и замените в файловой системе EOF на myheader цепочкой ссылку на текущий адрес файловой системы, указанный myfile. Это не тривиально и, очевидно, не может быть сделано не суперпользователем, а, вероятно, не суперпользователем... Играйте с inodes и т.д.

Вы можете более или менее подделать это, используя устройства loop. См. Например этот поток SO.

0

Я думаю, что это самая чистая вариация 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
0

Вот что я обнаружил:

echo -e "header \n$(cat file)" >file
  • 1
    Это то же самое, что и раннее предложение @nemisj. Нет?
0

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
0

Мне нравится @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'
0
sed -i -e "1s/^/new first line\n/" old_file.txt
  • 1
    К сожалению ... это только добавляет одну строку ... не произвольный файл ...
  • 0
    именно то, что я искал, но на самом деле не правильный ответ на вопрос
0
current=`cat my_file` && echo 'my_string' > my_file && echo $current >> my_file

где "my_file" - это файл, который добавляет "my_string" к.

0

Ба! Никто не хотел упоминать о tac.

endor@grid ~ $ 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/>
  • 3
    TAC может решить проблему, но вы только что распечатали справку для TAC ... Не особенно полезно.
  • 4
    здесь не очень полезно - это будет: { tac myfile; tac header; } | tac > newfile && mv newfile myfile
0

Если вы создаете сценарий в BASH, на самом деле, вы можете просто выпустить:

cat - yourfile  /tmp/out && mv /tmp/out yourfile

Что на самом деле в сложном примере вы сами разместили в своем собственном вопросе.

  • 0
    Редактор предложил изменить это на cat - yourfile <<<"text" > /tmp/out && mv /tmp/out yourfile , однако это довольно cat - yourfile <<<"text" > /tmp/out && mv /tmp/out yourfile отличается от моего ответа, так как это должен быть его собственный ответ.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню