Chociaż jest to stare pytanie, wydaje mi się, że jest to pytanie odwieczne i dostępne jest bardziej ogólne, jaśniejsze rozwiązanie, niż dotychczas sugerowano. Kredyt tam, gdzie należny jest kredyt: nie jestem pewien, czy wymyśliłbym to bez wzmianki o Stéphane Chazelas o <>
operatorze aktualizacji.
Otwarcie pliku do aktualizacji w powłoce Bourne'a ma ograniczone zastosowanie. Powłoka nie umożliwia wyszukiwania pliku ani ustawiania jego nowej długości (jeśli jest krótsza niż stara). Ale łatwo to naprawić, więc jestem zaskoczony, że nie jest to standardowe narzędzie /usr/bin
.
To działa:
$ grep -n foo T
8:foo
$ (exec 4<>T; grep foo T >&4 && ftruncate 4) && nl T;
1 foo
Podobnie jak to (czapka dla Stéphane'a):
$ { grep foo T && ftruncate; } 1<>T && nl T;
1 foo
(Używam GNU grep. Być może coś się zmieniło, odkąd napisał swoją odpowiedź).
Tyle że nie masz / usr / bin / ftruncate . Aby zobaczyć kilkadziesiąt linii C, możesz zobaczyć poniżej. To narzędzie ftruncate obcina dowolny deskryptor pliku do dowolnej długości, domyślnie ustawiając standardowe wyjście i bieżącą pozycję.
Powyższe polecenie (pierwszy przykład)
- otwiera deskryptor pliku 4 w
T
celu aktualizacji. Podobnie jak w przypadku open (2), otwarcie pliku w ten sposób ustawia bieżące przesunięcie na 0.
- grep następnie przetwarza
T
normalnie, a powłoka przekierowuje swoje wyjście na T
deskryptor 4.
- ftruncate wywołuje ftruncate (2) na deskryptorze 4, ustawiając długość na wartość bieżącego przesunięcia (dokładnie tam, gdzie zostawił go grep ).
Następnie podpowłoka kończy działanie, zamykając deskryptor 4. Oto 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;
}
Uwaga: ftruncate (2) nie może być importowany, jeśli jest używany w ten sposób. Aby uzyskać absolutną ogólność, przeczytaj ostatni zapisany bajt, ponownie otwórz plik O_WRONLY, wyszukaj, zapisz bajt i zamknij.
Biorąc pod uwagę, że pytanie ma 5 lat, powiem, że to rozwiązanie jest nieoczywiste. Korzysta z exec, aby otworzyć nowy deskryptor, a <>
operator, oba są tajemne. Nie mogę wymyślić standardowego narzędzia, które manipuluje i-węzłem za pomocą deskryptora pliku. (Składnia może być ftruncate >&4
, ale nie jestem pewien, czy poprawa.) Jest znacznie krótsza niż kompetentna, eksploracyjna odpowiedź camh. Jest tylko trochę jaśniejszy niż Stéphane, IMO, chyba że bardziej lubisz Perla niż ja. Mam nadzieję, że ktoś uzna to za przydatne.
Innym sposobem na zrobienie tego samego byłaby wykonywalna wersja lseek (2), która zgłasza bieżące przesunięcie; wyjście może być wykorzystane do / usr / bin / truncate , które zapewniają niektóre Linuxi.