Если вы можете жить с созданием двух оболочек (например, $ SHLVL
увеличивается на два):
tcsh -c 'source /tmp/myRCFile.tcsh; tcsh'
Чтобы избежать записи копии файла, вы могли бы записать файл поверх самого себя, например:
{
sed "$l1,$l2 d" < file
perl -le 'truncate STDOUT, tell STDOUT'
} 1<> file
Опасно, поскольку у вас нет резервной копии.
Или избежать sed
, украсть часть идеи manatwork:
{
head -n "$(($l1 - 1))"
head -n "$(($l2 - $l1 + 1))" > /dev/null
cat
perl -le 'truncate STDOUT, tell STDOUT'
} < file 1<> file
Это все еще можно улучшить, потому что вы перезаписываете первые l1 - 1 строки сами по себе, пока не делаете ' Это необходимо, но его избегание означало бы немного более сложное программирование, и, например, делать все в perl
, что может оказаться менее эффективным:
perl -ne 'BEGIN{($l1,$l2) = ($ENV{"l1"}, $ENV{"l2"})}
if ($. == $l1) {$s = tell(STDIN) - length; next}
if ($. == $l2) {seek STDOUT, $s, 0; $/ = \32768; next}
if ($. > $l2) {print}
END {truncate STDOUT, tell STDOUT}' < file 1<> file
Некоторое время для удаления строк с 1000000 по 1000050 из вывода seq 1e7
:
sed -i "$ l1, $ l2 d" файл
: 16.2s Все они работают по одному и тому же принципу: мы открываем два файловых дескриптора для файла, один в режиме только для чтения (0), используя
0
1 <> файл
( <> файл
будет 0 <> файл
). Эти файловые дескрипторы указывают на два описания открытых файлов , каждое из которых будет иметь текущую позицию курсора в файле, связанном с ними.
Во втором решении, например, первая head -n "$ (($ l1 - 1))"
прочитает $ l1 - 1
строк данных из fd 0 и записать эти данные в fd 1. Таким образом, в конце этой команды курсор на обоих описаниях открытых файлов , связанных с fds 0 и 1, будет в начале $ l1
-я строка.
Затем в head -n "$ (($ l2 - $ l1 + 1))"> / dev / null
, head
будет читать $ l2 - $ l1 + 1
строк из того же описания открытого файла через его fd 0, который все еще связан с ним, поэтому курсор на fd 0 переместится в начало строки после $ l2
один.
Но его fd 1 был перенаправлен на / dev / null
, поэтому после записи в fd 1 он не будет перемещать курсор в описании открытого файла , на которое указывает {...}
fd 1.
Итак, после запуска cat
, курсор на описание открытого файла , на который указывает fd 0, будет в начале следующей строки после $ l2
, в то время как курсор на fd 1 все еще будет находиться в начале $ l1
-й строки. Или, иначе говоря, вторая голова
пропустит эти строки для удаления при вводе, но не при выводе. Теперь cat
заменит $ l1
-ю строку следующей строкой после $ l2
и так далее.
cat
вернется, когда достигнет конца файла на fd 0. Но fd 1 укажет на место в файле, которое еще не было перезаписано. Эта часть должна быть удалена, она соответствует пространству, занимаемому удаленными строками, теперь смещенным в конец файла. Что нам нужно, так это обрезать файл в том месте, где сейчас указывает этот fd 1.
Это делается с помощью системного вызова ftruncate
. К сожалению, для этого не существует стандартной утилиты Unix, поэтому мы прибегаем к perl
. tell STDOUT
дает нам текущую позицию курсора, связанную с fd 1. И мы усекаем файл по этому смещению, используя интерфейс Perl для системного вызова ftruncate
: truncate
.
В третьем решении мы заменяем запись в fd 1 первой команды head
одним системным вызовом lseek
.
Использование sed
- хороший подход: Он понятен, он передает файл (нет проблем с длинными файлами), и его можно легко обобщить, чтобы сделать больше. Но если вам нужен простой способ редактировать файл на месте, проще всего использовать ed
или ex
:
(echo 10,31d; echo wq) | ed input.txt
Лучший подход, гарантированно работающий с файлами неограниченного размера (и для строк такой длины, какую позволяет ваша оперативная память) - это следующая perl
однострочная программа, которая редактирует файл на месте:
perl -n -i -e 'print if $. < 10 || $. > 31' input.txt
Объяснение:
-n
: Применить скрипт к каждой строке. Никакого другого вывода.
-i
: Редактировать файл на месте (используйте-i.bck
для создания резервной копии).
-e ...
: Выведите каждую строку, кроме строк с 10 по 31.
Если вам нужно прочитать и записать 50 ГБ, это займет много времени, независимо от того, что вы делаете. И если строки не имеют фиксированной длины или у вас нет другого способа узнать, где находятся строки, которые нужно удалить, нет способа прочитать файл до последней удаляемой строки. Возможно, специальная программа, которая просто считает символы новой строки и позже копирует полные блоки, немного быстрее, чем sed (1)
, но я считаю, что это не ваше узкое место. Попробуйте использовать время (1)
, чтобы узнать, как распределяется время.
Это поможет?
perl -e '
$num1 = 5;
$num2= 10000;
open IN,"<","input_file.txt";
open OUT,">","output_file.txt";
print OUT <IN> for (1 .. $num1-1)
<IN> for ($num1 .. $num2);
undef $/ and print OUT <IN>;
close IN;
close OUT;
'
Это удалит все строки от 5 до 10000 включительно. Измените числа по своему усмотрению. Однако не вижу эффективного способа сделать это на месте (т.е. при таком подходе придется печатать в другой выходной файл).
Если вы хотите отредактировать файл на месте, большинство инструментов оболочки вам не помогут, потому что, когда вы открываете файл для записи, у вас есть выбор только обрезать его (>
) или добавление к нему ( >>
), не перезаписывая существующее содержимое. dd
- заметное исключение. См. Есть ли способ изменить файл на месте?
export LC_ALL=C
lines_to_keep=$((linenum1 - 1))
lines_to_skip=$((linenum2 - linenum1 + 1))
deleted_bytes=$({ { head -n "$lines_to_keep"
head -n "$lines_to_skip" >&3;
cat
} <big_file | dd of=big_file conv=notrunc;
} 3>&1 | wc -c)
dd if=/dev/null of=big_file bs=1 seek="$(($(wc -c <big_file) - $deleted_bytes))"
(Предупреждение: не проверено!)
вы можете добавить инструкцию * q * uit к вашей команде sed, когда будет достигнута nnennum2, чтобы sed прекратил обработку файла.
sed 'linenum1,linenum2d;linenum2q' file
Обратите внимание, что это ответ на другой вопрос, отмеченный как повторяющийся.
Возник вопрос об удалении строки 4125889 из in.csv.
Вы можете делать что-то небезопасное - тогда вы можете действовать быстро, но можете потерять весь файл, или вы будете зависеть от скорости редактора, который вы используете.
Рекомендую:
echo '\ 0013 \ 0003y' | VED_FTMPFIR =. ved +4125878 in.csv
, где вам нужно в 3 раза больше размера файла и заканчиваются на in.csv
и in.csv.bak
или:
echo '\ 0013 \ 0003! ' | VED_FTMPFIR =. ved +4125878 in.csv
, где вам нужно вдвое больше размера файла, и получившийся файл будет записан на место.
Обратите внимание, что вам нужна POSIX-совместимая реализация оболочки (echo) для правильного расширения escape-последовательностей. Редактор ved
является частью инструментов schily и доступен по адресу:
http://sourceforge.net/projects/schilytools/files/
в schily - *. Tar.bz2
Он использует самый быстрый из известных мне файлов подкачки.
VED_FTMPFIR =.
Среда устанавливает каталог для файла подкачки в текущий каталог. выберите любой каталог, в котором достаточно места.
Это хорошо и просто:
perl -i -n -e 'print unless $.==13' /path/to/your/file
чтобы удалить, например, строку 13 из /path/to/your/file
Вы можете использовать Vim в режиме Ex:
ex -sc '1d2|x' input.txt
1
перейти к первой строке
2
выбрать 2 строки
d
удалить
x
сохранить и закрыть
В особом случае, когда содержимое строк, которые следует удалить, уникально в файле, другой вариант может использовать grep -v
и содержимое строки а не номера строк.Например, если должна быть удалена только одна уникальная строка (удаление одной строки, например, было запрошено в этом дублированном потоке ), или несколько строк, которые все имеют одинаковое уникальное содержание.
Вот пример
grep -v "content of lines to delete" input.txt > input.tmp
Я провел несколько тестов с файлом, который содержит 340 000 строк.
В этом случае метод grep
кажется примерно в 15 раз быстрее, чем метод sed
.
Вот команды и тайминги:
time sed -i "/CDGA_00004.pdbqt.gz.tar/d" /tmp/input.txt
real 0m0.711s
user 0m0.179s
sys 0m0.530s
time perl -ni -e 'print unless /CDGA_00004.pdbqt.gz.tar/' /tmp/input.txt
real 0m0.105s
user 0m0.088s
sys 0m0.016s
time (grep -v CDGA_00004.pdbqt.gz.tar /tmp/input.txt > /tmp/input.tmp; mv /tmp/input.tmp /tmp/input.txt )
real 0m0.046s
user 0m0.014s
sys 0m0.019s
Я пробовал как с параметром LC_ALL = C, так и без него, он не меняет тайминги. Строка поиска (CDGA_00004.pdbqt.gz.tar) находится где-то в середине файла.