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

332

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

a
b
c

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

a
b
Fragsworth
источник

Ответы:

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
thkala
источник
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

ohspite
источник
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

Manpreet Singh
источник
Это работает для меня, но я не понимаю. во всяком случае, это сработало.
Мелвин
Флаг -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
LHF
источник
2
Для решения фильтра, которое не редактирует файл на месте, используйте sed '$d'.
LHF
8
awk 'NR>1{print buf}{buf = $0}'

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

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

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

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

Фу Бах
источник
2
Это решение является фильтром и не редактирует файл на месте.
LHF
0
awk "NR != `wc -l < text.file`" text.file |> text.file

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

Майкл Кингски
источник
Это просто стирает весь файл для меня.
Картик Чуг
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
jasonleonhard
источник
0

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

vim + [FILE]

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

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

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

Рубин (1.9+)

ruby -ne 'BEGIN{prv=""};print prv ; prv=$_;' file
Каруми
источник