Anche se questa è una vecchia domanda, mi sembra che sia una domanda perenne, ed è disponibile una soluzione più generale e più chiara di quanto sia stato suggerito finora. Credito dove è dovuto il credito: non sono sicuro che me lo sarei inventato senza considerare la menzione di Stéphane Chazelas <>
dell'operatore di aggiornamento.
L'apertura di un file per l'aggiornamento in una shell Bourne è di utilità limitata. La shell non ti dà modo di cercare un file e di impostare la sua nuova lunghezza (se inferiore a quella precedente). Ma questo è facilmente risolto, così facilmente sono sorpreso che non sia tra le utility standard in /usr/bin
.
Questo funziona:
$ grep -n foo T
8:foo
$ (exec 4<>T; grep foo T >&4 && ftruncate 4) && nl T;
1 foo
Allo stesso modo (punta del cappello a Stéphane):
$ { grep foo T && ftruncate; } 1<>T && nl T;
1 foo
(Sto usando GNU grep. Forse qualcosa è cambiato da quando ha scritto la sua risposta.)
Tranne che non hai / usr / bin / ftruncate . Per una dozzina di linee di C, puoi, vedi sotto. Questa utility ftruncate tronca un descrittore di file arbitrario a una lunghezza arbitraria, impostando automaticamente l'output standard e la posizione corrente.
Il comando sopra (primo esempio)
- apre il descrittore di file 4
T
per l'aggiornamento. Proprio come con open (2), l'apertura del file in questo modo posiziona l'offset corrente su 0.
- grep quindi elabora
T
normalmente e la shell reindirizza il proprio output T
tramite il descrittore 4.
- ftruncate chiama ftruncate (2) sul descrittore 4, impostando la lunghezza sul valore dell'offset corrente (esattamente dove grep lo ha lasciato).
La subshell quindi esce, chiudendo il descrittore 4. Ecco qui :
#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) non è portabile quando utilizzato in questo modo. Per una generalità assoluta, leggere l'ultimo byte scritto, riaprire il file O_WRONLY, cercare, scrivere il byte e chiudere.
Dato che la domanda ha 5 anni, sto per dire che questa soluzione è impercettibile. Sfrutta exec per aprire un nuovo descrittore e l' <>
operatore, entrambi arcani. Non riesco a pensare a un'utilità standard che manipola un inode dal descrittore di file. (La sintassi potrebbe essere ftruncate >&4
, ma non sono sicuro che sia un miglioramento.) È notevolmente più breve della risposta esplorativa competente di Camh. È solo un po 'più chiaro di Stéphane, IMO, a meno che non ti piaccia Perl più di me. Spero che qualcuno lo trovi utile.
Un modo diverso di fare la stessa cosa sarebbe una versione eseguibile di lseek (2) che riporta l'offset corrente; l'output potrebbe essere usato per / usr / bin / truncate , che alcuni Linuxi forniscono.