Удалить последнюю строку из файла в Bash

У меня есть файл, foo.txt, содержащий следующие строки:

a
b
c

Я хочу простую команду, которая приводит к содержанию foo.txt являются:

a
b

17 ответов

Решение

С помощью GNU sed:

sed -i '$ d' foo.txt

-i опция не существует в GNU sed версии старше 3.95, поэтому вы должны использовать его как фильтр с временным файлом:

cp foo.txt foo.txt.tmp
sed '$ d' foo.txt.tmp > foo.txt
rm -f foo.txt.tmp

Конечно, в этом случае вы также можете использовать head -n -1 вместо sed,

Это, безусловно, самое быстрое и простое решение, особенно для больших файлов:

head -n -1 foo.txt > temp.txt ; mv temp.txt foo.txt

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

tail -n +2 foo.txt

что означает выходные строки, начинающиеся со строки 2.

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

У меня были проблемы со всеми ответами здесь, потому что я работал с ОГРОМНЫМ файлом (~300 ГБ), и ни одно из решений не масштабировалось. Вот мое решение:

dd if=/dev/null of=<filename> bs=1 seek=$(echo $(stat --format=%s <filename> ) - $( tail -n1 <filename> | wc -c) | bc )

В словах: узнать длину файла, который вы хотите получить (длина файла минус длина последней строки, используя bc) и установите эту позицию как конец файла (с помощью dd один байт /dev/null на него).

Это быстро, потому что tail начинает читать с конца, и dd перезапишет файл на месте, а не скопирует (и проанализирует) каждую строку файла, что и делают другие решения.

ПРИМЕЧАНИЕ: это удаляет строку из файла на месте! Сделайте резервную копию или протестируйте фиктивный файл, прежде чем попробовать его на своем собственном файле!

Чтобы удалить последнюю строку из файла, не читая весь файл и не переписывая что-либо, вы можете использовать

tail -n 1 "$file" | wc -c | xargs -I {} truncate "$file" -s -{}

Чтобы удалить последнюю строку, а также вывести ее на стандартный вывод ("выдвинуть"), вы можете объединить эту команду с tee:

tail -n 1 "$file" | tee >(wc -c | xargs -I {} truncate "$file" -s -{})

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

Если вы собираетесь использовать их несколько раз и хотите обрабатывать ошибки и некоторые другие функции, вы можете использовать poptail Команда здесь: https://github.com/donm/evenmoreutils

Для пользователей Mac:

На Mac голова -n -1 не будет работать. И я пытался найти простое решение [не беспокоясь о времени обработки], чтобы решить эту проблему только с помощью команд "head" и / или "tail".

Я попробовал следующую последовательность команд и был счастлив, что смог решить ее, используя команду "tail" [с опциями, доступными на Mac ]. Итак, если вы находитесь на Mac и хотите использовать только "хвост" для решения этой проблемы, вы можете использовать эту команду:

кошка file.txt | хвост -r | tail -n +2 | хвост -r

Пояснение:

1> tail -r: просто меняет порядок строк на входе

2> tail -n +2: печатает все строки, начиная со второй строки в своем входе

Пользователи Mac

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

sed -e '$ d' foo.txt

если вы хотите удалить последнюю строку самого входного файла, сделайте

sed -i '' -e '$ d' foo.txt

echo -e '$d\nw\nq'| ed foo.txt

Linux

$ - последняя строка, d для удаления:

sed '$d' ~/path/to/your/file/name

MacOS

Эквивалент sed -i

sed -i '' -e '$ d' ~/path/to/your/file/name
awk 'NR>1{print buf}{buf = $0}'

По сути, этот код говорит следующее:

Для каждой строки после первой выведите буферизованную строку

для каждой строки сбросьте буфер

Буфер отстает на одну строку, следовательно, вы заканчиваете тем, что печатаете строки с 1 по n-1

Вот решение с использованием губки (из пакета moreutils):

       head -n -1 foo.txt | sponge foo.txt

Краткое изложение решений:

  1. Если вам нужно быстрое решение для больших файлов, используйте эффективный подход tail или dd.

  2. Если вы хотите, чтобы что-то легко расширялось / настраивалось и переносилось, используйте подход перенаправления и перемещения.

  3. Если вы хотите что-то легко расширить / настроить, файл не слишком большой, переносимость (т. Е. В зависимости от moreutilsпакет) не проблема, и вы поклонник квадратных штанов, рассмотрите подход губки.

Приятным преимуществом метода губки по сравнению с подходами "перенаправление и перемещение" является то, что губка сохраняет права доступа к файлам.

Sponge использует значительно больше оперативной памяти по сравнению с методом "перенаправить и переместить". Это немного увеличивает скорость (всего около 20%), но если вас интересует скорость, то подходы "эффективного хвоста" и dd - это лучший вариант.

ОК, обработка большого количества данных, вывод был нормальным, но с одной нежелательной строкой.

Если я передал вывод скрипта по конвейеру:

| sed -i '$ d' Я получил бы следующую ошибку и, наконец, никакого вывода sed: нет входных файлов

Но | голова -n -1 сработала!

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

Используя дд:

BADLINESCOUNT=1
ORIGINALFILE=/tmp/whatever
dd if=${ORIGINALFILE} of=${ORIGINALFILE}.tmp status=none bs=1 count=$(printf "$(stat --format=%s ${ORIGINALFILE}) - $(tail -n${BADLINESCOUNT} ${ORIGINALFILE} | wc -c)\n" | bc )
/bin/mv -f ${ORIGINALFILE}.tmp ${ORIGINALFILE}

Используя усечение:

BADLINESCOUNT=1
ORIGINALFILE=/tmp/whatever
truncate -s $(printf "$(stat --format=%s ${ORIGINALFILE}) - $(tail -n${BADLINESCOUNT} ${ORIGINALFILE} | wc -c)\n" | bc ) ${ORIGINALFILE}

Например, чтобы удалить последнюю строку в файле zshrc:

      cat ~/.zshrc
sed -i '' -e '$ d' ~/.zshrc
cat ~/.zshrc

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

vim + [FILE]

Который + Знак там означает, что когда файл открывается в текстовом редакторе vim, курсор будет помещен в последнюю строку файла.

Теперь просто нажмите dдважды на клавиатуре. Это сделает именно то, что вы хотите - удалите последнюю строку. После этого нажмите: с последующим x а затем нажмите Enter. Это сохранит изменения и вернет вас в оболочку. Теперь последняя строка успешно удалена.

awk "NR != `wc -l < text.file`" text.file |> text.file

Этот фрагмент делает свое дело.

Вы также можете попробовать этот метод: пример удаления последних n строк.

а =0; пока [ $a -lt 4 ];do sed -i '$ d' output.txt; а =expr $a + 1;сделанный

Удаление последних 4 строк из файла (output.txt).

Рубин (1.9+)

ruby -ne 'BEGIN{prv=""};print prv ; prv=$_;' file
Другие вопросы по тегам