การทดแทนในไฟล์ข้อความ ** ที่ไม่มี ** การแสดงออกปกติ


68

ฉันต้องการแทนที่ข้อความภายในไฟล์ข้อความด้วยการแทนที่ โดยปกติฉันจะทำอะไรเช่น

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

ปัญหาคือทั้งสองtextและreplacementเป็นสตริงที่ซับซ้อนที่มีเครื่องหมายขีดกลาง, เครื่องหมายทับ, blackslashes, คำพูดและอื่น ๆ ถ้าฉันหลบหนีอักขระที่จำเป็นทั้งหมดภายในtextสิ่งนั้นจะกลายเป็นอ่านไม่ได้ ในขณะที่ฉันไม่ต้องการพลังของการแสดงออกปกติ: ฉันแค่ต้องการแทนที่ข้อความอย่างแท้จริง

มีวิธีการแทนที่ข้อความโดยไม่ใช้นิพจน์ทั่วไปด้วยคำสั่ง bash หรือไม่?

มันค่อนข้างจะเป็นเรื่องเล็กน้อยที่จะเขียนสคริปต์ที่ทำสิ่งนี้ แต่ฉันคิดว่าควรจะมีบางสิ่งอยู่แล้ว


จำเป็นต้องทำมันด้วยการทุบตี? วิธีแก้ปัญหาแบบง่าย ๆ คือการเปิดใน Word และทำfind and replace all
Akash

17
@akash เพราะระบบที่มาbashพร้อมกับ Microsoft Word? ;) ไม่เพียงแค่ล้อเล่น. OP อาจต้องการทำสิ่งนี้บนเครื่องระยะไกลหรือสำหรับชุดของไฟล์
slhck

@slhck :) เอาล่ะฉันเดาว่า gedit ควรมีตัวเลือกที่คล้ายกัน
Akash

ตัวเลือกจะหนีไปอย่างถูกต้องทุกอย่างก่อนที่จะผ่านไปsedซึ่งอาจเป็นความพยายามที่ไร้ประโยชน์เมื่อพิจารณาถึงสวิตช์และความแตกต่างของแพลตฟอร์มทั้งหมด
l0b0

คำตอบ:


6

เมื่อคุณไม่ต้องการพลังของการแสดงออกปกติอย่าใช้มัน นั่นเป็นเรื่องปกติ
แต่นี้ไม่ได้จริงๆแสดงออกปกติ

sed 's|literal_pattern|replacement_string|g'

ดังนั้นหาก/ปัญหาของคุณใช้|และคุณไม่จำเป็นต้องหลบหนีอดีต

PS: เกี่ยวกับความคิดเห็นที่ยังเห็นนี้คำตอบใน Stackoverflow หนีสตริงสำหรับรูปแบบการค้นหา sed


อัปเดต: หากคุณพอใจกับการใช้Perlลองด้วย\Qและ\Eเช่นนี้
perl -pe 's|\Qliteral_pattern\E|replacement_string|g'
RedGrittyBrickได้แนะนำเคล็ดลับที่คล้ายกันกับไวยากรณ์ Perl ที่แข็งแกร่งขึ้นในความคิดเห็นที่นี่


ขอบคุณฉันไม่รู้เกี่ยวกับความแตกต่างระหว่าง / และ |
Andrea

64
ฉันไม่แน่ใจว่าคำตอบนี้มีประโยชน์ ... ข้อแตกต่างระหว่างs|||และs///คือตัวคั่นนั้นแตกต่างกันและเพื่อให้ตัวละครตัวหนึ่งไม่จำเป็นต้องหลบหนี s###คุณสามารถทำอะไรได้อย่างเท่าเทียมกัน ปัญหาจริงที่นี่คือ OP ไม่ต้องการที่จะกังวลเกี่ยวกับการหลบหนีเนื้อหาของliteral_pattern(ซึ่งไม่ได้เป็นตัวอักษรเลยและจะถูกตีความว่าเป็น regex)
Benj

15
สิ่งนี้จะไม่หลีกเลี่ยงการตีความตัวละครพิเศษอื่น ๆ เกิดอะไรขึ้นถ้าการค้นหาด้วยการแก้ปัญหาของคุณมันตรงมากขึ้นกว่าที่ตั้งใจไว้1234.*aaa 1234\.\*aaa
Matteo

20
คำตอบนี้ไม่ควรได้รับการยอมรับ
Steven Lu

2
สิ่งนี้พลาดจุดทั้งหมด ข้อความที่จะจับคู่อาจมีความผิดปกติใด ๆ ในกรณีของฉันมันเป็นรหัสผ่านแบบสุ่ม คุณรู้ไหมว่ามันเป็นอย่างไร
Christian Bongiorno

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

นี่เป็นโซลูชั่นที่ปลอดภัยเพียง 100% ที่นี่เพราะ:

  • มันเป็นสถานีย่อยคงที่ไม่ใช่ regexp ไม่จำเป็นต้องหลบหนีอะไรเลย (ดังนั้นจะดีกว่าการใช้sed)
  • มันจะไม่แตกถ้าสตริงของคุณมี}อักขระ (ดังนั้นจะดีกว่าโซลูชัน Perl ที่ส่งมา)
  • มันจะไม่ทำลายด้วยตัวอักษรใด ๆ เพราะถูกนำมาใช้ไม่ได้ENV['FIND'] $FINDด้วย$FINDหรือข้อความของคุณ inlined 'ในรหัสทับทิมคุณสามารถตีไวยากรณ์ผิดพลาดถ้าสตริงของคุณมีค่า Escape

ฉันต้องใช้export FIND='find this; export REPLACE='replace with this';ในสคริปต์ทุบตีของฉันเพื่อให้ENV['FIND']และENV['replace']มีค่าที่คาดหวัง ฉันกำลังแทนที่สตริงการเข้ารหัสที่ยาวมาก ๆ ในไฟล์ นี่เป็นเพียงตั๋ว
DMfll

นี่เป็นคำตอบที่ดีเพราะมีความน่าเชื่อถือและทับทิมเป็นที่แพร่หลาย จากคำตอบนี้ฉันใช้เชลล์สคริปต์นี้
loevborg

น่าเสียดายที่ใช้ไม่ได้เมื่อ FIND มีหลายบรรทัด
adrelanos

ไม่มีสิ่งใดที่จะป้องกันไม่ให้ทำงานกับหลายบรรทัดใน FIND ใช้เครื่องหมายคำพูดคู่ \ n
Nowaker

7

replaceคำสั่งจะทำเช่นนี้

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

เปลี่ยนสถานที่:

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

หากต้องการ stdout:

replace text replacement < path/to/the/file

ตัวอย่าง:

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

replaceคำสั่งมาพร้อมกับ MySQL หรือ MariaDB


3
คำนึงถึงการแทนที่นั้นเลิกใช้แล้วและอาจไม่สามารถอธิบายได้ในอนาคต
Rogelio

1
เหตุใดคำสั่งพื้นฐานเช่นนี้ในโลกจึงมาพร้อมกับฐานข้อมูล
masterxilo

3
@masterxilo อาจเป็นคำถามที่ดีกว่า - ทำไมคำสั่งพื้นฐานเช่นนี้จึงไม่มาพร้อมกับระบบปฏิบัติการสมัยใหม่? ;-)
Mark Thomson


3

ตรวจสอบสคริปต์ Perl ของฉัน มันทำสิ่งที่คุณต้องการโดยไม่ต้องใช้นิพจน์ปกติโดยปริยายหรืออย่างชัดเจน:

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

มีประโยชน์มากใช่ไหม ฉันต้องเรียนรู้ Perl เพื่อทำมัน เพราะฉันต้องการมันจริงๆ


2

คุณสามารถทำได้โดยหนีจากรูปแบบของคุณ แบบนี้:

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'

เครดิตสำหรับโซลูชันนี้จะอยู่ที่นี่: https://stackoverflow.com/questions/407523/escape-a-string-for-a-sed-replace-pattern

Note1: ใช้งานได้กับคำหลักที่ไม่ว่างเท่านั้น sed คำหลักที่ว่างเปล่าไม่ได้รับการยอมรับ ( sed -e 's//replacement/')

Note2: น่าเสียดายที่ฉันไม่รู้จักเครื่องมือยอดนิยมที่จะไม่ใช้ regexp-s เพื่อแก้ปัญหา คุณสามารถเขียนเครื่องมือเช่นนี้ใน Rust หรือ C แต่ไม่มีค่าเริ่มต้น


สิ่งนี้พลาดจุดของ OP อย่างสมบูรณ์ เห็นได้ชัดว่าคุณสามารถหลบหนีรูปแบบได้ แต่สำหรับบางรูปแบบนี่น่าเบื่อ
icecreamsword

@icecreamsword คุณอ่านคำตอบของฉันด้านล่างบรรทัดแรกหรือไม่ สคริปต์ไม่หนีออกมาโดยอัตโนมัติ
VasyaNovikov

1

ฉันสรุปคำตอบอื่น ๆ อีกสองสามข้อแล้วก็มาด้วยกัน:

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นอกจากนี้ยังไม่ได้ช่วยในการหลบหนีการขึ้นบรรทัดใหม่ด้วย ทางออกใด ๆ
adrelanos

1

คุณสามารถใช้str_replaceของ php :

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

หมายเหตุ:คุณยังคงต้องหลบหนีราคาเดียว'และราคาคู่"แม้ว่า


0

Node.JS เทียบเท่า @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

นี่เป็นวิธีการทำงาน "เกือบ" อีกอย่างหนึ่ง

ใช้ vi หรือ vim

สร้าง textfile ด้วยการทดแทนของคุณ:

:% sno / สตริงการค้นหาของฉัน \\ "-: # 2; g ('. j'); \\"> / replacestring ของฉัน = \\ "bac) (o: # 46; \\"> /
: x

จากนั้นเรียกใช้ vi หรือ vim จาก commandline:

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

:% sno เป็นคำสั่ง vi เพื่อทำการค้นหาและแทนที่โดยไม่มีเวทย์มนตร์

/ เป็นตัวคั่นที่ฉันเลือก

: x บันทึกและออกจาก vi

คุณต้องหลีกเลี่ยงแบ็กสแลช '\' forwardslash '/' อาจถูกแทนที่ด้วยเช่นเครื่องหมายคำถาม '?' หรืออย่างอื่นที่ไม่ได้อยู่ในการค้นหาของคุณหรือแทนที่สตริง, ไพพ์ '|' ไม่ได้ผลสำหรับฉันสรรพสินค้าใหญ่

อ้างอิง: 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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.