Видалити конкретні номери рядків із текстового файлу за допомогою sed?


235

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


1
Чи можете ви навести більш конкретний приклад того, що ви хочете? Як ви вирішите, які рядки видалити?
Марк Байєрс

Може бути , дивись також stackoverflow.com/questions/13272717 / ... і просто applyeit в зворотному (друк , якщо ключ не в асоціативному масиві).
трійчатка

Відповіді:


374

Якщо потрібно видалити рядки від 5 до 10 та 12:

sed -e '5,10d;12d' file

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

sed -i.bak -e '5,10d;12d' file

Це дозволить створити резервну копію файлу file.bakта видалити дані рядки.

Примітка: Номери рядків починаються з 1. Перший рядок файлу дорівнює 1, а не 0.


32
Не всі унікси мають gnu sed з "-i". Не робіть помилки, повернувшись до "sed cmd file> file", який видалить ваш файл.
pra

4
що якщо я хотів видалити 5-й рядок до останнього рядка?
Юрген Пол

14
@WearetheWorldsed -e '5,$d' file
Брайан Кемпбелл

1
@BrianCampbell Що мені робити, щоб видалити лише певний рядок ??
Канагавелу Сугумар

14
@KanagaveluSugumar sed -e '5d' file. Синтаксис є <address><command>; де <address>може бути як один рядок типу, так 5і діапазон рядків типу 5,10, а команда dвидаляє заданий рядок або рядки. Адреси також можуть бути регулярними виразами або знаком долара, що $вказує останній рядок файлу.
Брайан Кемпбелл

50

Ви можете видалити певний окремий рядок із його номером рядка

sed -i '33d' file

Це видалить рядок на 33 рядок і збереже оновлений файл.


1
У моєму випадку "sed" видалив неправильну лінію. Тому я використовую цей підхід: sed -i '0,/<TARGET>/{/<NEW_VALUE>/d;}' '<SOME_FILE_NAME>'. Дякую!
Едуардо Лусіо

Тут же я написав цикл і, як не дивно, деякі файли втратили правильний рядок, але деякі файли теж втратили один інший рядок, не маючи поняття, що пішло не так. (GNU / Linux bash4.2) команда awk нижче добре працювала в циклі
FatihSarigol

Будьте дуже обережні, використовуючи сортування -r, якщо ви вилучаєте зі списку рядків, інакше ваш перший sed змінить номери рядків усього іншого! ...
Konchog

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

25

і awk також

awk 'NR!~/^(5|10|25)$/' file

2
NB: Ця лінія awk працювала для мене надійніше, ніж варіант sed (між OS-X та Ubuntu Linux)
Джей Тейлор,

3
Зауважте, що це нічого не видаляє з файлу. Він просто виводить файл без цих рядків у stdout. Тож вам також потрібно перенаправити вихід на тимчасовий файл, а потім перемістити тимчасовий файл, щоб замінити оригінал.
mivk


6

Це дуже часто симптом антипаттера. Інструмент, який створив номери рядків, цілком може бути замінений на той, який видаляє рядки відразу. Наприклад;

grep -nh error logfile | cut -d: -f1 | deletelines logfile

(де deletelinesутиліта, яку ви уявляєте, що вам потрібна) - це те саме, що

grep -v error logfile

Сказавши, що, якщо ви потрапили в ситуацію, коли вам справді потрібно виконати це завдання, ви можете створити простий sedскрипт з файлу номерів рядків. З гумором (але, можливо, трохи заплутано) ви можете це зробити sed.

sed 's%$%d%' linenumbers

Це приймає файл номерів рядків, по одному на рядок, і створює, на стандартному виході, однакові номери рядків, dдодані після кожного. Це дійсний sedскрипт, який ми можемо зберегти у файл або (на деяких платформах) передати в інший sedекземпляр:

sed 's%$%d%' linenumbers | sed -f - logfile

