sed найти и заменить в linux [duplicate]

Всякий раз, когда вы хотите использовать переменные переменные, вероятно, лучше использовать словарь. Поэтому вместо записи

$foo = "bar"
$$foo = "baz"

вы пишете

mydict = {}
foo = "bar"
mydict[foo] = "baz"

Таким образом, вы не будете случайно перезаписывать ранее существовавшие переменные (что является аспектом безопасности), и вы можете иметь разные " Пространства имен». [/ д2]

166
задан David Dibben 29 September 2008 в 13:22
поделиться

19 ответов

 # sed script to change "foo" to "bar" only on the first occurrence
 1{x;s/^/first/;x;}
 1,/foo/{x;/first/s///;x;s/foo/bar/;}
 #---end of script---

или, если хотите: Примечание редактора: работает только с GNU sed.

sed '0,/RE/s//to_that/' file 

Источник

103
ответ дан fedorqui 23 August 2018 в 01:34
поделиться
  • 1
    это очень много подстановок и шаблонов пространства ... – mitchnull 29 September 2008 в 13:42
  • 2
    Думаю, я предпочитаю решение «или если вы предпочитаете». Было бы также полезно объяснить ответы - и чтобы ответ отвечал на вопрос напрямую, а затем обобщал, а не обобщал только. Но хороший ответ. – Jonathan Leffler 29 September 2008 в 14:15
  • 3
    FYI для пользователей Mac, вы должны заменить 0 на 1, поэтому: sed '1, / RE / s // to_that /' file – mhost 5 October 2014 в 02:24
  • 4
    @mhost Нет, это будет, чем заменить, если шаблон найден в строке # 1, а не если шаблон находится в другой строке, но все еще является первым найденным шаблоном. PS следует отметить, что этот 0, работает только с gnu sed – Jotne 29 October 2014 в 09:59
  • 5
    Может ли кто-нибудь объяснить вам решение «или если вы предпочитаете»? Я не знаю, где поставить «бесплатно». шаблон. – Jean-Luc Nacif Coelho 2 February 2015 в 19:44

Следующая команда удаляет первое вхождение строки в файл. Он также удаляет пустую строку. Он представлен в XML-файле, но он будет работать с любым файлом.

Полезно, если вы работаете с файлами xml и хотите удалить тег. В этом примере он удаляет первое вхождение тега «isTag».

Команда:

sed -e 0,/'<isTag>false<\/isTag>'/{s/'<isTag>false<\/isTag>'//}  -e 's/ *$//' -e  '/^$/d'  source.txt > output.txt

Исходный файл (source.txt)

<xml>
    <testdata>
        <canUseUpdate>true</canUseUpdate>
        <isTag>false</isTag>
        <moduleLocations>
            <module>esa_jee6</module>
            <isTag>false</isTag>
        </moduleLocations>
        <node>
            <isTag>false</isTag>
        </node>
    </testdata>
</xml>

Результат файла (output.txt)

<xml>
    <testdata>
        <canUseUpdate>true</canUseUpdate>
        <moduleLocations>
            <module>esa_jee6</module>
            <isTag>false</isTag>
        </moduleLocations>
        <node>
            <isTag>false</isTag>
        </node>
    </testdata>
</xml>

ps: он не работал для меня в Solaris SunOS 5.10 (довольно старый), но он работает на Linux 2.6, sed версии 4.1.5

0
ответ дан Andreas Panagiotidis 23 August 2018 в 01:34
поделиться
  • 1
    Это выглядит замечательно подобно той же базовой идее, что и ряд предыдущих ответов, с тем же предостережением, что он работает только с GNU sed (следовательно, он не работал с Solaris). Вы должны удалить это, пожалуйста - это действительно не дает отличительной новой информации на вопрос, которому было уже 4½ года, когда вы ответили. Конечно, у него есть пример работал, но это дискуссионный значение, когда вопрос имеет столько ответов, как это делает. – Jonathan Leffler 17 January 2016 в 04:56
#!/bin/sed -f
1,/^#include/ {
    /^#include/i\
#include "newfile.h"
}

Как работает этот скрипт: для строк между 1 и первым #include (после строки 1), если строка начинается с #include, затем добавьте указанную строку.

, если первая #include находится в строке 1, то обе строки 1 и следующая последующая #include будут иметь добавленную линию. Если вы используете GNU sed, у него есть расширение, в котором 0,/^#include/ (вместо 1,) поступит правильно.

12
ответ дан Chris Jester-Young 23 August 2018 в 01:34
поделиться
  • 1
    +1, отметив, что 0 является расширением GNU. – Paul Wagland 22 March 2013 в 12:01

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

sed '/old/s/old/new/1' file

-bash-4.2$ cat file
123a456a789a
12a34a56
a12
-bash-4.2$ sed '/a/s/a/b/1' file
123b456a789a
12b34a56
b12

. Если вы, например, заменив 1 на 2, вы можете заменить все второе а вместо этого.

1
ответ дан FatihSarigol 23 August 2018 в 01:34
поделиться
  • 1
    Вам не нужно делать все это, 's/a/b/' означает match a и do just first match for every matching line – Samveen 6 October 2017 в 08:53

sed имеет очень простой синтаксис для этого: «-i» является интерактивным (нет необходимости в новом файле). Чтобы заменить только первый экземпляр:

sed -i 's/foo/bar/' file

, чтобы заменить глобально, вы использовали бы

sed -i 's/foo/bar/g' file

. В вашем примере я бы использовал (^ и $ - начало и конец строки соответственно )

sed -i 's/^#include/#include\n#include/' file
-4
ответ дан jaap 23 August 2018 в 01:34
поделиться
  • 1
    sed -i 's/foo/bar/' file заменяет первый экземпляр foo на каждой строке; а не для всего файла. – k107 7 June 2012 в 02:52
  • 2
    -i для "на месте" как в «редактировании файла на месте вместо потока в стандартный вывод». Не «интерактивный». – kmarsh 19 April 2016 в 14:04

Я знаю, что это старый пост, но у меня было решение, которое я использовал:

grep -E -m 1 -n 'old' file | sed 's/:.*$//' - | sed 's/$/s\/old\/new\//' - | sed -f - file

В основном используйте grep, чтобы найти первое вхождение и остановиться там. Также напечатайте номер строки, т.е. 5: line. Труба, которая в sed и удалить: и что-нибудь после этого, вы просто остаетесь с номером строки. Труба, которая в sed, которая добавляет s /.*/ замену до конца, которая дает 1-строчный скрипт, который передается в последний sed для запуска в качестве скрипта в файле.

, так что если regex = #include и replace = blah, а первое обнаружение grep находит в строке 5, тогда данные, переданные в последний sed, будут 5s /.*/ blah /.

2
ответ дан Michael Edwards 23 August 2018 в 01:34
поделиться
  • 1
    Я совершенно ненавижу многострочные скрипты sed или команды sed с чем угодно, кроме s и linenumber, поэтому я нахожусь на борту с этим подходом. Вот что я использовал для моего использования (работает с bash): filepath = / etc / hosts; patt = '^ \ (127 \ .0 \ .0 \ .1. * \)'; repl = '\ 1 newhostalias'; sed $ (IFS =: linearray = ($ (grep -E -m 1 -n "$ patt & quot; $ filepath & quot;)) & amp; & echo $ {linearray [0]}) s / & quot; $ patt & quot; / / "$ repl" / "$ filepath" – parity3 14 December 2015 в 22:32

Возможное решение:

    /#include/!{p;d;}
    i\
    #include "newfile.h"
    :
    n
    b

Объяснение:

  • читать строки, пока мы не найдем #include, напечатаем эти строки, а затем запустите новый цикл
  • вставить новую строку include
  • ввести цикл, который просто читает строки (по умолчанию sed также будет печатать эти строки), мы не вернемся к первой части скрипта отсюда
7
ответ дан mitchnull 23 August 2018 в 01:34
поделиться
28
ответ дан mklement0 23 August 2018 в 01:34
поделиться

Использование FreeBSD ed и избежать ошибки ed «нет совпадения» в случае, если в обрабатываемом файле нет инструкции include:

teststr='
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
'

# using FreeBSD ed
# to avoid ed's "no match" error, see
# *emphasized text*http://codesnippets.joyent.com/posts/show/11917 
cat <<-'EOF' | sed -e 's/^ *//' -e 's/ *$//' | ed -s <(echo "$teststr")
   H
   ,g/# *include/u\
   u\
   i\
   #include "newfile.h"\
   .
   ,p
   q
EOF
2
ответ дан nazq 23 August 2018 в 01:34
поделиться

Это может сработать для вас (GNU sed):

sed -si '/#include/{s//& "newfile.h\n&/;:a;$!{n;ba}}' file1 file2 file....

или если память не является проблемой:

sed -si ':a;$!{N;ba};s/#include/& "newfile.h\n&/' file1 file2 file...
2
ответ дан potong 23 August 2018 в 01:34
поделиться

Вы можете использовать awk для выполнения чего-то подобного.

awk '/#include/ && !done { print "#include \"newfile.h\""; done=1;}; 1;' file.c

Объяснение:

/#include/ && !done

Запускает оператор действия между {}, когда строка соответствует "#include" и мы еще не обработали его.

{print "#include \"newfile.h\""; done=1;}

Это печатает #include "newfile.h", нам нужно избежать кавычек. Затем мы устанавливаем переменную done в 1, поэтому мы не добавляем больше включений.

1;

Это означает «распечатать строку» - пустое действие по умолчанию для печати $ 0, которое выводит весь текст линия. Один лайнер и легче понять, чем sed IMO: -)

22
ответ дан richq 23 August 2018 в 01:34
поделиться
  • 1
  • 2
    Это действительно более понятно, но для меня он добавляет строку вместо ее замены; команда используется: awk '/version/ && !done {print " \"version\": \"'${NEWVERSION}'\""; done=1;}; 1;' package.json – Cereal Killer 7 August 2015 в 13:58
  • 3
    здесь та же самая понятная команда, но она добавляет строку над найденной строкой вместо ее замены – Flion 1 November 2018 в 15:46

Я, наконец, получил это, чтобы работать в сценарии Bash, используемом для вставки уникальной метки времени в каждый элемент в фиде RSS:

        sed "1,/====RSSpermalink====/s/====RSSpermalink====/${nowms}/" \
            production-feed2.xml.tmp2 > production-feed2.xml.tmp.$counter

Он меняет только первое вхождение.

${nowms} - это время в миллисекундах, установленное скриптом Perl, $counter является счетчиком, используемым для управления контуром в скрипте, \ позволяет продолжить выполнение команды на следующей строке.

Файл читается, и stdout перенаправляется в рабочий файл.

Как я понимаю, 1,/====RSSpermalink====/ сообщает sed, когда останавливается, устанавливая ограничение диапазона, а затем s/====RSSpermalink====/${nowms}/ является знакомой командой sed, чтобы заменить первую строку на вторую.

В моем случае я помещаю эту команду в двойные кавычки, потому что я использую ее в скрипте Bash с переменными.

2
ответ дан Rob W 23 August 2018 в 01:34
поделиться

Достаточно полная коллекция ответов на linuxtopia sed FAQ . Также подчеркивается, что некоторые ответы, предоставленные людьми, не будут работать с версией sed, отличной от GNU, например

sed '0,/RE/s//to_that/' file

в версии, отличной от GNU, должно быть

sed -e '1s/RE/to_that/;t' -e '1,/RE/s//to_that/'

Однако эта версия не будет работать с gnu sed.

Вот версия, которая работает с обоими:

-e '/RE/{s//to_that/;:a' -e '$!N;$!ba' -e '}'

ex:

sed -e '/Apple/{s//Banana/;:a' -e '$!N;$!ba' -e '}' filename
215
ответ дан rogerdpack 23 August 2018 в 01:34
поделиться
  • 1
    переведенный на человеческий язык: начинайте с строки 0, продолжайте, пока вы не сравните «Яблоко», выполните замену в фигурных скобках. cfr: grymoire.com/Unix/Sed.html#uh-29 – mariotomo 25 June 2013 в 08:46
  • 2
    В OS X я получаю sed: 1: "…": bad flag in substitute command: '}' – ELLIOTTCABLE 7 January 2015 в 17:57
  • 3
    @Landys это все еще заменяет экземпляры в других строках; не только первый экземпляр – sarat 11 April 2015 в 03:56
  • 4
    @sarat Да, ты прав. sed '1,/pattern/s/pattern/replacement/' filename работает только в том случае, если «шаблон не будет отображаться в первой строке», на Mac. Я удалю свой предыдущий комментарий, поскольку он не является точным. Деталь можно найти здесь ( linuxtopia.org/online_books/linux_tool_guides/the_sed_faq/… ). Ответ Энди работает только для GNU sed, но не для Mac. – Landys 11 April 2015 в 13:53
  • 5
    @ELLIOTTCABLE на OS X, используйте sed -e '1s/Apple/Banana/;t' -e '1,/Apple/s//Banana/'. От ответа @ MikhailVS (в настоящее время) ниже. – djb 28 April 2015 в 00:20
  • 6
    Работает без скобок: sed '0,/foo/s/foo/bar/' – Innokenty 4 June 2015 в 14:00
  • 7
    Я получаю sed: -e expression #1, char 3: unexpected , '`с этим – Jonathan 24 March 2017 в 13:53

Ничего нового, но, возможно, немного более конкретного ответа: sed -rn '0,/foo(bar).*/ s%%\1%p'

Пример: xwininfo -name unity-launcher производит вывод, например:

xwininfo: Window id: 0x2200003 "unity-launcher"

  Absolute upper-left X:  -2980
  Absolute upper-left Y:  -198
  Relative upper-left X:  0
  Relative upper-left Y:  0
  Width: 2880
  Height: 98
  Depth: 24
  Visual: 0x21
  Visual Class: TrueColor
  Border width: 0
  Class: InputOutput
  Colormap: 0x20 (installed)
  Bit Gravity State: ForgetGravity
  Window Gravity State: NorthWestGravity
  Backing Store State: NotUseful
  Save Under State: no
  Map State: IsViewable
  Override Redirect State: no
  Corners:  +-2980+-198  -2980+-198  -2980-1900  +-2980-1900
  -geometry 2880x98+-2980+-198

Извлечение идентификатора окна с помощью xwininfo -name unity-launcher|sed -rn '0,/^xwininfo: Window id: (0x[0-9a-fA-F]+).*/ s%%\1%p' :

0x2200003
0
ответ дан Stephen Niedzielski 23 August 2018 в 01:34
поделиться

В качестве альтернативного варианта вы можете посмотреть команду ed.

man 1 ed

teststr='
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
'

# for in-place file editing use "ed -s file" and replace ",p" with "w"
# cf. http://wiki.bash-hackers.org/howto/edit-ed
cat <<-'EOF' | sed -e 's/^ *//' -e 's/ *$//' | ed -s <(echo "$teststr")
   H
   /# *include/i
   #include "newfile.h"
   .
   ,p
   q
EOF
2
ответ дан timo 23 August 2018 в 01:34
поделиться

Просто добавьте число вхождений в конец:

sed s/#include/#include "newfile.h"\n#include/1
12
ответ дан unexist 23 August 2018 в 01:34
поделиться
  • 1
    К сожалению, это не работает. Он заменяет только первое вхождение в каждой строке файла, а не первое вхождение в файл. – David Dibben 29 September 2008 в 13:32
  • 2
    Кроме того, это расширение GNU sed, а не стандартная функция sed. – Jonathan Leffler 29 September 2008 в 14:19
  • 3
    Хм ... время проходит. POSIX 2008/2013 для sed указывает заменяющую команду с помощью: [2addr]s/BRE/replacement/flags и отмечает, что «значение флагов должно быть равно нулю или больше: n Замените для n-го вхождения только BRE, найденного в пространстве шаблонов. Таким образом, по крайней мере, в POSIX 2008 трейлинг 1 не является расширением GNU sed. Действительно, даже в стандарте SUS / POSIX 1997 это было поддержано, поэтому я был не в порядке в 2008 году. – Jonathan Leffler 17 January 2016 в 04:44

Я бы сделал это с помощью awk-скрипта:

BEGIN {i=0}
(i==0) && /#include/ {print "#include \"newfile.h\""; i=1}
{print $0}    
END {}

, затем запустил его с awk:

awk -f awkscript headerfile.h > headerfilenew.h

может быть неаккуратным, я новичок в этом.

2
ответ дан wakingrufus 23 August 2018 в 01:34
поделиться
[f1] [g1] это сработало для меня. [/g1] [g2] пример [/g2] [f2] [g3] Примечание редактора: оба работают только с [g0] GNU [/g0] [f3]. [/g3]
220
ответ дан rogerdpack 5 November 2018 в 23:15
поделиться
  • 1
    переведено на человеческий язык: начните со строки 0, продолжайте, пока не найдете «Apple», выполните подстановку в фигурных скобках. cfr: [g0] grymoire.com/Unix/Sed.html#uh-29 [/g0] – mariotomo 25 June 2013 в 08:46
  • 2
    На OS X я получаю [f1] – ELLIOTTCABLE 7 January 2015 в 17:57
  • 3
    @Landys это все еще заменяет экземпляры в других строках; не только первый экземпляр – sarat 11 April 2015 в 03:56
  • 4
    @ Сарат Да, ты прав. [f1] работает только в том случае, если «шаблон не будет отображаться в первой строке» на Mac. Я удалю свой предыдущий комментарий, так как он не точный. Подробности можно найти здесь ([g0] linuxtopia.org/online_books/linux_tool_guides/the_sed_faq/… [/g0]) Ответ Энди работает только для GNU sed, но не для Mac. – Landys 11 April 2015 в 13:53
  • 5
    @ELLIOTTCABLE в OS X, используйте [f1]. От ответа @ MikhailVS (в настоящее время) ниже. – djb 28 April 2015 в 00:20
  • 6
    Работает и без скобок: [f1] – Innokenty 4 June 2015 в 14:00
  • 7
    Я получаю minted, '`с этим – Jonathan 24 March 2017 в 13:53
[g0] POSIXly (также действует в sed), используется только одно регулярное выражение, требуется память только для одной строки (как обычно): [/g0] [f1] [g1] Объяснено: [/g1] [f2]
0
ответ дан Isaac 5 November 2018 в 23:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: