Как удалить первую строку текстового файла с помощью скрипта bash/sed?

Мне нужно повторно удалить первую строку из огромного текстового файла с помощью скрипта bash.

сейчас я использую sed -i -e "1d" $FILE - но для удаления требуется около минуты.

есть ли более эффективный способ сделать это?

14 ответов


попробовать хвост GNU:

tail -n +2 "$FILE"

-n x: просто распечатайте последний x строки. tail -n 5 даст вам последние 5 строк ввода. The + знак вида инвертирует аргумент и делает tail печать ничего, кроме первого x-1 строки. tail -n +1 напечатал бы весь файл,tail -n +2 все, кроме первой строки, и т. д.

GNU tail гораздо быстрее, чем sed. tail также доступно на BSD и -n +2 флаг согласовано в обоих инструментах. Проверьте FreeBSD или OS X man-страницы для более.

версия BSD может быть намного медленнее, чем sed, хотя. Интересно, как им это удалось?--7--> надо просто читать файл построчно, пока sed выполняет довольно сложные операции, связанные с интерпретацией скрипта, применением регулярных выражений и тому подобное.

Примечание: у вас может возникнуть искушение использовать

# THIS WILL GIVE YOU AN EMPTY FILE!
tail -n +2 "$FILE" > "$FILE"

но это даст вам пустой файл. Причина в том, что перенаправление (>) происходит перед tail вызывается оболочкой:

  1. оболочка усекает файл $FILE
  2. Shell создает новый процесс для tail
  3. Shell перенаправляет stdout

вы можете использовать-i для обновления файла без использования оператора'>'. Следующая команда удалит первую строку из файла и сохранит ее в файле.

sed -i '1d' filename

для тех, кто находится на SunOS, который не является GNU, следующий код поможет:

sed '1d' test.dat > tmp.dat 

нет, это примерно так же эффективно, как вы собираетесь получить. Вы можете написать программу на C, которая может выполнять работу немного быстрее (меньше времени запуска и обработки аргументов), но она, вероятно, будет стремиться к той же скорости, что и sed, поскольку файлы становятся большими (и я предполагаю, что они большие, если это займет минуту).

но ваш вопрос страдает от той же проблемы, что и многие другие, поскольку он заранее предполагает решение. Если бы вы рассказали нам подробно что вы пытаетесь делай, а не то как, мы можем предложить лучший вариант.

например, если это файл A, который обрабатывает другая программа B, одним из решений было бы не удалять первую строку, а изменять программу B для ее обработки по-другому.

предположим, что все ваши программы добавляются в этот файл A и программа B в настоящее время читает и обрабатывает первую строку перед ее удалением.

вы можете перепроектировать программу B, чтобы она не пыталась удалить первая строка, но поддерживает постоянное (возможно, файловое) смещение в файл A, чтобы при следующем запуске он мог искать это смещение, обрабатывать строку там и обновлять смещение.

затем, в спокойное время (полночь?), он может выполнить специальную обработку файла A, чтобы удалить все строки, обрабатываемые в настоящее время, и установить смещение обратно в 0.

Это, безусловно, будет быстрее для программы, чтобы открыть и искать файл, а не открывать и переписывать. Это обсуждение предполагает вас контролировать программу Б, конечно. Я не знаю, так ли это, но могут быть другие возможные решения, если вы предоставите дополнительную информацию.


вы can редактировать файлы на месте: просто используйте perl -i флаг, как это:

perl -ni -e 'print unless $. == 1' filename.txt

Это заставляет первую строку исчезнуть, как вы просите. Perl нужно будет прочитать и скопировать весь файл, но он организует для вывода, который будет сохранен под именем исходного файла.


Как сказал Пакс, вы, вероятно, не получите быстрее, чем это. Причина в том, что практически нет файловых систем, поддерживающих усечение с начала файла, поэтому это будет O (n операции), где n размер файла. Что вы можете сделать много быстрее, хотя перезаписывает первую строку с тем же количеством байтов (возможно, с пробелами или комментарием), которые могут работать для вас в зависимости от того, что вы пытаетесь сделать (что это, кстати?).


на sponge util избегает необходимости жонглирования временным файлом:

tail -n +2 "$FILE" | sponge "$FILE"

Как насчет использования csplit?

man csplit
csplit -k file 1 '{1}'

должны отображаться строки, кроме первой строки:

cat textfile.txt | tail -n +2

может использовать vim для этого:

vim -u NONE +'1d' +'wq!' /tmp/test.txt

Это должно быть быстрее, так как vim не будет читать весь файл при обработке.


Если вы хотите изменить файл на месте, вы всегда можете использовать оригинал ed вместо streaming правопреемником sed:

ed "$FILE" <<<$'1d\nwq\n'

поскольку похоже, что я не могу ускорить удаление, я думаю, что хорошим подходом может быть обработка файла в таких пакетах:

While file1 not empty
  file2 = head -n1000 file1
  process file2
  sed -i -e "1000d" file1
end

недостатком этого является то, что если программа будет убита в середине (или если там есть какой - то плохой sql-в результате чего часть "процесс" умрет или закроется), будут строки, которые либо пропущены, либо обработаны дважды.

(file1 содержит строки кода sql)


Если то, что вы хотите сделать, это восстановить после сбоя, вы могли бы просто создать файл, который имеет то, что вы сделали до сих пор.

if [[ -f $tmpf ]] ; then
    rm -f $tmpf
fi
cat $srcf |
    while read line ; do
        # process line
        echo "$line" >> $tmpf
    done

будет ли использование хвоста на N-1 строках и направление этого в файл, а затем удаление старого файла и переименование нового файла в старое имя выполнять эту работу?

Если бы я делал это программно, я бы прочитал файл и запомнил смещение файла после прочтения каждой строки, чтобы я мог вернуться к этой позиции, чтобы прочитать файл с одной строкой меньше.