Несмотря на то, что это старый вопрос, мне кажется, что это постоянный вопрос, и доступно более общее, более четкое решение, чем предполагалось до сих пор. Кредит, где кредит должен: я не уверен, что я придумал бы это без учета упоминания Стефана Шазеласа об <>
операторе обновлений.
Открытие файла для обновления в оболочке Bourne имеет ограниченную полезность. Оболочка не дает возможности искать файл и не может устанавливать его новую длину (если она короче старой). Но это легко исправить, так легко я удивляюсь, что он не входит в число стандартных утилит в /usr/bin
.
Это работает:
$ grep -n foo T
8:foo
$ (exec 4<>T; grep foo T >&4 && ftruncate 4) && nl T;
1 foo
Как это (шляпа Стефану):
$ { grep foo T && ftruncate; } 1<>T && nl T;
1 foo
(Я использую GNU grep. Возможно, что-то изменилось, так как он написал свой ответ.)
За исключением того, что у вас нет / usr / bin / ftruncate . Для пары десятков строк C, вы можете увидеть ниже. Эта утилита ftruncate усекает произвольный дескриптор файла до произвольной длины, по умолчанию используется стандартный вывод и текущая позиция.
Приведенная выше команда (1-й пример)
- открывает дескриптор файла 4
T
для обновления. Как и в случае с open (2), открытие файла таким образом устанавливает текущее смещение в 0.
- Затем grep обрабатывается
T
нормально, и оболочка перенаправляет свои выходные данные T
через дескриптор 4.
- ftruncate вызывает ftruncate (2) для дескриптора 4, устанавливая длину в значение текущего смещения (именно там, где его оставил grep ).
Затем подоболочка завершается, закрывая дескриптор 4. Вот 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;
}
Примечание: ftruncate (2) является непереносимым при использовании таким способом. Для абсолютной общности прочитайте последний записанный байт, снова откройте файл O_WRONLY, найдите, запишите байт и закройте.
Учитывая, что этому вопросу 5 лет, я собираюсь сказать, что это решение неочевидно. Для открытия нового дескриптора используется exec , а <>
оператор - оба являются загадочными. Я не могу вспомнить стандартную утилиту, которая манипулирует индексом по дескриптору файла. (Синтаксис может быть ftruncate >&4
, но я не уверен, что это улучшение.) Это значительно короче, чем компетентный, исследовательский ответ Camh. Это немного яснее, чем у Стефана, ИМО, если вы не любите Perl больше, чем я. Я надеюсь, что кто-то найдет это полезным.
Другим способом сделать то же самое может быть исполняемая версия lseek (2), которая сообщает о текущем смещении; вывод может быть использован для / usr / bin / truncate , который предоставляют некоторые Linuxi.