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


332

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

a
b
c

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

a
b

Ответы:


408

Использование 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

56
В Mac OS X (по состоянию на 10.7.4) эквивалент sed -iкоманды выше sed -i '' -e '$ d' foo.txt. Кроме того, в head -n -1настоящее время не будет работать на Mac.
mklement0

26
Не могли бы вы объяснить, что делает регулярное выражение $ d? Это вопрос удаления последней строки, поэтому я думаю, что это самая важная часть для всех, кто просматривает этот вопрос. Спасибо :)
Кравемир

38
@Miro: отнюдь не является $ dрегулярным выражением. Это команда sed. dэто команда для удаления строки, в то время как $означает «последняя строка в файле». При указании местоположения (называемого «диапазон» в sed lingo) перед командой эта команда применяется только к указанному местоположению. Таким образом, эта команда явно говорит: «в диапазоне последней строки в файле, удалите его». Довольно гладко и прямо к делу, если вы спросите меня.
Даниэль Камил Козар

1
Как удалить последнюю строку, но только если это пустая строка?
Скан

1
@Alex: обновлено для GNU sed; понятия не имею о различных версиях sed для BSD / UNIX / MacOS ...
thkala

279

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

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

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

tail -n +2 foo.txt

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

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


16
head -n -1 foo.txtдостаточно
ДМИТРИЙ МАЛИКОВ

20
head -n -1 не работает на голове bdsutils. по крайней мере, в той версии, которую использует macos, так что это не работает.
johannes_lalala

23
@johannes_lalala На Mac вы можете brew install coreutils(основные утилиты GNU) и использовать gheadвместо head.
интересно там

Вот простой bash-скрипт, который автоматизирует его, если у вас есть несколько файлов с различным количеством строк внизу для удаления: cat TAILfixer FILE=$1; TAIL=$2; head -n -${TAIL} ${FILE} > temp ; mv temp ${FILE}Запустите его для удаления 4 строк, например, из myfile, как: ./TAILfixer myfile 4конечно, сначала сделайте его исполняемым с помощьюchmod +x TAILfixer
FatihSarigol

Недурно, потому что это сработало, но, несмотря на все сравнения sed, эта команда тоже чертовски медленная
Джефф Дидерикс,

121

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

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


3
Я даже не знал о дд. Это должен быть главный ответ. Спасибо вам большое! Обидно, что решение не работает для (заблокированных) сжатых файлов.
tommy.carstensen

4
Очень, очень умный подход! Просто примечание: он требует и работает с реальным файлом, поэтому его нельзя использовать на каналах, подстановках команд, перенаправлениях и подобных цепочках.
MestreLion

3
Это должен быть главный ответ. Работал мгновенно для моего файла ~ 8 ГБ.
Питер Коган

8
пользователи Mac: dd if = / dev / null of = <filename> bs = 1 seek = $ (echo $ (stat -f =% z <filename> | cut -c 2-) - $ (tail -n1 <filename> | wc -c) | bc)
Игорь

2
Этот подход - путь для больших файлов!
thomas.han

61

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

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


3
Краткое примечание: truncateне доступно в OS X по умолчанию. Но это легко установить с помощью Brew: brew install truncate.
Гийом Будро

3
Очень хорошо. Гораздо лучше без дд. Я был в ужасе от использования дд в качестве единственной неуместной цифры или буквы может означать катастрофу .. +1
Йосси Фархун

1
(ПРЕДУПРЕЖДЕНИЕ О ШУМЕ) На самом деле, ("dd" -> "катастрофа") требует 1 замены и 6 вставок; вряд ли "единственная неуместная цифра или буква". :)
Кайл

29

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

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

sed -e '$ d' foo.txt

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

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


Это работает для меня, но я не понимаю. во всяком случае, это сработало.
Мелвин

Флаг -i ''указывает sed изменять файл на месте, сохраняя резервную копию в файле с расширением, предоставленным в качестве параметра. Поскольку параметр является пустой строкой, файл резервной копии не создается. -eговорит sed выполнить команду. Команда $ dозначает: найти последнюю строку ( $) и удалить ее ( d).
Кшиштоф Шафранек

24

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

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

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

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

Пояснение:

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

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


2
Установка coreutils с использованием Homebrew даст вам голову GNU в качестве «ghead», что позволит вам сделатьghead -n -1
отчасти

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

2
Для решения фильтра, которое не редактирует файл на месте, используйте sed '$d'.
LHF

8
awk 'NR>1{print buf}{buf = $0}'

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

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

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

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


2
Это решение является фильтром и не редактирует файл на месте.
LHF


0

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

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

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

Linux

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

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

MacOS

Эквивалент Сед-я

sed -i '' -e '$ d' ~/path/to/your/file/name

0

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

vim + [FILE]

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

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


2
Акцент на простоте был потерян здесь
Питер М

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.