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.

Andrea
fonte
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
related: stackoverflow.com/questions/29613304/...
Ciro Santilli新疆改造中心法轮功六四事件

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

nik
fonte
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 '.
Nowaker
fonte
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.

Derek Veit
fonte
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

Você também pode usar o \Qmecanismo do perl para " citar (desabilitar) metacaracteres padrão "

perl -pe 'BEGIN {$text = q{your */text/?goes"here"}} s/\Q$text\E/replacement/g'
Glenn Jackman
fonte
3
Ouperl -pe 's(\Qyour */text/?goes"here")(replacement)' file
RedGrittyBrick
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.

Samer Ata
fonte
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.

VasyaNovikov
fonte
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" "$@"
}
Xiong Chiamiov
fonte
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 ".

simlev
fonte
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;});});'
AT
fonte
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

Samuel Åslund
fonte