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

435

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

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

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

  • 0
    что означает -i?
  • 4
    @cikatomo: это встроенное редактирование - оно редактирует файл с тем, что вы генерируете.
Показать ещё 1 комментарий
Теги:
scripting
sed

15 ответов

874
Лучший ответ

Попробуй хвост:

tail -n +2 "$FILE"

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

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

Версия BSD может быть намного медленнее, чем sed. Интересно, как им это удалось; tail должен просто читать файл построчно, в то время как sed выполняет довольно сложные операции, включая интерпретацию скрипта, применение регулярных выражений и тому подобное.

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

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

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

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

Если вы хотите удалить первую строку внутри файла, вы должны использовать:

tail -n +2 "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE"

&& гарантирует, что файл не будет перезаписан при возникновении проблемы.

  • 32
    Он не работает с линиями 15 МБ или более
  • 3
    @user: Интересный момент. Где ты взял номер?
Показать ещё 26 комментариев
130

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

sed -i '1d' filename
  • 1
    Я получаю сообщение об ошибке: unterminated transform source string
  • 1
    Сед был намного быстрее, когда я рассчитал время операции.
Показать ещё 5 комментариев
71

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

sed '1d' test.dat > tmp.dat 
  • 16
    Интересная демография
  • 0
    Мне больше нравится эта версия, потому что она читается лучше. :)
17

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

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

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

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

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

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

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

  • 3
    Также известный как проблема XY .
  • 0
    Я думаю, что ОП пытается добиться того, что заставило меня найти этот вопрос. У меня есть 10 файлов CSV с 500k строк в каждом. Каждый файл имеет ту же строку заголовка, что и первая строка. Я кошка: эти файлы в один файл, а затем импортировать их в БД, позволяя БД создавать имена столбцов из первой строки. Очевидно, я не хочу, чтобы эта строка повторялась в файле 2-10.
Показать ещё 1 комментарий
10

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

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

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

9

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

  • 0
    Re "... почти нет файловых систем, поддерживающих усечение ..." : это интересно; пожалуйста, рассмотрите возможность включения такой файловой системы в скобки.
7

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

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

Команда ed была оригинальным текстовым редактором UNIX, еще до появления полноэкранных терминалов, а тем более графических рабочих станций. Редактор ex - это расширенная версия ed, которая также эквивалентна работе с приглашением двоеточия в vi, поэтому многие из этих команд работают. Хотя ed предназначен для интерактивного использования, его также можно использовать в пакетном режиме, посылая ему строку команд, что и делает это решение.

Последовательность <<<$'1d\nwq\n' использует поддержку Bash для строк here (<<<) и кавычек POSIX ($'... ') для подачи ввода в команду ed состоящую из двух строк: 1d, который удаляет первую строку, а затем wq, что ж обряды файла обратно на диск и затем д UITS сеанс редактирования.

  • 0
    это элегантно +1
7

Утилита sponge избавляет от необходимости манипулирования временным файлом:

tail -n +2 "$FILE" | sponge "$FILE"
  • 0
    sponge действительно намного чище и надежнее, чем принятое решение ( tail -n +2 "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE" )
  • 0
    Следует четко указать, что для 'sponge' требуется установить пакет 'moreutils'.
Показать ещё 1 комментарий
5

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

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

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

  • 0
    Может понадобиться процитировать +wq! если ваша оболочка bash. Наверное, не со времен ! не в начале слова, но привычка цитировать вещи, вероятно, хорошо во всем. (И если вы стремитесь к суперэффективности, не цитируя без необходимости, кавычки вокруг 1d тоже не нужны.)
4

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

man csplit
csplit -k file 1 '{1}'
  • 0
    Этот синтаксис также будет работать, но генерировать только два выходных файла вместо трех: csplit file /^.*$/1 . Или проще: csplit file //1 . Или еще проще: csplit file 2 .
3

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

cat textfile.txt | tail -n +2
  • 3
    - вы должны сделать "tail -n +2 textfile.txt"
  • 5
    @niglesiais Я не согласен с «бесполезным использованием кошки», так как оно ясно дает понять, что это решение подходит для содержимого по конвейеру, а не только для файлов.
2

Вы можете легко сделать это с:

cat filename | sed 1d > filename_without_first_line

в командной строке; или чтобы окончательно удалить первую строку файла, используйте режим sed на -i флагом -i:

sed -i 1d <filename>
1

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

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

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

(файл1 содержит строки кода sql)

  • 0
    Что содержит первая строка? Можете ли вы просто переписать его с комментарием SQL, как я предложил в своем посте?
0

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

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

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

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

  • 0
    Первое решение по сути идентично тому, что сейчас делает Brent. Я не понимаю ваш программный подход, нужно удалить только первую строку, вы просто прочитали бы и отбросили первую строку и скопировали остальную часть в другой файл, который снова совпадает с подходами sed и tail.
  • 0
    Второе решение подразумевает, что файл не сжимается первой строкой каждый раз. Программа просто обрабатывает его, как если бы оно было сокращено, но каждый раз начинается с следующей строки
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню