Substituição no arquivo de texto ** sem ** expressões regulares


68

Preciso substituir algum texto dentro de um arquivo de texto por um substituto. Normalmente eu faria algo como

sed -i 's/text/replacement/g' path/to/the/file

O problema é que ambos texte replacementsão cadeias complexas que contêm traços, barras, barras negras, aspas e assim por diante. Se eu escapar de todos os caracteres necessários, texta coisa se torna rapidamente ilegível. Por outro lado, não preciso do poder das expressões regulares: só preciso substituir o texto literalmente.

Existe uma maneira de fazer a substituição de texto sem usar expressões regulares com algum comando bash?

Seria bastante trivial escrever um script que faça isso, mas acho que já deveria existir algo.


Necessário fazê-lo através do bash? Uma solução simplista seria a de abrir no Word e fazer umfind and replace all
Akash

17
@akash Porque os sistemas que bashsempre são enviados com o Microsoft Word? ;) Não, é brincadeira. O OP pode querer fazer isso em uma máquina remota ou para um lote de arquivos.
slhck

@slhck :) Bem, eu acho gedit deve ter uma opção semelhante
Akash

Uma opção seria escapar de alguma forma corretamente de tudo antes de passá-lo sed, o que provavelmente é um esforço fútil, considerando todas as opções e diferenças de plataforma.
l0b0

Respostas:


6

Quando você não precisar do poder das expressões regulares, não o use. Está bem.
Mas, isso não é realmente uma expressão regular .

sed 's|literal_pattern|replacement_string|g'

Portanto, se /o problema é seu, use |e você não precisa escapar do primeiro.

ps: sobre os comentários, veja também esta resposta do Stackoverflow em Escape a string for sed search pattern .


Atualização: Se você está bem usando o Perl, experimente \Qe \Ecom isso,
perl -pe 's|\Qliteral_pattern\E|replacement_string|g'
RedGrittyBricktambém sugeriu um truque semelhante com a sintaxe do Perl mais forte em um comentário aqui


Obrigado, eu não sabia sobre a diferença entre / e |
Andrea

64
Não tenho certeza se essa resposta é útil ... A única diferença entre s|||e s///é que o caractere separador é diferente e, portanto, um caractere não precisa ser escapado. Você poderia fazer igualmente s###. O problema real aqui é que o OP não quer se preocupar em escapar do conteúdo de literal_pattern(que não é literal e será interpretado como um regex).
Benj

15
Isso não evitará a interpretação de outros caracteres especiais. E se a pesquisa 1234.*aaacom a sua solução corresponder muito mais do que o pretendido 1234\.\*aaa.
Matteo

20
Esta resposta deve não ser aceito
Steven Lu

2
Isso erra completamente o ponto. O texto a ser correspondido pode conter qualquer estranheza. No meu caso, é uma senha aleatória. Você sabe como os ir
Christian Bongiorno

13
export FIND='find this'
export REPLACE='replace with this'
ruby -p -i -e "gsub(ENV['FIND'], ENV['REPLACE'])" path/to/file

Esta é a única solução 100% segura aqui, porque:

  • É uma subestação estática, não uma regexp, não há necessidade de escapar de nada (portanto, superior ao uso sed)
  • Não quebrará se sua string contiver }char (portanto, superior a uma solução Perl enviada)
  • Não vai quebrar com nenhum personagem, porque ENV['FIND']é usado, não $FIND. Com $FINDou seu texto embutido no código Ruby, você pode encontrar um erro de sintaxe se a sua string contiver um escape '.

Eu tive que usar export FIND='find this; export REPLACE='replace with this';no meu script bash para isso ENV['FIND']e ENV['replace']tinha os valores esperados. Eu estava substituindo algumas seqüências criptografadas muito longas em um arquivo. Este foi apenas o bilhete.
DMfll

Esta é uma boa resposta, porque é confiável e o rubi é onipresente. Com base nesta resposta, agora uso este script de shell .
loevborg

Infelizmente, não funciona quando o FIND contém várias linhas.
adrelanos 26/01

Não há nada que o impeça de trabalhar com várias linhas no FIND. Use aspas duplas \ n.
Nowaker 27/01

7

O replacecomando fará isso.

https://linux.die.net/man/1/replace

Mudança no local:

replace text replacement -- path/to/the/file

Para stdout:

replace text replacement < path/to/the/file

Exemplo:

$ replace '.*' '[^a-z ]{1,3}' <<EOF
> r1: /.*/g
> r2: /.*/gi
> EOF
r1: /[^a-z ]{1,3}/g
r2: /[^a-z ]{1,3}/gi

