Сначала необходимо подключиться, AFAIK CentOS 6 минимальный набор сетевое устройство к ONBOOT=No
, просто сделайте a dhclient
с административными привилегиями к Вашему сетевому интерфейсу и необходимо быть в порядке:
$ sudo dhclient
На уровне системного вызова это должно быть возможно. Программа может открыть Ваш конечный файл для записи, не усекая его и начать писать то, что это читает из stdin. При чтении EOF выходной файл может быть усеченным.
Так как Вы фильтруете строки от входа, положение записи выходного файла должно всегда быть меньше, чем положение чтения. Это означает, что Вы не должны повреждать свой вход с новым выводом.
Однако находя программу, которая, это - проблема. dd(1)
имеет опцию conv=notrunc
это не усекает выходной файл на открытом, но он также не усекает в конце, оставляя исходное содержание файла после grep содержания (с командой как grep pattern bigfile | dd of=bigfile conv=notrunc
)
Так как это очень просто с точки зрения системного вызова, я записал небольшую программу и протестировал ее в маленькой полной петлевой файловой системе (на 1 МиБ). Это сделало то, что Вы хотели, но Вы действительно хотите протестировать это с некоторыми другими файлами сначала. Это всегда будет опасной перезаписью файла.
overwrite.c
/* This code is placed in the public domain by camh */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
int main(int argc, char **argv)
{
int outfd;
char buf[1024];
int nread;
off_t file_length;
if (argc != 2) {
fprintf(stderr, "usage: %s <output_file>\n", argv[0]);
exit(1);
}
if ((outfd = open(argv[1], O_WRONLY)) == -1) {
perror("Could not open output file");
exit(2);
}
while ((nread = read(0, buf, sizeof(buf))) > 0) {
if (write(outfd, buf, nread) == -1) {
perror("Could not write to output file");
exit(4);
}
}
if (nread == -1) {
perror("Could not read from stdin");
exit(3);
}
if ((file_length = lseek(outfd, 0, SEEK_CUR)) == (off_t)-1) {
perror("Could not get file position");
exit(5);
}
if (ftruncate(outfd, file_length) == -1) {
perror("Could not truncate file");
exit(6);
}
close(outfd);
exit(0);
}
Вы использовали бы его как:
grep pattern bigfile | overwrite bigfile
Я главным образом отправляю это, чтобы другие прокомментировали перед попыткой его. Возможно, кто-то еще знает о программе, которая делает что-то подобное, которое более тестируется.
Можно использовать sed
для редактирования файлов на месте (но это действительно создает промежуточный временный файл):
Удалить все строки, содержащие foo
:
sed -i '/foo/d' myfile
Сохранять все строки, содержащие foo
:
sed -i '/foo/!d' myfile
$HOME
будет перезаписываемо, но /tmp
будет только для чтения (по умолчанию). Например, если у Вас есть Ubuntu, и Вы загрузились в Консоль восстановления, это обычно имеет место. Кроме того, оператор здесь-документа <<<
не будет работать там также, как это требует /tmp
быть r/w, потому что это впишет во временный файл там также. (cf. этот вопрос, включая a strace
'd вывод)
– syntaxerror
03.12.2014, 16:36
Я предположу, что Ваша команда фильтрации - то, что я назову фильтром уменьшения префикса, который имеет свойство, что байт N в выводе никогда не пишется прежде считал, по крайней мере, N байты входа. grep
имеет это свойство (как долго, поскольку оно только фильтрует и не делает другие вещи как добавление номеров строки для соответствий). С таким фильтром можно перезаписать вход, как Вы продвигаетесь. Конечно, необходимо быть уверены в не делании любой ошибки, так как перезаписанная часть в начале файла будет потеряна навсегда.
Большинство инструментов Unix только дает выбор добавления в файл или усечения его без возможности перезаписи его. Одно исключение на стандартной панели инструментов dd
, которому можно сказать не усечь его выходной файл. Таким образом, план состоит в том, чтобы проникнуть команду в dd conv=notrunc
. Это не изменяет размер файла, таким образом, мы также захватываем длину нового содержания и усекаем файл к той длине (снова с dd
). Обратите внимание, что эта задача по сути неустойчива — если ошибка происходит, Вы самостоятельно.
export LC_ALL=C
n=$({ grep -v foo <big_file |
tee /dev/fd/3 |
dd of=big_file conv=notrunc; } 3>&1 | wc -c)
dd if=/dev/null of=big_file bs=1 seek=$n
Можно записать примерно эквивалентный Perl. Вот быстрая реализация, которая не пытается быть эффективной. Конечно, можно хотеть сделать начальную букву, фильтрующую непосредственно на том языке также.
grep -v foo <big_file | perl -e '
close STDOUT;
open STDOUT, "+<", $ARGV[0] or die;
while (<STDIN>) {print}
truncate STDOUT, tell STDOUT or die
' big_file
С любой оболочкой типа Борна:
{
cat < bigfile | grep -v to-exclude
perl -e 'truncate STDOUT, tell STDOUT'
} 1<> bigfile
По какой-то причине кажется, что люди склонны забывать об этом 40-летнем¹ и стандартном операторе перенаправления чтения+записи.
Мы открываем bigfile
в режиме чтение+запись и (что здесь наиболее важно) без усечения на stdout
, в то время как bigfile
открывается (отдельно) на cat
stdin
. После того, как grep
закончился, и если он удалил несколько линий, stdout
теперь указывает где-то в пределах bigfile
, нам нужно избавиться от того, что находится за этой точкой. Поэтому команда perl
, которая усекает файл (усекает STDOUT
) в текущей позиции (как возвращается командой tell STDOUT
).
(параметр cat
предназначен для GNU grep
, который в противном случае будет жаловаться, если stdin и stdout указывают на один и тот же файл).
¹ Ну, хотя <>
находилась в оболочке Борна с самого начала в конце семидесятых годов, первоначально она была недокументирована и не была реализована должным образом . Он не был в первоначальной реализации ash
с 1989 года и, хотя это POSIX sh
оператор перенаправления (с начала 90-х годов, так как POSIX sh
основан на ksh88
, который всегда им обладал), например, она не добавлялась во FreeBSD sh
до 2000 года, так что портативно 15-летняя , вероятно, более точна. Также обратите внимание, что дескриптор файла по умолчанию, когда он не указан, это <>
во всех оболочках, за исключением того, что в ksh93
в 2010 году он изменился с 0 до 1 в ksh93t+ (нарушение обратной совместимости и соответствия POSIX)
Хотя это старый вопрос, мне кажется, что это вечный вопрос, и доступно более общее и более четкое решение, чем предлагалось до сих пор . Кредит, если необходимо: я не уверен, что придумал бы его, не учитывая упоминание Стефана Шазеласа об операторе обновления <>
.
Открытие файла для обновления в оболочке 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-й пример)
T
для обновления. Как и в случае с open (2), при открытии файла текущее смещение устанавливается на 0. T
, а оболочка перенаправляет свой вывод на T
через дескриптор 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 для открытия нового дескриптора и оператора <>
, оба из которых являются загадочными. Я не могу придумать стандартную утилиту, которая манипулирует индексом дескриптора файла. (Синтаксис может быть ftruncate> & 4
, но я не уверен, что это улучшение.) Это значительно короче, чем компетентный исследовательский ответ camh. Это просто немного яснее, чем у Стефана, ИМО, если только вам не нравится Perl больше, чем мне. Надеюсь, кому-то это пригодится.
Другой способ сделать то же самое - исполняемая версия lseek (2), которая сообщает текущее смещение; вывод может быть использован для / usr / bin / truncate , который предоставляют некоторые Linuxi.
ed
вероятно, правильный выбор отредактировать оперативный файл:
ed my_big_file << END_OF_ED_COMMANDS
g/foo:/d
w
q
END_OF_ED_COMMANDS
ed
версии ведут себя по-другому....., это от man ed
(GNU Ed 1.4)... If invoked with a file argument, then a copy of file is read into the editor's buffer. Changes are made to this copy and not directly to file itself.
– Peter.O
14.04.2011, 20:34
ed
не gool решение для редактирования файлов на 35 ГБ, так как файл читается в буфер.
– glenn jackman
14.04.2011, 22:07
!
), таким образом, это может иметь несколько более интересных приемов его рукав.
– Peter.O
14.04.2011, 23:49
ed
усекает файл и переписывает его. Таким образом, это не изменит данные по диску, оперативному как требования OP. Кроме того, это не может работать, если файл является слишком большим, чтобы быть загруженным в памяти.
– Nick Matteo
14.04.2017, 20:08
Можно использовать дескриптор файла чтения-записи удара для открытия файла (для перезаписи его на месте), затем sed
и truncate
... но конечно, никогда не позволяйте Вашим изменениям быть больше, чем чтение объема данных до сих пор.
Вот сценарий (использование: колотите переменный $BASHPID),
# Create a test file
echo "going abc" >junk
echo "going def" >>junk
echo "# ORIGINAL file";cat junk |tee >( wc=($(wc)); echo "# ${wc[0]} lines, ${wc[2]} bytes" ;echo )
#
# Assign file to fd 3, and open it r/w
exec 3<> junk
#
# Choose a unique filename to hold the new file size and the pid
# of the semi-asynchrounous process to which 'tee' streams the new file..
[[ ! -d "/tmp/$USER" ]] && mkdir "/tmp/$USER"
f_pid_size="/tmp/$USER/pid_size.$(date '+%N')" # %N is a GNU extension: nanoseconds
[[ -f "$f_pid_size" ]] && { echo "ERROR: Work file already exists: '$f_pid_size'" ;exit 1 ; }
#
# run 'sed' output to 'tee' ...
# to modify the file in-situ, and to count the bytes
<junk sed -e "s/going //" |tee >(echo -n "$BASHPID " >"$f_pid_size" ;wc -c >>"$f_pid_size") >&3
#
#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
# The byte-counting process is not a child-process,
# so 'wait' doesn't work... but wait we must...
pid_size=($(cat "$f_pid_size")) ;pid=${pid_size[0]}
# $f_pid_size may initially contain only the pid...
# get the size when pid termination is assured
while [[ "$pid" != "" ]] ; do
if ! kill -0 "$pid" 2>/dev/null; then
pid="" # pid has terminated. get the byte count
pid_size=($(cat "$f_pid_size")) ;size=${pid_size[1]}
fi
done
rm "$f_pid_size"
#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
#
exec 3>&- # close fd 3.
newsize=$(cat newsize)
echo "# MODIFIED file (before truncating)";cat junk |tee >( wc=($(wc)); echo "# ${wc[0]} lines, ${wc[2]} bytes" ;echo ) cat junk
#
truncate -s $newsize junk
echo "# NEW (truncated) file";cat junk |tee >( wc=($(wc)); echo "# ${wc[0]} lines, ${wc[2]} bytes" ;echo ) cat junk
#
exit
Вот тестовый вывод
# ORIGINAL file
going abc
going def
# 2 lines, 20 bytes
# MODIFIED file (before truncating)
abc
def
c
going def
# 4 lines, 20 bytes
# NEW (truncated) file
abc
def
# 2 lines, 8 bytes
Я был бы карта распределения памяти файл, сделать все оперативный символ использования* указатели на явную память, затем не отобразить файл и усечь его.
grepped
данные превышают длину того, что позволяет командная строка. это затем повреждает данные
– Anthon
15.12.2013, 13:30
Не совсем на месте , но - это может быть полезно в аналогичных обстоятельствах.
Если дисковое пространство является проблемой, сначала сжимайте файл (поскольку это текст, это даст огромное сокращение), затем используйте sed (или grep, или что-то еще) обычным способом в середине конвейера распаковки / сжатия.
# Reduce size from ~35Gb to ~6Gb
$ gzip MyFile
# Edit file, creating another ~6Gb file
$ gzip -dc <MyFile.gz | sed -e '/foo/d' | gzip -c >MyEditedFile.gz
Для тех, кто ищет в Google вопрос "как изменить файл в -месте?", правильный ответ в обычном случае будет остановить в поисках неясного функции оболочки, которые рискуют повредить ваш файл для незначительного повышения производительности, и вместо этого используют некоторые варианты этого шаблона:
grep "foo" file > file.new && mv file.new file
Только в чрезвычайно необычной ситуации , когда это по какой-то причине невозможно, следует серьезно рассмотреть любой из других ответов на этой странице (, хотя их, безусловно, интересно читать ). Я соглашусь, что загадка OP, связанная с отсутствием места на диске для создания второго файла, является именно такой ситуацией. Хотя даже тогда есть и другие варианты, например. предоставлено @Ed Randall и @Basile Starynkevitch.
grep
не произведет больше данных, чем они читают, положение записи должно всегда быть позади положения чтения. Даже если Вы запишете на том же уровне как чтение, то он все еще будет в порядке. Попробуйте rot13 этим вместо grep, и с другой стороны. md5sum прежде и после и Вы будете видеть, что это - то же. – camh 11.04.2011, 16:45dd
, но это является громоздким. – Gilles 'SO- stop being evil' 12.04.2011, 00:24