Como faço para adicionar texto ao início de um arquivo no Bash?

245

Oi eu quero prefixar o texto em um arquivo. Por exemplo, quero adicionar tarefas ao início de um arquivo todo.txt. Estou ciente echo 'task goes here' >> todo.txt mas isso adiciona a linha ao final do arquivo (não o que eu quero).

user479534
fonte
2
Não há uma maneira eficiente de fazer isso para arquivos grandes: unix.stackexchange.com/questions/87772/…
Ciro Santilli 新疆改造中心 六四事件 法轮功
@ você deve dar uma olhada neste método superuser.com/a/1352628/11116
Evan Carroll

Respostas:

330
echo 'task goes here' | cat - todo.txt > temp && mv temp todo.txt

ou

sed -i '1s/^/task goes here\n/' todo.txt

ou

sed -i '1itask goes here' todo.txt
Dennis Williamson
fonte
4
o primeiro funciona muito bem! você se importaria de explicar a lógica? Não estou particularmente certo como interpretar a sintaxe.
user479534
36
@ user8347: Tubulação ( | ) a mensagem ( echo '...' ) para cat que usa - (entrada padrão) como o primeiro arquivo e todo.txt como o segundo. cat CONCATENA vários arquivos. Envie a saída ( > ) para um arquivo chamado temp. Se não houver erros ( && ) de cat depois renomeie mv ) a temp arquivo de volta para o arquivo original ( todo.txt ).
Dennis Williamson
1
Eu não sabia sobre essa sintaxe possível no último exemplo do sed, alguém pode me dizer qual é o nome desse tipo de sintaxe para que eu possa pesquisar mais sobre isso?
Kira
3
@ Kira: O 1 significa fazer o próximo comando apenas na linha um do arquivo e o i comando é inserir. Procure na página man sob a seção "Addresses" e na seção "Zero-or One-address commands".
Dennis Williamson
1
@Terrance: Se o seu sed comando é algo como isto (inserindo uma guia principal): '1i\tSome Text', você pode precisar dobrar a barra invertida. Caso contrário, seu dialeto de sed pode não reconhecer \t como guia.
Dennis Williamson
62

Uma opção mais simples na minha opinião é:

echo -e "task goes here\n$(cat todo.txt)" > todo.txt

Isso funciona porque o comando dentro de $(...) é executado antes todo.txt é sobrescrito com > todo.txt

Enquanto as outras respostas funcionam bem, acho isso muito mais fácil de lembrar, porque eu uso eco e gato todos os dias.

EDITAR: Esta solução é uma péssima ideia se houver alguma barra invertida todo.txtporque graças ao -e O eco da bandeira irá interpretá-los. Outra maneira muito mais fácil de obter novas linhas na string de prefácio é ...

echo "task goes here
$(cat todo.txt)" > todo.txt

... simplesmente para usar novas linhas. Claro, não é mais uma linha única, mas realisticamente também não era uma linha direta. Se você estiver fazendo isso dentro de um script e estiver preocupado com o recuo (por exemplo, se estiver executando isso dentro de uma função), há algumas soluções alternativas para que isso ainda se ajuste bem, incluindo, mas não se limitando a:

(echo 'task goes here' && cat todo.txt) > todo.txt
echo 'task goes here'$'\n'"$(cat todo.txt)" > todo.txt

Além disso, se você se importa se uma nova linha é adicionada ao final de todo.txt, não use isso. Bem, exceto o penúltimo. Isso não atrapalha no final.

John Alberts
fonte
1
Eu não recebo $ (...) executado.
SCL
2
Isso pode funcionar melhor (ou nada) com aspas duplas em vez de simples…
ℝaphink
5
Não vai o -e também converter seqüências de escape dentro todo.txt?
mk12
2
Soluções alternativas geram - & gt; cat: & lt; destination filename & gt ;: arquivo de entrada é o arquivo de saída ...
ingyhere
printf seria muito mais consistente em todas as plataformas e geralmente deveria funcionar mais suavemente do que echo -e
Peter Berg
28

o moreutils tem uma boa ferramenta chamada sponge:

echo "task goes here" | cat - todo.txt | sponge todo.txt

Ele "absorverá" STDIN e, em seguida, gravará no arquivo, o que significa que você não precisa se preocupar com arquivos temporários e movê-los.

Você pode ter moreutils com muitas distribuições do Linux, apt-get install moreutils, ou no OS X usando Homebrew com brew install moreutils.

slhck
fonte
Eu iria para (echo 'ble'; cat todo.txt) :-)
Ciro Santilli 新疆改造中心 六四事件 法轮功
6

Você pode usar o Vim no modo Ex:

ex -s -c '1i|task goes here' -c x todo.txt
  1. 1 selecione a primeira linha

  2. i inserir

  3. x salvar e fechar

Steven Penny
fonte
4

Você pode criar um novo arquivo temporário.

echo "new task" > new_todo.txt
cat todo.txt >> new_todo.txt
rm todo.txt
mv new_todo.txt todo.txt

