Comando Unix para acrescentar texto a um arquivo

125

Existe um comando Unix para acrescentar alguns dados de string a um arquivo de texto?

Algo como:

prepend "to be prepended" text.txt
Um dois três
fonte
você está procurando um único comando ou um pequeno script / alias está ok?
Levon
2
Se você combinar todas as respostas, esta é provavelmente a forma mais compacta:<<(echo "to be prepended") < text.txt | sponge text.txt
Sridhar Sarnobat
Esta questão realmente trouxe a criatividade para muitas pessoas!
Richard Jessop

Respostas:

111
sed -i.old '1s;^;to be prepended;' inFile
  • -igrava a alteração no local e faz um backup se alguma extensão for fornecida. (Nesse caso, .old)
  • 1s;^;to be prepended;substitui o início da primeira linha pela sequência de substituição fornecida, usando ;como um delimitador de comando.
Prince John Wesley
fonte
19
Se você pudesse adicionar alguma explicação ao comando, seria útil para outras pessoas não tão familiarizadas com o funcionamento do sed. O OP pode querer inserir \ndepois de pré-anexado; dependendo de suas necessidades. Solução pura.
Levon
Sim, uma explicação seria ótima. O pode inFileincluir diretórios em seu caminho?
zakdances
3
@ Levon Eu queria muito inserir um \n. Deveria ter lido os comentários primeiro. Droga.
Chishaku
No meu caso, quero ter um ponto-e-vírgula no final da linha '1s;^;my-prepended-line\;\n;'
anexada
5
Observe que '\ n' funciona apenas para o GNU sed, não para o BSD (como OSX, FreeBSD, etc.). Para ser mais portátil com eles, consulte: stackoverflow.com/questions/1421478/…
jwd
163
printf '%s\n%s\n' "to be prepended" "$(cat text.txt)" >text.txt
shime
fonte
2
Em uma linha e no arquivo de texto original! Esta é a resposta simples e correta. Sugiro que a nova linha adicional \ n echo -e "seja anexada \ n $ (cat text.txt)">
text.txt
32
Esta solução tem problemas ... converte ocorrências de "\ n" em novas linhas reais ... problemáticas se você estiver anexando um arquivo de código com cadeias que contêm "\ n".
Paul Go
1
Eu tinha isso no meu arquivo git config --get-regex $arg | sed -r 's/\t{3}/\\n/g';e isso atrapalha quando converte o \te \n.
hippy
8
Isso (1) carrega o arquivo inteiro na memória e (2) coloca o arquivo inteiro em um único argumento de linha de comando. Ótimo para coisas simples, mas cuidado com as limitações.
jwd
2
A última vez que tentei, acredito que isso seria interrompido em arquivos muito grandes, onde o gato falha ao ler todo o conteúdo do text.txt antes de ser substituído. Se a opção -e o eco é um problema, você pode citar uma nova linha em bash ou fazerecho "to be prepended"$'\n'"$(cat text.txt)"
jbo5112
42

Substituição de Processo

Estou surpreso que ninguém tenha mencionado isso.

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

o que é indiscutivelmente mais natural do que a resposta aceita (imprimir algo e encaminhá-lo para um comando de substituição é lexicograficamente contra-intuitivo).

... e seqüestrar o que ryan disse acima, com spongevocê não precisa de um arquivo temporário:

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

EDIT: Parece que isso não funciona no Bourne Shell /bin/sh


Aqui String (apenas zsh)

Usando uma string here - <<<, você pode:

<<< "to be prepended" < text.txt | sponge text.txt
Sridhar Sarnobat
fonte
3
Esta resposta é a vencedora para mim. Simples e viável apenas com os componentes internos. Bom trabalho!
PiersyP
1
@PiersyP Concordo! Isso ajudou muito, obrigado Sridhar-Sarnobat.
O problema é que há 1 arquivo, não 2 arquivos. Portanto, a primeira linha da resposta realmente não funciona. A última linha pode funcionar (mas requer esponja).
VasiliNovikov
1
O que a esponja faz?
Hemanta
1
Suas construções <<(echo "to be prepended") < text.txte <<< "to be prepended" < text.txtnão funcionam no bash; eles exigem zsh.
Anders Kaseorg 25/08/19
32

Esta é uma possibilidade:

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

você provavelmente não conseguirá facilmente contornar um arquivo intermediário.

Alternativas (pode ser complicado com o escape da concha):

sed -i '0,/^/s//to be prepended/' text.txt
0xC0000022L
fonte
A estratégia 1) é a mais intuitiva de todas as respostas aqui, eu sinto. E uma ligeira variação: cat <(echo "to be prepended") text.txt > newfile.txt . Venha para pensar sobre isso, não tenho certeza se o meu está relacionado, por isso estou postando uma resposta separada.
Sridhar Sarnobat
1
O primeiro método não irá preservar as permissões de arquivo
Xlsx
A única maneira de preservar permissões é usar esponja
Sridhar Sarnobat
20

Isso funcionará para formar a saída. O - significa entrada padrão, que é fornecida através do tubo a partir de eco.

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

