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



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



a
b
c


Мне нужна простая команда, в результате которой содержимое foo.txt будет:



a
b
1355   11  

11 ответов:

Использование 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), и установите эту позицию, чтобы быть концом файла (путем dding один байт /dev/null на него).

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

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

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

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

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

tail -n 1 "$file" | tee >(wc -c | xargs -I {} truncate "$file" -s -{})
Эти команды могут эффективно обрабатывать очень большой файл. Это похоже на ответ Йоси и вдохновлено им, но позволяет избежать использования нескольких дополнительных функций.

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

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

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

sed -e '$ d' foo.txt

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

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

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

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

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

Кот файл.txt / tail-r / tail-n +2 / tail-r

Пояснение:

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

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

echo -e '$d\nw\nq'| ed foo.txt
awk 'NR>1{print buf}{buf = $0}'

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

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

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

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

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

Этот фрагмент делает трюк.

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

Используя dd:

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}

Ruby (1.9+)

ruby -ne 'BEGIN{prv=""};print prv ; prv=$_;' file

Comments

    Ничего не найдено.