Você precisa da permissão de gravação em um diretório para criar ou remover arquivos, mas não para gravar em um arquivo. A maioria dos comandos shell, quando recebe um arquivo de saída, simplesmente abre esse arquivo para gravação e substitui os dados que estavam anteriormente no arquivo. O operador de redirecionamento >
trunca o arquivo existente (isto é, exclui o conteúdo do arquivo existente, resultando em um arquivo com tamanho zero) e começa a gravar nele. O operador de redirecionamento >>
faz com que os dados sejam anexados ao final do arquivo.
Escrever em arquivos é limitado pelas possibilidades oferecidas pela interface de baixo nível. Você pode sobrescrever bytes em um arquivo no lugar, anexar ao final do arquivo e truncar um arquivo para um tamanho escolhido. Não é possível inserir bytes ao alternar bytes subseqüentes (como em foobar
→ fooNEWSTUFFbar
) nem excluir bytes enquanto alterna bytes subseqüentes para trás (como em foobar
→ for
), exceto simulando essas operações lendo os dados para mover e escrever no seu novo local.
O problema com a edição de arquivos é que é difícil garantir que o conteúdo do arquivo permaneça consistente se algo der errado (bug do programa, disco cheio, perda de energia,…). É por isso que o processamento robusto de arquivos geralmente envolve a gravação de um arquivo temporário com os novos dados e a movimentação do arquivo temporário.
Outra limitação na edição de arquivos é que operações complexas envolvendo leituras e gravações não são atômicas. Este é um problema apenas se alguma outra tarefa desejar ler o arquivo ao mesmo tempo que sua modificação. Por exemplo, se você alterar foobar
para fooNEWSTUFFbar
lendo os últimos 3 bytes ( bar
), em seguida, gravando os novos dados ( foo
→ fooNEWSTUFF
) e, finalmente, anexando a cauda antiga ( fooNEWSTUFF
→ fooNEWSTUFFbar
), um leitor concorrente pode ver fooNEWSTUFF
ou até mesmo outros estados parciais do arquivo com apenas parte dos dados gravados. Novamente, gravar em um arquivo temporário primeiro resolve esse problema, porque mover o arquivo temporário no lugar é uma operação atômica.
Se você não se importa com essas limitações, pode modificar um arquivo no lugar. Anexar dados é fácil ( >>
), a maioria das outras transformações estão mais envolvidas. Uma armadilha comum é que
somefilter <somefile >somefile
NÃO aplica somefilter
ao conteúdo do arquivo: o operador >
trunca o arquivo de saída antes de somefilter
começar a ler a partir dele.
moreutils de Joey Hess contém um utilitário chamado sponge
que corrige este problema. Em vez de redirecionar a saída para somefile
, você canaliza para sponge
, que lê todas as suas entradas e então substitui o arquivo existente pela entrada que foi lida. Observe que ainda é possível acabar com dados parciais se o filtro falhar.
somefilter <somefile | sponge somefile
Se você não tiver sponge
, a maneira fácil e portátil de corrigir isso é primeiro ler os dados na memória:
content=$(cat somefile; echo a)
content=${content%a}
O echo a
bit é para preservar as novas linhas no final do arquivo - a substituição de comandos sempre remove as novas linhas. Você pode então passar o conteúdo para um comando:
printf %s "$content" | somefilter >somefile
Isso substitui o conteúdo do arquivo pela saída do filtro. Se o comando falhar por algum motivo, o conteúdo original do arquivo é perdido e o arquivo contém os dados que o comando escreveu antes de falhar.
Tenha em atenção que este método não funciona para ficheiros binários, porque a maioria das shells não suporta bytes nulos.
Outra maneira de modificar um arquivo é usar o editor ed
, que tem uma strong semelhança com sed
, mas carrega o arquivo na memória e o salva no lugar, ao contrário da operação linha por linha do sed.
Atuar em um arquivo sem carregá-lo na memória e sem criar um arquivo temporário é mais complicado apenas com ferramentas shell padrão, mas isso pode ser feito. O redirecionamento de shell e a maioria dos utilitários de processamento de texto permitem que você acrescente a um arquivo ou sobrescreva-o desde o início. Você pode usar dd conv=notrunc seek=…
para sobrescrever dados em algum deslocamento em um arquivo sem afetar as partes que não estão sendo sobrescritas; veja Existe uma maneira de modificar um arquivo no local? para um exemplo.