it-roy-ru.com

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

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

a
b
c

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

a
b
280
Fragsworth

Используя 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.

MacOS:

В Mac OS X (по состоянию на 10.7.4) эквивалент команды sed -i выше:

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

На сегодняшний день это самое быстрое и простое решение, особенно для больших файлов:

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

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

tail -n +2 foo.txt

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

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

250
John

У меня были проблемы со всеми ответами здесь, потому что я работал с ОГРОМНЫМ файлом (~ 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 перезапишет файл на месте, а не скопирует (и проанализирует) каждую строку файла, как это делают другие решения.

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

103
Yossi Farjoun

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

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

52
ohspite

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

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

sed -e '$ d' foo.txt

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

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

28
manpreet singh

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

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

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

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

Пояснение:

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

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

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

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

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

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

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

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

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

0
Michael Kingski

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

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

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}
0
virtual-light