O replacecomando vem com MySQL ou MariaDB.


3
ter em conta tht substituir é obsoleto e não pode ser disponible no futuro
Rogelio

11
Por que diabos esse comando básico vem com um banco de dados?
Masterxilo 8/08

3
@masterxilo Pode ser uma pergunta melhor - por que um comando tão básico não vem com os sistemas operacionais modernos? ;-)
Mark Thomson


3

confira meu script Perl. ele faz exatamente o que você precisa sem o uso implícito ou explícito da expressão regular:

https://github.com/Samer-Al-iraqi/Linux-str_replace

str_replace Search Replace File # replace in File in place

STDIN | str_replace Search Replace # to STDOUT

muito útil né? Eu tive que aprender Perl para fazer isso. porque eu realmente preciso disso.


2

Você pode fazer isso escapando de seus padrões. Como isso:

keyword_raw='1/2/3'
keyword_regexp="$(printf '%s' "$keyword_raw" | sed -e 's/[]\/$*.^|[]/\\&/g')"
# keyword_regexp is now '1\/2\/3'

replacement_raw='2/3/4'
replacement_regexp="$(printf '%s' "$replacement_raw" | sed -e 's/[\/&]/\\&/g')"
# replacement_regexp is now '2\/3\/4'

echo 'a/b/c/1/2/3/d/e/f' | sed -e "s/$keyword_regexp/$replacement_regexp/"
# the last command will print 'a/b/c/2/3/4/d/e/f'

Os créditos para essas soluções estão aqui: https://stackoverflow.com/questions/407523/escape-a-string-for-a-sed-replace-pattern

Nota1: isso funciona apenas para palavras-chave não vazias. Palavras-chave vazias não são aceitas pelo sed ( sed -e 's//replacement/').

Nota 2: infelizmente, não conheço uma ferramenta popular que NÃO usaria o regexp-s para resolver o problema. Você pode escrever essa ferramenta em Rust ou C, mas não está lá por padrão.


Isso perde completamente o ponto do OP. Obviamente, você pode escapar do padrão, mas para alguns padrões isso é tedioso.
icecreamsword

@icecreamsword você leu minha resposta abaixo da primeira linha? O script escapa automaticamente .
VasyaNovikov

1

Reuni algumas outras respostas e cheguei a isso:

function unregex {
   # This is a function because dealing with quotes is a pain.
   # http://stackoverflow.com/a/2705678/120999
   sed -e 's/[]\/()$*.^|[]/\\&/g' <<< "$1"
}
function fsed {
   local find=$(unregex "$1")
   local replace=$(unregex "$2")
   shift 2
   # sed -i is only supported in GNU sed.
   #sed -i "s/$find/$replace/g" "$@"
   perl -p -i -e "s/$find/$replace/g" "$@"
}

Não funciona com novas linhas. Também não ajuda a escapar de novas linhas \n. Qualquer solução?
adrelanos 29/03

1

Você pode usar o str_replace do php :

php -R 'echo str_replace("\|!£$%&/()=?^\"'\''","replace",$argn),PHP_EOL;'<input.txt >output.txt

Nota: Você ainda precisará escapar de aspas simples 'e aspas duplas ".


0

Node.JS equivalente a @Nowaker:

export FNAME='moo.txt'
export FIND='search'
export REPLACE='rpl'
node -e 'fs=require("fs");fs.readFile(process.env.FNAME,"utf8",(err,data)=>{if(err!=null)throw err;fs.writeFile(process.env.FNAME,data.replace(process.env.FIND,process.env.REPLACE),"utf8",e=>{if(e!=null)throw e;});});'

0

Aqui está mais uma "quase" maneira de trabalhar.

Use vi ou vim.

Crie um arquivo de texto com sua substituição:

:% sno / minha cadeia de pesquisa \\ "-: # 2; g ('. j'); \\"> / minha substituição = \\ "bac) (o: # 46; \\"> /
: x

execute o vi ou vim na linha de comando:

vi -S commandfile.txt path/to/the/file

:% sno é o comando vi para pesquisar e substituir sem mágica.

/ é o meu separador escolhido.

: x salva e sai vi.

Você precisa escapar das barras invertidas '\' a barra forwards '/' pode ser substituída por, por exemplo, um ponto de interrogação '?' ou qualquer outra coisa que não esteja na sua pesquisa ou na sequência de substituição, canal '|' não funcionou para mim tho.

ref: https://stackoverflow.com/questions/6254820/perform-a-non-regex-search-replace-in-vim https://vim.fandom.com/wiki/Search_without_need_to_escape_slash http://linuxcommand.org/ lc3_man_pages / vim1.html

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.