Auch wenn dies eine alte Frage ist, scheint es mir eine mehrjährige Frage zu sein, und es gibt eine allgemeinere, klarere Lösung als bisher vorgeschlagen. Kredit, bei dem der Kredit fällig ist: Ich bin mir nicht sicher, ob ich darauf gekommen wäre, ohne Stéphane Chazelas 'Erwähnung des <>
Update-Betreibers in Betracht zu ziehen .
Das Öffnen einer Datei zur Aktualisierung in einer Bourne-Shell ist von begrenztem Nutzen. Mit der Shell können Sie nicht nach einer Datei suchen und auch nicht die neue Länge festlegen (wenn sie kürzer als die alte ist). Aber das ist leicht zu beheben, so leicht bin ich überrascht, dass es nicht zu den Standard-Dienstprogrammen in gehört /usr/bin
.
Das funktioniert:
$ grep -n foo T
8:foo
$ (exec 4<>T; grep foo T >&4 && ftruncate 4) && nl T;
1 foo
Wie das geht (Hutspitze an Stéphane):
$ { grep foo T && ftruncate; } 1<>T && nl T;
1 foo
(Ich verwende GNU grep. Vielleicht hat sich etwas geändert, seit er seine Antwort geschrieben hat.)
Es sei denn, Sie haben kein / usr / bin / ftruncate . Ein paar Dutzend Zeilen von C können Sie unten sehen. Dieses Dienstprogramm ftruncate schneidet einen beliebigen Dateideskriptor auf eine beliebige Länge ab, wobei die Standardausgabe und die aktuelle Position standardmäßig verwendet werden.
Der obige Befehl (1. Beispiel)
- Öffnet den Dateideskriptor 4
T
zum Aktualisieren. Genau wie bei open (2) positioniert das Öffnen der Datei auf diese Weise den aktuellen Offset auf 0.
- grep wird dann
T
normal verarbeitet und die Shell leitet ihre Ausgabe T
über den Deskriptor 4 weiter.
- ftruncate ruft ftruncate (2) in Deskriptor 4 auf und setzt die Länge auf den Wert des aktuellen Offsets (genau dort, wo grep ihn gelassen hat).
Die Subshell wird dann beendet und der Deskriptor 4 geschlossen. Hier ist 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;
}
Hinweis: ftruncate (2) ist bei dieser Verwendung nicht portierbar. Um die absolute Allgemeinheit zu gewährleisten, lesen Sie das letzte geschriebene Byte, öffnen Sie die Datei O_WRONLY erneut, suchen Sie, schreiben Sie das Byte und schließen Sie sie.
Angesichts der Tatsache, dass die Frage 5 Jahre alt ist, werde ich sagen, dass diese Lösung nicht naheliegend ist. Es nutzt exec , um einen neuen Deskriptor und den <>
Operator zu öffnen, die beide geheimnisvoll sind. Ich kann nicht an ein Standarddienstprogramm denken, das einen Inode durch Dateideskriptor manipuliert. (Die Syntax könnte lauten ftruncate >&4
, aber ich bin mir nicht sicher, ob es eine Verbesserung gibt.) Sie ist erheblich kürzer als die kompetente, explorative Antwort von camh. Es ist nur ein bisschen klarer als das von Stéphane, IMO, es sei denn, Sie mögen Perl mehr als ich. Ich hoffe, jemand findet es nützlich.
Eine andere Möglichkeit wäre eine ausführbare Version von lseek (2), die den aktuellen Offset ausgibt. Die Ausgabe könnte für / usr / bin / truncate verwendet werden , was einige Linuxi bereitstellen.