Embora essa seja uma pergunta antiga, parece-me uma pergunta perene, e uma solução mais geral e clara está disponível do que foi sugerido até agora. Crédito no momento em que o crédito é devido: não tenho certeza de que teria pensado nisso sem considerar a menção de Stéphane Chazelas ao <>
operador de atualização.
Abrir um arquivo para atualização em um shell Bourne é de utilidade limitada. O shell não oferece uma maneira de procurar em um arquivo, nem como definir seu novo tamanho (se menor que o antigo). Mas isso é facilmente remediado, tão facilmente que me surpreende que não esteja entre os utilitários padrão /usr/bin
.
Isso funciona:
$ grep -n foo T
8:foo
$ (exec 4<>T; grep foo T >&4 && ftruncate 4) && nl T;
1 foo
Como faz isso (dica de chapéu para Stéphane):
$ { grep foo T && ftruncate; } 1<>T && nl T;
1 foo
(Estou usando o GNU grep. Talvez algo tenha mudado desde que ele escreveu sua resposta.)
Exceto que você não possui / usr / bin / ftruncate . Para algumas dezenas de linhas de C, você pode ver abaixo. Esse utilitário ftruncate trunca um descritor de arquivo arbitrário para um comprimento arbitrário, padronizando a saída padrão e a posição atual.
O comando acima (1º exemplo)
- abre o descritor de arquivo 4
T
para atualização. Assim como em open (2), abrir o arquivo dessa maneira posiciona o deslocamento atual em 0.
- O grep processa
T
normalmente e o shell redireciona sua saída para o T
descritor 4.
- ftruncate chama ftruncate (2) no descritor 4, configurando o comprimento para o valor do deslocamento atual (exatamente onde grep o deixou).
O subshell então sai, fechando o descritor 4. Aqui está ftruncate :
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int
main( int argc, char *argv[] ) {
off_t i, fd=1, len=0;
off_t *addrs[2] = { &fd, &len };
for( i=0; i < argc-1; i++ ) {
if( sscanf(argv[i+1], "%lu", addrs[i]) < 1 ) {
err(EXIT_FAILURE, "could not parse %s as number", argv[i+1]);
}
}
if( argc < 3 && (len = lseek(fd, 0, SEEK_CUR)) == -1 ) {
err(EXIT_FAILURE, "could not ftell fd %d as number", (int)fd);
}
if( 0 != ftruncate((int)fd, len) ) {
err(EXIT_FAILURE, argc > 1? argv[1] : "stdout");
}
return EXIT_SUCCESS;
}
NB, ftruncate (2) não é portável quando usado dessa maneira. Para generalidade absoluta, leia o último byte escrito, reabra o arquivo O_WRONLY, procure, escreva o byte e feche.
Dado que a pergunta tem 5 anos, vou dizer que esta solução não é óbvia. Ele tira vantagem do exec para abrir um novo descritor e o <>
operador, ambos arcanos. Não consigo pensar em um utilitário padrão que manipule um inode pelo descritor de arquivo. (A sintaxe pode ser ftruncate >&4
, mas não tenho certeza de que haja uma melhoria.) É consideravelmente menor que a resposta exploratória e competente de camh. É um pouco mais claro que o de Stéphane, na IMO, a menos que você goste mais do Perl do que eu. Espero que alguém ache útil.
Uma maneira diferente de fazer a mesma coisa seria uma versão executável do lseek (2) que relata o deslocamento atual; a saída pode ser usada para / usr / bin / truncate , que alguns Linuxi fornecem.