Você também pode usar sed ou awk. Mas basicamente a mesma coisa acontece.

Keith
fonte
1
Diga que você está sem espaço em disco para que new_todo.txt fica escrito apenas parcialmente. Sua solução parece perder o arquivo original.
NPE
Quem fica sem espaço em disco? ;-) É apenas um exemplo simples.
Keith
1
@ Keith Alguém trabalhando em uma VM que não esperava precisar de uma unidade virtual particularmente grande. Ou alguém movendo um arquivo grande. Em qualquer caso, o argumento real contra isso é permissões de diretório; se você não tiver permissão para criar novos arquivos no diretório fornecido, o único comando que será executado com sucesso no seu script é o rm do arquivo original.
Parthian Shot
3

Se o arquivo de texto for pequeno o suficiente para caber na memória, você não precisará criar um arquivo temporário para substituí-lo. Você pode carregar tudo na memória e escrevê-lo de volta no arquivo.

echo "$(echo 'task goes here' | cat - todo.txt)" > todo.txt

É impossível adicionar linhas ao início do arquivo sem gravar o arquivo inteiro.

Rucent88
fonte
Apenas para fazer a pergunta óbvia: Onde está o limite de caracteres das variáveis ​​da shell?
nixda
Tanto quanto eu sei, é limitado apenas pela quantidade de memória disponível. Eu preenchi variáveis ​​bem mais de 100MB na memória. text=$(cat file). Tenha cuidado para usar apenas o texto, porque as variáveis ​​do shell não são binárias mywiki.wooledge.org/BashFAQ/058
Rucent88
2

Você não pode inserir conteúdo no início de um arquivo. A única coisa que você pode fazer é substituir o conteúdo existente ou anexar bytes após o término atual do arquivo.

Qualquer solução para sua pergunta, então, requer que um arquivo temporário (ou buffer) seja criado (na memória ou no disco), o que acabará substituindo o arquivo original.

Cuidado para não perder dados preservando o arquivo original enquanto cria o novo, caso o sistema de arquivos esteja cheio durante o processo. por exemplo:

cat <(echo task go there) todo.txt > todo.txt.new && mv todo.txt.new todo.txt
jlliagre
fonte
Downvoters são bem-vindos para explicar sua motivação. Nenhuma das respostas restantes, incluindo a aceita, contradiz qualquer coisa na minha resposta.
jlliagre
Isto é difícil de analisar como o & lt; ... & gt; parecem suportes, que eu suponho que não são. Um espaço entre o & lt; e o (pode ajudar?
dumbledad
Isso não está funcionando para mim. echo HOME=\"/g/Users/timregan/\" | cat - 'F:\Program Files\Git\etc\profile' funciona mas cat <echo HOME=\"/g/Users/timregan/\" 'F:\Program Files\Git\etc\profile' dá o erro "echo: Nenhum tal arquivo ou diretório"
dumbledad
@dumbledad Você está pensando demais na minha resposta. Não há nada para você analisar. Um espaço entre o < e a ( iria quebrar a sintaxe. Experimentar cat <(echo HOME=\"/g/Users/timregan/\") 'F:\Program Files\Git\etc\profile'
jlliagre
0

Você pode usar tee:

echo 'task goes here' | cat - todo.txt | tee todo.txt
Radu Rădeanu
fonte
3
Não, você não pode askubuntu.com/a/752451
Steven Penny
0

Git Bash + Windows10 + Multilinha :

Aqui está uma versão que permite usar seqüências de várias linhas .

##############################################
## This section for demo purpose only,      ##
## So you can save entire file as           ##
## whatever.sh and run it.                  ##
##                                          ##
##############################################
> MY_TARGET_FILE.txt ##Make Or Clear File
echo "[STARTER_CONTENT]" >> MY_TARGET_FILE.txt
##############################################

## Below is main code:

##################################################
TARGET_FILE_VARIABLE="MY_TARGET_FILE.txt"
ADD_TO_HEAD_VARIABLE=$(cat << "HEREDOC_HEAD_TEXT"
//|  +-------------------------------------+   |//
//|  |                                     |   |//
//|  |     MESSAGE_FOR_HEAD_OF_FILE        |   |//
//|  |                                     |   |//
//|  +-------------------------------------+   |//
HEREDOC_HEAD_TEXT
)
##################################################
TAR=$TARGET_FILE_VARIABLE                       ##
TEX=$ADD_TO_HEAD_VARIABLE                       ##
echo "$TEX" | cat - $TAR > TEMP && mv TEMP $TAR ##
##################################################

## Expected contents of MY_TARGET_FILE.txt :
## //|  +-------------------------------------+   |//
## //|  |                                     |   |//
## //|  |     MESSAGE_FOR_HEAD_OF_FILE        |   |//
## //|  |                                     |   |//
## //|  +-------------------------------------+   |//
## [STARTER_CONTENT]
J MADISON
fonte