На деяких платформах sed -fаргумент параметрів не розуміє -стандартного введення, тому вам доведеться перенаправити скрипт на тимчасовий файл і очистити його, коли ви закінчите, або, можливо, замініть одиночний тире /dev/stdinабо, /proc/$pid/fd/1якщо ваша ОС (або оболонка) ) має те.

Як завжди, -iперед -fваріантом можна додати sedредагування цільового файлу замість того, щоб виробляти результат на стандартному виведенні. На * платформах BSDish (включаючи OSX) вам також потрібно надати явний аргумент -i; загальна ідіома - подавати порожній аргумент; -i ''.


Я не зовсім згідний із "симптомом антипаттера". Типи файлів на основі розмітки (наприклад, XML або JSON) потребують конкретних рядків наприкінці, щоб бути дійсними файлами. У такому випадку часто найрозумнішим підходом є видалення цих рядків, додавання у файл того, що ви хочете додати, а потім повторне додавання цих рядків, тому що введення рядків між ними прямо зараз може бути набагато більше зусиль, і йде проти потенційне бажання уникати додаткових інструментів, таких як sed, наскільки це можливо.
Егор Ганс

Я не зовсім розумію, який сценарій ви уявляєте. Там є сценарії , в яких це є законним підхід , але переважна більшість випадків , які я бачив є новачками , які роблять більш-менш точно , що мій перший приклад демонструє. (Можливо , вони прийшли з якого - або мови дійсно низького рівня і використовуються для поділу їх проблеми , як в минулому молекулярному рівні, тому що ви повинні в асемблері або C)
tripleee

Видалення матеріалів за номером рядка з XML або JSON звучить надзвичайно крихко, якщо не відверто небезпечно.
трійчатка

Що я в основному маю на увазі під цим, - це те, що як творець такого файлу ви знаєте, що має бути в кінці документа (тобто набір закритих дужок / квадратних дужок в останніх рядках для JSON, або точний теги для закриття XML). Усвідомлюючи це, найпростішим підходом до розширення такого документа є 1) видалення останніх рядків, 2) додавання нового вмісту, 3) повторне додавання останніх рядків. Таким чином, документ може бути дійсним як до, так і після його розширення, не потребуючи пошуку способу додавання рядків до документа.
Єгор Ганс

1
Поки що це єдина відповідь з відповідним рішенням для великої кількості рядків (тобто наданих файлом). І передмова має сенс теж. Він заслуговує на більшу кількість нагород. BTW, якщо ви хочете надрукувати рядки, а не видаляти їх, використовуйте pзамість цього параметр dразом із опцією -n(без нього не буде працювати -nі !dне працюватиме).
Skippy le Grand Gourou

2

Я хотів би запропонувати узагальнення з awk.

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

awk '{nl=((NR-1)%2000)+1; if ( (nl<714) || ((nl>1025)&&(nl<1029)) ) print  $0}'
 OriginFile.dat > MyOutputCuttedFile.dat

У цьому прикладі розмір блоку становить 2000, і я хочу надрукувати рядки [1..713] та [1026..1029].

  • NR - це змінна, яка використовується awk для зберігання поточного номера рядка.
  • % дає залишок (або модуль) ділення двох цілих чисел;
  • nl=((NR-1)%BLOCKSIZE)+1Тут ми записуємо в змінну nl номер рядка всередині поточного блоку. (Дивись нижче)
  • ||і &&є логічним оператором АБО і І .
  • print $0 пише повний рядок

Why ((NR-1)%BLOCKSIZE)+1:
(NR-1) We need a shift of one because 1%3=1, 2%3=2, but 3%3=0.
  +1   We add again 1 because we want to restore the desired order.

+-----+------+----------+------------+
| NR  | NR%3 | (NR-1)%3 | (NR-1)%3+1 |
+-----+------+----------+------------+
|  1  |  1   |    0     |     1      |
|  2  |  2   |    1     |     2      |
|  3  |  0   |    2     |     3      |
|  4  |  1   |    0     |     1      |
+-----+------+----------+------------+


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