Видаліть останній рядок із файлу в Bash


330

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

a
b
c

Я хочу просту команду, яка призводить до вмісту foo.txtбуття:

a
b

Відповіді:


406

Використання 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»? Це питання про видалення останнього рядка, тому я думаю, що це найважливіша частина для всіх, хто переглядає це питання. Дякую :)
kravemir

38
@Miro: Ні в якому разі не $ dє регулярним виразом. Це команда sed. dє командою для видалення рядка, тоді як $означає "останній рядок у файлі". Коли вказується місце розташування (яке називається "діапазон" в sed lingo) перед командою, ця команда застосовується лише до вказаного місця. Отже, ця команда прямо говорить «видаліть її в діапазоні останнього рядка у файлі». Досить стрункий і прямо до точки, якщо ви запитаєте мене.
Даніель Каміль Козар

1
Як видалити останній рядок, але лише якщо це порожній рядок?
скан

1
@Alex: оновлено для GNU sed;
Поняття

279

Це, безумовно, найшвидше і найпростіше рішення, особливо на великих файлах:

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

якщо ви хочете видалити верхній рядок, скористайтеся цим:

tail -n +2 foo.txt

що означає вихідні лінії, починаючи з другого рядка.

Не використовуйте 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

У мене виникли проблеми з усіма відповідями тут, оскільки я працював з ВЕЛИЧЕЗНИМ файлом (~ 300Gb), і жодне з рішень не масштабувало. Ось моє рішення:

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
Я навіть не знав про дд. Це має бути головна відповідь. Дуже дякую! Прикро, що рішення не працює для (блокування) gzipped файлів.
tommy.carstensen

4
Дуже, дуже розумний підхід! Лише зауваження: він вимагає і працює над реальним файлом, тому його не можна використовувати в трубах, підстановках команд, перенаправленнях і подібних ланцюгах.
MestreLion

3
Це має бути головна відповідь. Працював миттєво для мого файлу ~ 8 Гб.
Пітер Коган

8
користувачі mac: dd, якщо = / dev / null з = <ім'я файлу> bs = 1 шукати = $ (ехо $ (stat -f =% z <ім'я файла> | cut -c 2-) - $ (хвіст -n1 <ім'я файлу> | wc -c) | bc)
Ігор

2
Цей підхід - це шлях для великих файлів!
thomas.han

61

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

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

Щоб видалити останній рядок, а також надрукувати його на stdout ("поп"), ви можете комбінувати цю команду з 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
(JOKE WARNING) Насправді ("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 не працює. І я намагався знайти просте рішення [не турбуючись про час обробки], щоб вирішити цю проблему лише за допомогою команд «голова» та / або «хвіст».

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

cat file.txt | хвіст -р | хвіст -n +2 | хвіст -р

Пояснення:

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

2> хвіст -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

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

Використання 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}

Ой. Шлях занадто складний.
Роберт

0

Linux

$ - останній рядок, d для видалення:

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

MacOS

Еквівалент sed -i

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.