Хоча це старе питання, мені здається, це питання багаторічне, і є більш загальне, чіткіше рішення, ніж було запропоновано дотепер. Кредит, у якому виплачується кредит: я не впевнений, що я б його придумав, не враховуючи згадки Стефана Шазеласа про <>
оператора оновлення.
Відкриття файлу для оновлення в оболонці 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 . Про кілька десятків рядків С ви можете подивитися нижче. Ця утиліта ftruncate обрізає довільний дескриптор файлу довільної довжини, дефолтуючи до стандартного виводу та поточного положення.
Наведена вище команда (1-й приклад)
- відкриває дескриптор файлів 4
T
для оновлення. Так само, як і у відкритому (2), відкриття файлу таким чином розміщує поточне зміщення у 0.
- Потім grep обробляється
T
нормально, і оболонка перенаправляє свій вихід на T
дескриптор 4.
- ftruncate виклики ftruncate (2) на дескрипторі 4, встановивши довжину до значення поточного зміщення (саме там, де залишився греп ).
Потім нижня частина корпусу виходить, закриваючи дескриптор 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 , щоб відкрити новий дескриптор, і <>
оператор, обидва з яких є таємними. Я не можу придумати стандартну утиліту, яка маніпулює inode дескриптором файлів. (Синтаксис міг бути ftruncate >&4
, але я не впевнений, що поліпшення.) Це значно коротше, ніж компетентний дослідницький відповідь Кем. Це просто трохи зрозуміліше, ніж Стефан, ІМО, якщо ви не любите Перла більше, ніж я. Сподіваюся, хтось вважає це корисним.
Іншим способом зробити те саме було б виконувана версія lseek (2), яка повідомляє про поточне зміщення; вихід може бути використаний для / usr / bin / усікання , який надають деякі Linuxi.