Para reescrever o arquivo, é necessário um arquivo temporário, pois não é possível canalizar novamente para o arquivo de entrada.

echo "to be prepended" | cat - text.txt > text.txt.tmp
mv text.txt.tmp text.txt
Adão
fonte
2
isso não anexa o texto ao arquivo, text.txtconforme solicitado, mas o exibe no stdout. A saída poderia ser canalizados para um arquivo diferente se isso funciona para o OP
Levon
1
isso definitivamente imprimiria "a ser anexado" e, em seguida, o conteúdo de "text.txt". Mas eu gostaria que o texto fosse anexado ao arquivo
One Two Three
1
e se eu tiver texto de várias linhas? Como insiro o caractere de nova linha?
One Two Three
Isso seria legal, mas o bash não gosta: $ echo RewriteBase / | cat - web / .htaccess> web / cat .htaccess: web / .htaccess: arquivo de entrada é lima de saída
geek-merlin
14

Prefira a resposta de Adam

Podemos facilitar o uso da esponja . Agora não precisamos criar um arquivo temporário e renomeá-lo por

echo -e "to be prepended \n another line" | cat - text.txt | sponge text.txt
ryan
fonte
essa foi a única solução que funcionou para mim. é engraçado o suficiente que o nome do meu servidor seja bob esponja.
Anmer An
9

Se for aceitável substituir o arquivo de entrada:

Nota:

  • Isso pode ter efeitos colaterais inesperados, principalmente o potencial de substituir um link simbólico por um arquivo normal, terminar com permissões diferentes no arquivo e alterar a data de criação (nascimento) do arquivo.

  • sed -i, como na resposta do príncipe John Wesley , tenta pelo menos restaurar as permissões originais, mas as outras limitações também se aplicam.

Aqui está uma alternativa simples que usa um arquivo temporário (evita a leitura de todo o arquivo de entrada na memória da maneira que a solução do shime faz):

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

Usar um comando de grupo ( { ...; ...; }) é um pouco mais eficiente do que usar um subshell ( (...; ...)), como na solução de 0xC0000022L .

As vantagens são:

  • É fácil controlar se o novo texto deve ser anexado diretamente à primeira linha ou se deve ser inserido como nova (s) linha (s) (basta anexar \nao printfargumento).

  • Diferentemente da sedsolução, ele funciona se o arquivo de entrada estiver vazio ( 0bytes).


A sedsolução pode ser simplificada se a intenção for anexar uma ou mais linhas inteiras ao conteúdo existente (assumindo que o arquivo de entrada não esteja vazio):

sedA ifunção de insere linhas inteiras:

Com o 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

Uma variante portátil que também funciona com o 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

Observe que a nova linha literal após o \é necessária.


Se o arquivo de entrada precisar ser editado no local (preservando seu inode com todos os seus atributos):

Usando o venerável edutilitário POSIX :

Nota:

  • edinvariavelmente lê primeiro o arquivo de entrada como um todo na memória.

Para anexar diretamente à primeira linha (como em sed, isso não funcionará se o arquivo de entrada estiver completamente vazio ( 0bytes)):

ed -s text.txt <<EOF
1 s/^/to be prepended/
w
EOF
  • -sedmensagens de status suprimidas .
  • Observe como os comandos são fornecidos edcomo um documento aqui com várias linhas ( <<EOF\n...\nEOF), ou seja, via stdin ; por padrão, a expansão de strings é executada nesses documentos (variáveis ​​de shell são interpoladas); cite o delimitador de abertura para suprimir isso (por exemplo, <<'EOF').
  • 1 torna a primeira linha a linha atual
  • A função sexecuta uma substituição de seqüência de caracteres baseada em regex na linha atual, como em sed; você pode incluir novas linhas literais no texto de substituição, mas elas devem ser \escapadas.
  • wgrava o resultado de volta no arquivo de entrada (para teste, substitua wpor ,ppara imprimir apenas o resultado, sem modificar o arquivo de entrada).

Para acrescentar uma ou mais linhas inteiras :

Assim como seda ifunção, invariavelmente, adiciona uma nova linha final ao texto a ser inserido.

ed -s text.txt <<EOF
0 i
line 1
line 2
.
w
EOF
  • 0 itorna 0(o início do arquivo) a linha atual e inicia o modo de inserção ( i); observe que os números de linha são 1baseados em outras coisas .
  • As linhas a seguir são o texto a ser inserido antes da linha atual, terminado com .em sua própria linha.
mklement0
fonte
7

Provavelmente nada embutido, mas você poderia escrever o seu com bastante facilidade, assim:

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

Algo assim pelo menos ...

Rob I
fonte
6
+1 por usar $$ (o PID atual) como parte do nome do arquivo temporário. Se você criar um script para uso geral, isso impedirá que outro usuário substitua potencialmente o arquivo temporário executando o mesmo script ao mesmo tempo.
E # Brown
3
O uso $$é insuficiente para o código de produção (ataque do symlink do google); mas certamente é melhor que um nome de arquivo estático.
Tripleee 14/05
1
apenas usemktemp
mewa 11/03/19
7

Em algumas circunstâncias, o texto anexado pode estar disponível apenas no stdin. Então essa combinação deve funcionar.

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

