Substitusi dalam file teks ** tanpa ** ekspresi reguler

68

Saya perlu mengganti beberapa teks di dalam file teks dengan pengganti. Biasanya saya akan melakukan sesuatu seperti

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

Masalahnya adalah bahwa kedua textdan replacementstring kompleks yang mengandung tanda hubung, garis miring, blackslashes, kutipan dan sebagainya. Jika saya melarikan diri semua karakter yang diperlukan di textdalam hal itu menjadi tidak dapat dibaca dengan cepat. Di sisi lain saya tidak perlu kekuatan ekspresi reguler: Saya hanya perlu mengganti teks secara harfiah.

Apakah ada cara untuk melakukan subtitusi teks tanpa menggunakan ekspresi reguler dengan beberapa perintah bash?

Agak sepele untuk menulis naskah yang melakukan ini, tetapi saya pikir sudah ada sesuatu yang sudah ada.

Andrea
sumber
Diperlukan untuk melakukannya melalui bash? Solusi sederhana adalah dengan membuka di Word dan melakukanfind and replace all
Akash
17
@ashash Karena sistem yang bashselalu dikirim dengan Microsoft Word? ;) Tidak .. Hanya bercanda. OP mungkin ingin melakukan ini pada mesin jarak jauh atau untuk banyak file.
slhck
@ slhck :) Yah, saya kira gedit harus memiliki opsi serupa
Akash
Suatu opsi adalah entah bagaimana melarikan diri dengan benar segala sesuatu sebelum meneruskannya sed, yang mungkin merupakan upaya yang sia-sia mengingat semua sakelar dan perbedaan platform.
l0b0
terkait: stackoverflow.com/questions/29613304/…
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

Jawaban:

6

Saat Anda tidak membutuhkan kekuatan ekspresi reguler, jangan gunakan itu. Ini baik saja.
Tapi, ini bukan ekspresi reguler .

sed 's|literal_pattern|replacement_string|g'

Jadi, jika /masalah Anda, gunakan |dan Anda tidak perlu melarikan diri dari yang pertama.

ps: tentang komentar, lihat juga jawaban Stackoverflow pada Escape a string for sed search pattern .


Pembaruan: Jika Anda baik-baik saja menggunakan Perl, coba \Qdan gunakan \Eini,
perl -pe 's|\Qliteral_pattern\E|replacement_string|g'
RedGrittyBrickjuga menyarankan trik serupa dengan sintaks Perl yang lebih kuat dalam komentar di sini.

nik
sumber
Terima kasih, saya tidak tahu tentang perbedaan antara / dan |
Andrea
64
Saya tidak yakin jawaban ini bermanfaat ... Satu-satunya perbedaan antara s|||dan s///adalah bahwa karakter pemisah berbeda dan sehingga satu karakter tidak perlu melarikan diri. Anda bisa melakukan hal yang sama s###. Masalah sebenarnya di sini adalah bahwa OP tidak mau harus khawatir tentang melarikan diri dari isi literal_pattern(yang tidak literal sama sekali dan akan ditafsirkan sebagai sebuah regex).
Benj
15
Ini tidak akan menghindari penafsiran karakter khusus lainnya. Bagaimana jika mencari 1234.*aaadengan solusi Anda itu lebih cocok dari yang dimaksudkan 1234\.\*aaa.
Matteo
20
Jawaban ini tidak boleh diterima
Steven Lu
2
Ini sepenuhnya melenceng. Teks yang akan dicocokkan bisa berisi keanehan apa pun. Dalam kasus saya ini adalah kata sandi acak. Anda tahu bagaimana
hasilnya
13
export FIND='find this'
export REPLACE='replace with this'
ruby -p -i -e "gsub(ENV['FIND'], ENV['REPLACE'])" path/to/file

Ini adalah satu-satunya solusi aman 100% di sini, karena:

  • Ini adalah substitusi statis, bukan regexp, tidak perlu melarikan diri apa pun (dengan demikian, lebih unggul daripada menggunakan sed)
  • Tidak akan pecah jika string Anda mengandung }char (dengan demikian, lebih unggul daripada solusi Perl yang dikirimkan)
  • Itu tidak akan putus dengan karakter apa pun, karena ENV['FIND']digunakan, tidak $FIND. Dengan $FINDatau teks Anda digarisbawahi dalam kode Ruby, Anda dapat menemukan kesalahan sintaks jika string Anda berisi yang tidak terhapuskan '.
