Удалить конкретные номера строк из текстового файла, используя sed?

235

Я хочу удалить один или несколько конкретных номеров строк из файла. Как бы я сделал это с помощью sed?

Джастин этир
источник
1
Можете ли вы привести более конкретный пример того, что вы хотите? Как вы решите, какие строки удалить?
Марк Байерс
Возможно, посмотрите также stackoverflow.com/questions/13272717/… и просто примените его в обратном порядке (выведите, если ключ не находится в ассоциативном массиве).
tripleee

Ответы:

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», который уничтожит ваш файл.
пра
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
Будьте очень осторожны, используя sort -r, если вы удаляете из списка строк, иначе ваш первый sed изменит номера строк всего остального! ...
Konchog
Чтобы прокомментировать неправильные строки, удаляемые в цикле: обязательно начинайте с самого большого номера строки, в противном случае каждая удаленная строка будет
смещать
25

и как хорошо

awk 'NR!~/^(5|10|25)$/' file
ghostdog74
источник
2
NB: Эта строка awk работала для меня более надежно, чем вариант sed (между OS-X и Ubuntu Linux)
Джей Тейлор,
3
Обратите внимание, что это ничего не удаляет в файле. Он просто печатает файл без этих строк на стандартный вывод. Поэтому вам также нужно перенаправить вывод во временный файл, а затем переместить временный файл, чтобы заменить оригинал.
mivk
17
$ cat foo
1
2
3
4
5
$ sed -e '2d;4d' foo
1
3
5
$ 
Мэтью Слэттери
источник
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не понимает аргумент option -для обозначения стандартного ввода, поэтому вам нужно перенаправить скрипт во временный файл и очистить его, когда вы закончите, или, возможно, заменить одиночную черту /dev/stdinили /proc/$pid/fd/1вашей ОС (или оболочкой). ) имеет это.

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

tripleee
источник
Я не совсем согласен с «симптомом антипаттерна». Типы файлов на основе разметки (например, XML или JSON) требуют определенных строк в конце, чтобы быть действительными файлами. В этом случае зачастую наиболее разумный подход состоит в том, чтобы удалить эти строки, вставить в файл то, что вы хотите добавить, а затем повторно добавить эти строки, потому что поместить строки между ними может быть гораздо больше усилий, и это противоречит потенциальное желание избегать дополнительных инструментов, таких как sed, как можно больше.
Егор Ганс
Я не совсем понимаю, какой сценарий вы представляете. Там являются сценарии , в которых это является законным подход , но подавляющее большинство случаев , которые я видел являются новичками , которые делают более или менее точно , что мой первый пример демонстрирует. (Возможно, они происходят от какого-то действительно низкоуровневого языка и привыкли делить свои проблемы далеко за пределы молекулярного уровня, потому что вы должны использовать asm или C.)
tripleee
Удаление материала по номеру строки из XML или JSON звучит крайне хрупко , если не прямо опасно.
tripleee
Под этим я в основном подразумеваю, что, как создатель такого файла, вы знаете, что должно быть в конце документа (т. Е. Набор закрывающих / квадратных скобок в последних нескольких строках для JSON, или точный закрывающие теги для XML). Помня об этом, самый простой подход к расширению такого документа: 1) удалить последние несколько строк, 2) добавить новый контент, 3) повторно добавить последние несколько строк. Таким образом, документ может быть действительным как до, так и после его расширения, без необходимости искать способ добавления строк в середине документа.
Егор Ганс
1
Пока что это единственный ответ с подходящим решением для большого количества строк (т.е. предоставленный файлом). И предисловие тоже имеет смысл. Это заслуживает большего количества голосов. Кстати, если вы хотите печатать строки, а не удалять их, используйте pвместо dвместе с опцией -n(она не будет работать -nи !dне будет работать).
Скиппи ле Гран Гуру
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 номер строки внутри текущего блока. (увидеть ниже)
  • ||и &&являются логическим оператором OR и AND .
  • 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      |
+-----+------+----------+------------+

Hastur
источник
2
Я восхищаюсь тем, как ты оправдываешь свое безумное имя.
Юкка Далбом