Se você deseja omitir a teesaída, acrescente > /dev/null.

sehari24jam
fonte
Eu realmente gosto desta resposta. É uma maneira muito limpa e clara de fazer isso. O uso de tee é uma solução natural para o problema de tentar extrair a saída para o arquivo de entrada. Também não há hacks com renomeação. Melhor do que a solução mais votada no momento, pois não cria um novo arquivo. >> Este é basicamente o mais próximo que você poderá >> de anexar em vez de anexá-lo.
DC2
Esta é de longe a melhor e mais limpa resposta.
Nerdoc 25/02
6
Existe uma garantia que teenão atrapalha text.txtantes de catconseguir lê-lo? Acho que não - o que tornaria essa solução perigosa para a saúde do arquivo.
Jonathan Leffler
3
@JonathanLeffler provavelmente não. Eu tentei este código para teste: 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. Se lenAfor 1000000 (arquivo de 1 Mb) e lenB10000 (prefixo de 10 KB de texto), o arquivo "a.txt" será substituído por 20 KB de letras "b". Isso está totalmente quebrado. Agora, se você usar 1Mb a.txt e 1Mb de texto para teeacrescentar , entra em um loop que gera um arquivo de 7Gb +, tive que interromper o comando. Portanto, é óbvio que o resultado é imprevisível para tamanhos grandes. Não tenho informações se deve funcionar em tamanhos pequenos.
VasiliNovikov
3
Para colocá-lo em termos conceituais: Este comando resultará em perda de dados se o arquivo de entrada for maior que o tamanho do buffer do pipeline do sistema , que normalmente é de 64 KB atualmente.
mklement0
7

Solução:

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

Observe que isso é seguro em todos os tipos de entradas, porque não há expansões. Por exemplo, se você deseja anexar !@#$%^&*()ugly text\n\t\n, funcionará apenas:

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

A última parte que resta para consideração é a remoção de espaço em branco no final do arquivo durante a substituição de comandos "$(cat file.txt)". Todas as soluções para isso são relativamente complexas. Se você deseja preservar as novas linhas no final do arquivo.txt, consulte: https://stackoverflow.com/a/22607352/1091436

VasiliNovikov
fonte
Adoro. Super portátil e não precisa gerar um novo arquivo. Obrigado!
pyb 29/03
1
O único problema com isso ocorre se o conteúdo de file.txtfor maior do que o que pode ser ajustado na lista de argumentos printf.
Jonathan Leffler
6

Outra maneira de usar sed:

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

Se a linha a ser anexada for multilinha:

sed -i.old '1 {i\ 
to be prepended\
multiline
}' inFile
Mert Nuhoglu
fonte
Por que não sed -i '1ito be prepended' inFile- ou é permitido apenas para o GNU sed?
usuário desconhecido
@userunknown: No padrão sed, o icomando é seguido por uma barra invertida e uma nova linha, e cada linha de entrada, exceto a última, também termina com uma barra invertida. O GNU sedpermite atalhos nas linhas que você pergunta, mas é apenas o GNU sedque o faz. '
Jonathan Leffler
3

Conforme testado no Bash (no Ubuntu), se iniciar com um arquivo de teste via;

echo "Original Line" > test_file.txt

você pode executar;

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

ou, se a versão do bash for muito antiga para $ (), você poderá usar backticks;

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

e receba o seguinte conteúdo de "test_file.txt";

New Line
Original Line

Nenhum arquivo intermediário, apenas bash / eco.

AlexanderESmith
fonte
2
melhor resposta, eu escrevi essa linha para rodar meu arquivo usando esta resposta: para i em $ (<file2.txt); echo "$ (echo $ i; cat arquivo.txt)"> arquivo.txt; feito;
Aurangzeb
2

Outra solução bastante direta é:

    $ echo -e "string\n" $(cat file)
Pat
fonte
Isso funcionou para mim ... apenas uma ligeira modificação, a fim de manter as multilinhas do arquivo de entrada, você deve colocá-lo entre aspas, algo assim: echo -e "string \ n" "$ (cat ~ / file.txt) "> ~ / arquivo.txt;
Craig Wayne
2
Não ter a catexpansão de parâmetro entre aspas duplas é uma boa razão para um voto negativo . Esse código está dividindo o conteúdo do arquivo em palavras e passando cada uma dessas palavras como um argumento separado para echo. Você perde os limites do argumento original, perde novas linhas / guias / etc., E cadeias de caracteres como \te \nno texto original são substituídas por guias e novas linhas em vez de serem mantidas como estavam.
26718 Charles Duffy
1

Se você gosta do vi / vim, este pode ser o seu estilo.

printf '0i\n%s\n.\nwq\n' prepend-text | ed file
Ian Kelling
fonte
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
Jim
fonte
0

Eu recomendo definir uma função e, em seguida, importar e usá-la quando necessário.

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
}

Em seguida, use-o assim:

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

O conteúdo do seu arquivo será:

This is second
This is first

Estou prestes a usar essa abordagem para implementar um atualizador de log de alterações.

AwesomeBobX64
fonte