Pembuat sekarang
sumber
Saya harus menggunakan export FIND='find this; export REPLACE='replace with this';dalam skrip bash saya sehingga ENV['FIND']dan ENV['replace']memiliki nilai yang diharapkan. Saya mengganti beberapa string terenkripsi yang sangat lama dalam sebuah file. Ini hanya tiketnya.
DMfll
Ini adalah jawaban jawaban yang bagus karena dapat diandalkan dan ruby ​​ada di mana-mana. Berdasarkan jawaban ini saya sekarang menggunakan skrip shell ini .
loevborg
Sayangnya tidak berfungsi ketika FIND berisi banyak baris.
adrelanos
Tidak ada yang akan mencegahnya bekerja dengan banyak baris di FIND. Gunakan kutipan ganda \ n.
Sekarang,
7

The replaceperintah akan melakukan hal ini.

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

Ubah di tempat:

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

Untuk stdout:

replace text replacement < path/to/the/file

Contoh:

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

The replaceperintah datang dengan MySQL atau MariaDB.

Derek Veit
sumber
3
memperhitungkan bahwa penggantian itu sudah usang dan mungkin tidak dapat disangkal di masa depan
Rogelio
1
Mengapa perintah dasar seperti itu datang dengan database?
masterxilo
3
@ masterxilo Pertanyaan yang lebih baik mungkin - mengapa perintah dasar seperti itu tidak datang dengan sistem operasi modern? ;-)
Mark Thomson
3

Anda juga dapat menggunakan \Qmekanisme perl untuk " mengutip (menonaktifkan) metakarakter pola "

perl -pe 'BEGIN {$text = q{your */text/?goes"here"}} s/\Q$text\E/replacement/g'
glenn jackman
sumber
3
Atauperl -pe 's(\Qyour */text/?goes"here")(replacement)' file
RedGrittyBrick
3

lihat skrip Perl saya. itu melakukan apa yang Anda butuhkan tanpa menggunakan ekspresi reguler reguler:

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

sangat berguna bukan? Saya harus belajar Perl untuk melakukannya. karena saya benar-benar sangat membutuhkannya.

Samer Ata
sumber
2

Anda dapat melakukannya dengan keluar dari pola Anda. Seperti ini:

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'

Kredit untuk solusi ini ada di sini: https://stackoverflow.com/questions/407523/escape-a-string-for-a-sed-replace-pattern

Catatan1: ini hanya berfungsi untuk kata kunci yang tidak kosong. Kata kunci kosong tidak diterima oleh sed ( sed -e 's//replacement/').

Note2: sayangnya, saya tidak tahu alat populer yang TIDAK akan menggunakan regexp-s untuk menyelesaikan masalah. Anda dapat menulis alat seperti itu di Rust atau C, tetapi tidak ada di sana secara default.

VasyaNovikov
sumber
Ini benar-benar merindukan poin OP. Jelas Anda bisa lepas dari polanya, tetapi untuk beberapa pola ini membosankan.
icecreamsword
@icecreamsword apakah Anda membaca jawaban saya di bawah baris pertama? Script lolos secara otomatis .
VasyaNovikov
1

Saya mengumpulkan beberapa jawaban lain dan muncul dengan ini:

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
sumber
Tidak bekerja dengan baris baru. Juga tidak membantu untuk menghindari baris baru dengan \n. Ada solusi?
adrelanos
1

Anda dapat menggunakan str_replace php :

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

Catatan: Anda masih harus lolos dari tanda kutip tunggal 'dan ganda ".

simlev
sumber
0

Node.JS setara dengan @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;});});'
DI
sumber
0

Inilah satu lagi cara kerja "hampir".

Gunakan vi atau vim.

Buat file teks dengan subtitusi Anda di dalamnya:

:% sno / string pencarian saya \\ "-: # 2; g ('. j'); \\"> / replacestring saya = \\ "bac) (o: # 46; \\"> /
: x

kemudian jalankan vi atau vim dari commandline:

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

:% sno adalah perintah vi untuk melakukan pencarian dan penggantian tanpa sihir.

Saya adalah pemisah yang saya pilih.

: x menyimpan dan keluar vi.

Anda harus lolos dari garis miring terbalik '\' forwardslash '/' dapat diganti dengan misalnya tanda tanya '?' atau sesuatu yang tidak ada dalam pencarian Anda atau ganti-string, pipa '|' tidak bekerja untuk saya 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
sumber