Hoje eu estava tentando editar rapidamente um .hgignore
arquivo do shell bash do Cygwin e adicionei uma linha que era um erro. Não tenho certeza se essa era a melhor maneira de fazê-lo, mas rapidamente pensei em usar head -1 .hgignore
para remover a linha incorreta (anteriormente eu só tinha uma linha no arquivo). Com certeza, quando executado, fornece a primeira linha como a única saída.
Mas quando tentei redirecionar a saída e reescrever o arquivo usando head -1 .hgignore > .hgignore
, o arquivo estava vazio. Por que isso acontece? Se eu tentar acrescentar head -1 .hgignore >> .hgignore
, ele será anexado corretamente, mas esse obviamente não é o resultado desejado. Por que um redirecionamento truncado não funciona nesse caso?
shell
io-redirection
voithos
fonte
fonte
cut
alterar um arquivo no local? , Como posso fazer com que o iconv substitua o arquivo de entrada pela saída convertida?Respostas:
Quando o shell obtém uma linha de comando como:
command > file.out
o próprio shell abre (e talvez crie) o arquivo chamadofile.out
. O shell define o descritor de arquivo 0 para o descritor de arquivo obtido desde o início. É assim que o redirecionamento de E / S funciona: todo processo conhece os descritores de arquivo 0, 1 e 2.A parte mais difícil disso é como abrir
file.out
. Na maioria das vezes, você desejafile.out
abrir para gravação no deslocamento 0 (ou seja, truncado) e é isso que o shell fez por você. Ele truncou .hgignore, abriu-o para gravação, duplicou o filedescriptor para 0 e depois executouhead
. Ficheiros instantâneos.No shell bash, você faz um
set noclobber
para alterar esse comportamento.fonte
Eu acho que Bruce responde o que está acontecendo aqui com o oleoduto.
Um dos meus pequenos utilitários favoritos é o
sponge
comando do moreutils . Ele resolve exatamente esse problema "absorvendo" toda a entrada disponível antes de abrir o arquivo de saída de destino e gravar os dados. Ele permite que você escreva pipelines exatamente como você esperava:A solução do pobre homem é canalizar a saída para um arquivo temporário; depois que o pipline for concluído (por exemplo, o próximo comando que você executar), será movido o arquivo temporário de volta ao local original do arquivo.
fonte
head -1 .hgignore | tee .hgignore
?tee
está em coreutils, e como um efeito colateral regalia /, este também escreve para STDOUTtee
abre e trunca o arquivo para o qual está gravando quando é instanciado, como todo o resto, para que não resolva o problema principal da condição de corrida na leitura do conteúdo do arquivo antes de você truncá-lo com a gravação.tee
parece fazer a coisa desejada. Eu tenho versão8.13
na minha máquina.tee
programa truncará seus arquivos, não está configurado para duplicá-los.No
file
é truncado antes dehead
ser iniciado, mas se você o escrever:não
file
é como é aberto no modo de leitura e gravação. No entanto, quandohead
termina a gravação, ele não trunca o arquivo, portanto a linha acima não funcionaria (head
apenas reescreveria a primeira linha sobre si mesma e deixaria as outras intocadas).No entanto, após o
head
retorno e enquanto ofd
ainda estiver aberto, você pode chamar outro comando que faça otruncate
.Por exemplo:
O que importa aqui é que
truncate
acima,head
apenas move o cursor para fd 1 dentro do arquivo logo após a primeira linha. Ele reescreve a primeira linha da qual não precisamos, mas isso não é prejudicial.Com uma cabeça POSIX, poderíamos fugir sem reescrever a primeira linha:
Aqui, estamos usando o fato de
head
mover a posição do cursor em seu stdin. Emborahead
normalmente lesse sua entrada por grandes partes para melhorar o desempenho, o POSIX exigiria (sempre que possível) queseek
retornasse logo após a primeira linha, se a ultrapassasse. Observe, no entanto, que nem todas as implementações fazem isso.Como alternativa, você pode usar o
read
comando do shell neste caso:fonte
STDIN
semelhante ao que você realizou usandoperl
acimadd
pode truncar em qualquer deslocamento absoluto arbitrário no arquivo. Assim, você pode determinar o deslocamento da segunda linha e truncado de lá com bytedd bs=1 seek="$offset" of=file
A solução do homem real é
ou como one-liner
Ou com o GNU sed:
(Não, estou brincando. Eu usaria um editor interativo.
vi .hgignore
GddZZ
)fonte
:wq
maisZZ
?:x
que é o que os meus dedos fazer automaticamenteZQ
é o mesmo que:q!
Você pode usar o Vim no modo Ex:
2,
selecione as linhas 2 até o finald
excluirx
salvar e fecharfonte
Para edição de arquivo no local, você também pode usar o truque de manipulação de arquivo aberto, como mostra Jürgen Hötzel na saída Redirect do sed 's / c / d /' myFile para o myFile .
fonte
rm .hgignore
seu poder falhar, gastando horas de trabalho duro. Ok, não importa.hgignore
, mas por que você faria algo tão complicado assim? Assim, meu voto negativo: tecnicamente correto, mas uma péssima idéia.perl -i
(para edição local) faz, e não ficaria surpreso se algumas implementaçõessed -i
fizessem isso também (embora a versão mais recente do GNUsed
pareça não).