Existe um comando Unix para acrescentar alguns dados de string a um arquivo de texto?
Algo como:
prepend "to be prepended" text.txt
<<(echo "to be prepended") < text.txt | sponge text.txt
Existe um comando Unix para acrescentar alguns dados de string a um arquivo de texto?
Algo como:
prepend "to be prepended" text.txt
<<(echo "to be prepended") < text.txt | sponge text.txt
Respostas:
sed -i.old '1s;^;to be prepended;' inFile
-i
grava 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.\n
depois de pré-anexado; dependendo de suas necessidades. Solução pura.
inFile
incluir diretórios em seu caminho?
\n
. Deveria ter lido os comentários primeiro. Droga.
'1s;^;my-prepended-line\;\n;'
printf '%s\n%s\n' "to be prepended" "$(cat text.txt)" >text.txt
git config --get-regex $arg | sed -r 's/\t{3}/\\n/g';
e isso atrapalha quando converte o \t
e \n
.
echo "to be prepended"$'\n'"$(cat text.txt)"
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 sponge
você 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
Usando uma string here - <<<
, você pode:
<<< "to be prepended" < text.txt | sponge text.txt
<<(echo "to be prepended") < text.txt
e <<< "to be prepended" < text.txt
não funcionam no bash; eles exigem zsh.
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
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.
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
text.txt
conforme solicitado, mas o exibe no stdout. A saída poderia ser canalizados para um arquivo diferente se isso funciona para o OP
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
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 \n
ao printf
argumento).
Diferentemente da sed
solução, ele funciona se o arquivo de entrada estiver vazio ( 0
bytes).
A sed
soluçã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):
sed
A i
funçã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.
Usando o venerável ed
utilitário POSIX :
Nota:
ed
invariavelmente 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 ( 0
bytes)):
ed -s text.txt <<EOF
1 s/^/to be prepended/
w
EOF
-s
ed
mensagens de status suprimidas .ed
como 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 atuals
executa 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.w
grava o resultado de volta no arquivo de entrada (para teste, substitua w
por ,p
para imprimir apenas o resultado, sem modificar o arquivo de entrada).Para acrescentar uma ou mais linhas inteiras :
Assim como sed
a i
funçã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 i
torna 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 1
baseados em outras coisas ..
em sua própria linha.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 ...
$$
é insuficiente para o código de produção (ataque do symlink do google); mas certamente é melhor que um nome de arquivo estático.
mktemp
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 tee
saída, acrescente > /dev/null
.
tee
não atrapalha text.txt
antes de cat
conseguir lê-lo? Acho que não - o que tornaria essa solução perigosa para a saúde do arquivo.
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 lenA
for 1000000 (arquivo de 1 Mb) e lenB
10000 (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 tee
acrescentar , 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.
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
file.txt
for maior do que o que pode ser ajustado na lista de argumentos printf
.
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
sed -i '1ito be prepended' inFile
- ou é permitido apenas para o GNU sed?
sed
, o i
comando é 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 sed
permite atalhos nas linhas que você pergunta, mas é apenas o GNU sed
que o faz. '
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.
Outra solução bastante direta é:
$ echo -e "string\n" $(cat file)
cat
expansã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 \t
e \n
no texto original são substituídas por guias e novas linhas em vez de serem mantidas como estavam.
Se você gosta do vi / vim, este pode ser o seu estilo.
printf '0i\n%s\n.\nwq\n' prepend-text | ed file
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.