Substitua a linha inteira que contém uma string usando Sed

319

Eu tenho um arquivo de texto que tem uma linha específica algo como

sometext sometext sometext TEXT_TO_BE_REPLACED sometext sometext sometext

Preciso substituir toda a linha acima por

This line is removed by the admin.

A palavra-chave de pesquisa é TEXT_TO_BE_REPLACED

Eu preciso escrever um script de shell para isso. Como posso conseguir isso usando sed?

Rahul
fonte
@Sronron Tentei sed -rne / s ((TEXT_TO_BE_REPLACED) \ s + \ w + / \ 1 aaaa / xxx 'Mas isso substituiria aaaa por xxx na string se houver aaaa. No meu caso, o texto não é fixo ... tudo o que tenho é TEXT_TO_BE_REPLACED.
Rahul
essa não é uma expressão sed válida. Tem certeza de que deseja com o sed? Qualquer linguagem de programação com bons regexps pode substituir você (procure uma subfunção). Isso seria mais fácil do que entender sed por si mesmo
Scharron
@ Charron Bem, isso deve ser feito com shell script, então eu acho que sed é a minha melhor aposta.
Rahul

Respostas:

483

Você pode usar o comando change para substituir a linha inteira e o -isinalizador para fazer as alterações no local. Por exemplo, usando o GNU sed:

sed -i '/TEXT_TO_BE_REPLACED/c\This line is removed by the admin.' /tmp/foo
Todd A. Jacobs
fonte
4
Observe que você precisa de um espaço antes do c \. Acabei de editar para adicionar isso.
Marcus Downing
27
@MarcusDowning O GNU sed não requer espaço; funciona muito bem como publicado originalmente. Se o seu sed particular precisar de espaço, observe de qualquer maneira qual sed é incompatível e adicione a chamada necessária como um comentário. No entanto, não altere o código de trabalho em uma resposta aceita.
Todd A. Jacobs
7
Como posso usar uma variável em vez do texto "Isso ..."? Se eu substituí-lo por $ variable, ele não imprime seu conteúdo, mas o nome da variável.
26614 Steven
18
Há um problema c\ quando seguido diretamente por uma variável: …c\$VAR…a barra invertida escapará do dólar. Neste caso, eu (bash / sed no Ubuntu 15.10) tive que escrever # …c\\$VAR…
:
6
para um recurso mac: sed -i '' '/TEXT_TO_BE_REPLACED/c\This line is removed by the admin.' /tmp/foo; (quando o primeiro parâmetro está em branco edita no arquivo, caso contrário, cria um backup)
AndreDurao
152

Você precisa usar wildards ( .*) antes e depois para substituir toda a linha:

sed 's/.*TEXT_TO_BE_REPLACED.*/This line is removed by the admin./'
Thor
fonte
Obrigado, tenho o meu trabalho: sed 's /.* <expressão>. * / <Expressão> SE_LABEL = ABC <expressão> / g' MYR2.xml> test.txt
Nasri Najib
9
Isso está funcionando no Mac OS X Yosemite, com a exceção de que estou usando os sinalizadores -i e -e da seguinte maneira:sed -i -e "s/.*search_string.*/Replacement_line/' file_being_searched.txt
Kent Bull
1
@KentJohnson Acho que você tem aspas incompatíveis em seu comando.
HashHazard
@ MBarnett você está certo, eu deveria ter duas aspas duplas lá.
Kent Bull
2
Apenas para a informação completa. Para torná-la inplace pode-se adicionar -ia opção
Temak
20

A resposta aceita não funcionou para mim por vários motivos:

  • minha versão do sed não gosta -icom uma extensão de comprimento zero
  • a sintaxe do c\comando é estranha e eu não consegui fazê-lo funcionar
  • Não percebi que alguns dos meus problemas vêm de barras sem escape

Então, aqui está a solução que eu achei que deveria funcionar para a maioria dos casos:

function escape_slashes {
    sed 's/\//\\\//g' 
}

function change_line {
    local OLD_LINE_PATTERN=$1; shift
    local NEW_LINE=$1; shift
    local FILE=$1

    local NEW=$(echo "${NEW_LINE}" | escape_slashes)
    sed -i .bak '/'"${OLD_LINE_PATTERN}"'/s/.*/'"${NEW}"'/' "${FILE}"
    mv "${FILE}.bak" /tmp/
}

Portanto, o uso da amostra para corrigir o problema representava:

change_line "TEXT_TO_BE_REPLACED" "This line is removed by the admin." yourFile
Skensell
fonte
18

A resposta acima:

sed -i '/TEXT_TO_BE_REPLACED/c\This line is removed by the admin.' /tmp/foo

Funciona bem se a sequência / linha de substituição não for uma variável.

A questão é que, no Redhat 5, o \depois do cescape o $. O dobro\\ também não funcionou (pelo menos no Redhat 5).

Por meio de tentativa e teste, descobri que o \após o cé redundante se a sua linha / seqüência de caracteres de substituição for apenas uma única linha. Então eu não usei \depois doc , usei uma variável como uma única linha de substituição e foi uma alegria.

O código seria algo como:

sed -i "/TEXT_TO_BE_REPLACED/c $REPLACEMENT_TEXT_STRING" /tmp/foo

Observe o uso de aspas duplas em vez de aspas simples.

user4256255
fonte
você ainda pode usar aspas simples como esta: sed -i '/ TEXT_TO_BE_REPLACED / c' "$ VARIABLE" '' / tmp / foo #
Alistair McIntyre
4

Todas as respostas fornecidas até o momento pressupõem que você saiba algo sobre o texto a ser substituído, o que faz sentido, pois foi o que o OP pediu. Estou fornecendo uma resposta que pressupõe que você não sabe nada sobre o texto a ser substituído e que pode haver uma linha separada no arquivo com o mesmo conteúdo ou conteúdo semelhante que você não deseja que seja substituído. Além disso, suponho que você saiba o número da linha a ser substituída.

Os exemplos a seguir demonstram a remoção ou alteração do texto por números de linhas específicos:

# replace line 17 with some replacement text and make changes in file (-i switch)
# the "-i" switch indicates that we want to change the file. Leave it out if you'd
#   just like to see the potential changes output to the terminal window.
# "17s" indicates that we're searching line 17
# ".*" indicates that we want to change the text of the entire line
# "REPLACEMENT-TEXT" is the new text to put on that line
# "PATH-TO-FILE" tells us what file to operate on
sed -i '17s/.*/REPLACEMENT-TEXT/' PATH-TO-FILE

# replace specific text on line 3
sed -i '3s/TEXT-TO-REPLACE/REPLACEMENT-TEXT/'
b_laoshi
fonte
3

para manipulação de arquivos de configuração

Eu vim com esta solução inspirada na resposta Skensell

configLine [searchPattern] [replaceLine] [filePath]

será:

  • crie o arquivo se não existir
  • substitua a linha inteira (todas as linhas) em que searchPattern correspondeu
  • adicione replaceLine no final do arquivo se o padrão não foi encontrado

Função:

function configLine {
  local OLD_LINE_PATTERN=$1; shift
  local NEW_LINE=$1; shift
  local FILE=$1
  local NEW=$(echo "${NEW_LINE}" | sed 's/\//\\\//g')
  touch "${FILE}"
  sed -i '/'"${OLD_LINE_PATTERN}"'/{s/.*/'"${NEW}"'/;h};${x;/./{x;q100};x}' "${FILE}"
  if [[ $? -ne 100 ]] && [[ ${NEW_LINE} != '' ]]
  then
    echo "${NEW_LINE}" >> "${FILE}"
  fi
}

a mágica do status de saída maluca vem de https://stackoverflow.com/a/12145797/1262663

Grão
fonte
2
bash-4.1$ new_db_host="DB_HOSTNAME=good replaced with 122.334.567.90"
bash-4.1$ 
bash-4.1$ sed -i "/DB_HOST/c $new_db_host" test4sed
vim test4sed
'
'
'
DB_HOSTNAME=good replaced with 122.334.567.90
'

Funciona bem

nkl
fonte
1

No meu makefile eu uso isso:

@sed -i '/.*Revision:.*/c\'"`svn info -R main.cpp | awk '/^Rev/'`"'' README.md

PS: NÃO se esqueça de que o -i altera realmente o texto no arquivo ... portanto, se o padrão que você definiu como "Revisão" mudar, você também mudará o padrão a ser substituído.

Exemplo de saída:

Abc-Project escrito por John Doe

Revisão: 1190

Portanto, se você definir o padrão "Revisão: 1190", obviamente não é o mesmo que você definiu como "Revisão:" apenas ...

Martin Pfeffer
fonte
1
cat find_replace | while read pattern replacement ; do
sed -i "/${pattern}/c ${replacement}" file    
done 

O arquivo find_replace contém 2 colunas, c1 com o padrão correspondente, c2 com a substituição, o loop sed substitui cada linha contendo um dos padrões da variável 1

sjordi
fonte
Não, isso está errado em vários aspectos. Execute seduma vez com um arquivo de script contendo todas as substituições que você deseja executar. Executar sed -ino mesmo arquivo repetidamente é um antipadrão horrível.
Tripleee
0

É tão parecido com o anterior.

sed 's/[A-Za-z0-9]*TEXT_TO_BE_REPLACED.[A-Za-z0-9]*/This line is removed by the admin./'
Annapureddy Hari
fonte
3
Isso muda FOO=TEXT_TO_BE_REPLACEDpara FOO=This line ...isso não atende à especificação.
Jens
Sim. Nosso requisito é substituir a linha inteira por 'Esta linha é removida pelo administrador'. se encontrarmos a chave pattren 'TEXT_TO_BE_REPLACED'. O comando acima é satisfatório. Corrija-me se o meu entendimento é errado .. @Jens
Annapureddy Hari
@AnnapureddyHari, esta resposta não funcionará se o texto antes ou depois da sequência de pesquisa tiver algo além de A-Za-z0-9. Ele falha se houver um sinal de igual, como Jens apontou. A parte "FOO =" permanecerá; você não substituiu a linha inteira. Este código é míope sobre o que pode estar no arquivo. Se você quer dizer curinga, deve colocar curinga, como mostra a resposta de Thor.
msouth
0

O comando abaixo está funcionando para mim. Que está trabalhando com variáveis

sed -i "/\<$E\>/c $D" "$B"
Ritesh Borkar
fonte
Mas meu novo requisito é ignorar a linha comentada (começa com #) durante a substituição. Como estamos substituindo a linha completa, isso também substituirá as linhas comentadas e você terá propriedades duplicadas. Se alguém tiver uma solução para isso, por favor me avise.
Ritesh Borkar
-1

Costumo usar regex para extrair dados de arquivos que acabei de usar para substituir a citação literal \"por //nada :-)

cat file.csv | egrep '^\"([0-9]{1,3}\.[0-9]{1,3}\.)' | sed  s/\"//g  | cut -d, -f1 > list.txt
staM
fonte