Як використовувати sed для видалення останніх n рядків файлу


148

Я хочу видалити кілька n рядків з кінця файлу. Це можна зробити за допомогою sed?

Наприклад, для видалення рядків від 2 до 4 я можу використовувати

$ sed '2,4d' file

Але я не знаю номерів рядків. Я можу видалити останній рядок за допомогою

$sed $d file

але я хочу знати спосіб видалення n рядків з кінця. Будь ласка, дайте мені знати, як це зробити, використовуючи sed або інший метод.


2
@arashkordi: для цього використовується підрахунок рядків зверху файлу, а не знизу.
Ams

Питання, пов'язані із суперпользователями .
Тор

//, Можливо, подумайте про переформулювання питання, щоб зробити його більш загальним, ніж просто sed ?
Натан Басанес

1
sed $d fileповертає помилку. Замість $ d має бути взято в лапки, наприклад:sed '$d' file
Akronix

Відповіді:


221

Я не знаю про це sed, але це можна зробити за допомогою head:

head -n -2 myfile.txt

19
+1 для простоти. Еквівалентна команда sed непогана: sed -e :a -e '$d;N;2,5ba' -e 'P;D' file( ,5для останніх 5 рядків).
Марк Б

62
Зауважте, що це працює з деякими версіями head, але не є стандартними. Дійсно, стандарт для headштатів:The application shall ensure that the number option-argument is a positive decimal integer.
Вільям Персел

21
Щоб додати до відповіді @ WilliamPursell ... У оболонці Mac OS це не працює.
JDS

7
Ні на BSD, але питання позначено тегами Linux.
Ams

3
мінус 1, тому що, крім OSX, це не працює для мене на Ubuntu14 -head: illegal line count -- -2
Michael Durrant

30

Якщо жорстке кодування n - це варіант, ви можете використовувати послідовні дзвінки на sed. Наприклад, щоб видалити останні три рядки, тричі видаліть останній один рядок:

sed '$d' file | sed '$d' | sed '$d'

2
Це, плюс -i, щоб редагувати файл на місці, а не скидати його в stdout, працювало для мене.
WAF

27

З седу однолінійки :

# delete the last 10 lines of a file
sed -e :a -e '$d;N;2,10ba' -e 'P;D'   # method 1
sed -n -e :a -e '1,10!{P;N;D;};N;ba'  # method 2

Здається, це те, що ви розкрадаєте.


@Thor ви можете змінити кількість рядків , видаляються з файлу, змінюючи 10в'$d;N;2,10ba'
Alexej Магура

1
@AlexejMagura: Я коментував більш ранню версію відповіді.
Тор

22

Смішне та просте sedта tacрішення:

n=4
tac file.txt | sed "1,$n{d}" | tac

ПРИМІТКА

  • подвійні лапки "потрібні оболонці для оцінки $nзмінної в sedкоманді. В одиничних котируваннях не буде виконуватися інтерполят.
  • tacє catзворотним, дивman 1 tac
  • {}в sedтам , щоб відокремити $nі d(якщо немає, то оболонка спробувати інтерполювати неіснуючої $ndзмінної)

7
fyi no tacon osx
Michael Durrant

1
@MichaelDurrant port info coreutils|| brew info coreutils;
голоси

19

Використовуйте sed, але нехай оболонка dзаймається математикою, метою є використання команди, даючи діапазон (для видалення останніх 23 рядків):

sed -i "$(($(wc -l < file)-22)),\$d" file

Щоб видалити останні 3 рядки зсередини:

$(wc -l < file)

Дає кількість рядків файлу: скажімо, 2196

Ми хочемо видалити останні 23 рядки, тому для лівої сторони або діапазону:

$((2196-22))

Дає: 2174 Таким чином, оригінал sed після інтерпретації оболонки:

sed -i '2174,$d' file

З -iробити INPLACE редагувати файл тепер 2173 ліній!

Якщо ви хочете зберегти його в новому файлі, код:

sed -i '2174,$d' file > outputfile

15

Ви можете використовувати голову для цього.

Використовуйте

$ head --lines=-N file > new_file

де N - кількість рядків, які потрібно видалити з файлу.

Вміст вихідного файлу за мінусом N рядків тепер знаходиться у файлі new_file


8

Просто для повноти я хотів би додати своє рішення. Я закінчив це робити зі стандартом ed:

ed -s sometextfile <<< $'-2,$d\nwq'

Це видаляє останні 2 рядки з допомогою на місці монтажу (хоча він дійсно використовувати тимчасовий файл /tmp!!)


5

Щоб урізати дуже великі файли справді на місці, ми маємо truncateкоманду. Він не знає про рядки, але tail+ wcможе конвертувати рядки в байти:

file=bigone.log
lines=3
truncate -s -$(tail -$lines $file | wc -c) $file

Існує очевидна умова перегонів, якщо файл записується одночасно. У цьому випадку це може бути краще використовувати head- він рахує байти з початку файлу (IO дискового диска), тому ми завжди будемо усікати на межі рядка (можливо, більше рядків, ніж очікувалося, якщо файл активно записується):

truncate -s $(head -n -$lines $file | wc -c) $file

Зручна однолінійка, якщо не вдалося спробувати ввести пароль замість імені користувача:

truncate -s $(head -n -5 /var/log/secure | wc -c) /var/log/secure

4

Це може допомогти вам (GNU sed):

sed ':a;$!N;1,4ba;P;$d;D' file

Приємно. Ви пропустили закінчення апострофа ( ').
Тор

вибачте за такий пізній коментар, але я спробую і (припасований до мого AIX / posix) не вдалося надрукувати всі, крім останнього рядка. Читання коду, я не розумію , як останні чотири рядки будуть видалені, явний цикл на 4 перших рядках , так що , звичайно , цикл після d, Dабо Pз GNU СЕД , а не на Posix версії. Здається, рядки не друкуються після Dта зберігають у робочому буфері для нового циклу, не переходячи до рядка "кінець" дій.
NeronLeVelu

@NeronLeVelu програма читає у вікні з 4 рядків у простір шаблонів, а потім додає наступний рядок і друкує перший, поки не досягне кінця файлу, де видаляє залишилися рядки.
потонг

@potong так, я тестую на sed GNU, і він зберігає буфер між рядками, де posix вивантажує його після Dстворення протилежного ефекту.
NeronLeVelu

4

Більшість наведених вище відповідей, схоже, вимагають команд / розширень GNU:

    $ head -n -2 myfile.txt
    -2: Badly formed number

Для трохи більш портативного рішення:

     perl -ne 'push(@fifo,$_);print shift(@fifo) if @fifo > 10;'

АБО

     perl -ne 'push(@buf,$_);END{print @buf[0 ... $#buf-10]}'

АБО

     awk '{buf[NR-1]=$0;}END{ for ( i=0; i < (NR-10); i++){ print buf[i];} }'

Де "10" - "n".


2

З відповідей тут ви вже дізналися, що sed не найкращий інструмент для цієї програми.

Однак я думаю, що є спосіб зробити це за допомогою sed; ідея полягає в тому, щоб додати N рядків, щоб зайняти простір, поки ви не зможете прочитати, не потрапляючи на EOF. Коли EOF натиснуто, надрукуйте вміст місця утримування та закрийте.

sed -e '$!{N;N;N;N;N;N;H;}' -e x

Команда sed зверху опустить останні 5 рядків.


2

Спробуйте виконати таку команду:

n = line number
tail -r file_name | sed '1,nd' | tail -r

Поясніть, будь ласка, як це працює. FYI - для ініціалізації змінних оболонок у нас не повинно бути місця =.
codeforester

@codeforester Перший рядок був коментарем, я думаю. Він просто використовує tail -rтам, де інші використовували tacдля того, щоб упорядковувати порядок рядків, а останні останні nрядки ставали nпершими.
Ніколя Мелай

2

Це можна зробити в 3 етапи:

a) Порахуйте кількість рядків у файлі, який ви хочете відредагувати:

 n=`cat myfile |wc -l`

б) відніміть із цього числа кількість рядків, які слід видалити:

 x=$((n-3))

c) Скажіть sed видалити з цього рядка номер ( $x) до кінця:

 sed "$x,\$d" myfile

Погана математика. Якщо потрібно видалити 3 рядки, відніміть 3, але додайте 1. Якщо ви маєте математику, якщо файл має 10 рядків, 10 - 3 = 7, а ви використовуєте sedдля видалення рядків 7 - 10, тобто 4 рядки, а не 3.
mathguy

1

Ви можете отримати загальну кількість рядків за допомогою wc -l <file>та використовувати

head -n <total lines - lines to remove> <file>


1

Це видалить останні три рядки з file:

for i in $(seq 1 3); do sed -i '$d' file; done;


1
Це занадто дорого для великих файлів - весь файл повинен бути прочитаний і переписаний n разів! І стільки ж виделок для sed.
codeforester

Хоча це може бути не найефективнішим рішенням, воно є далеко найпростішим і легким для розуміння
Дхарам

0

Я віддаю перевагу цьому рішенню;

head -$(gcalctool -s $(cat file | wc -l)-N) file

де N - кількість рядків, які потрібно видалити.



0

Щоб видалити останні 4 рядки:

$ nl -b a file | sort -k1,1nr | sed '1, 4 d' | sort -k1,1n | sed 's/^ *[0-9]*\t//'   

трохи важкий (особливо в ресурсах) порівняно з головою. Хоча б tac file | sed '1,4d' | tacтому, що сортувати потім видалити префікс коштує багато ресурсів.
NeronLeVelu

@NeronLeVelu Ви маєте рацію, але немає tacкоманд - це деякі системи (наприклад, FreeBSD?)
mstafreshi

0

Я придумав це, де n - кількість рядків, які потрібно видалити:

count=`wc -l file`
lines=`expr "$count" - n`
head -n "$lines" file > temp.txt
mv temp.txt file
rm -f temp.txt

Це трохи кругле, але я думаю, що це легко прослідкувати.

  1. Підрахуйте кількість рядків у головному файлі
  2. Відніміть кількість ліній, які ви хочете вилучити з підрахунку
  3. Роздрукуйте кількість рядків, які ви хочете зберегти та зберегти у тимчасовому файлі
  4. Замініть основний файл тимчасовим файлом
  5. Видаліть тимчасовий файл

0

Для видалення останніх N рядків файлу ви можете використовувати ту саму концепцію

$ sed '2,4d' file

Ви можете скористатися комбінацією з хвостовою командою, щоб повернути файл: якщо N дорівнює 5

$ tail -r file | sed '1,5d' file | tail -r > file

І цей спосіб працює також там, де head -n -5 fileкоманда не працює (як на mac!).


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.