ฉันจะใช้ตัวแปรใน LHS และ RHS ของการทดแทน sed ได้อย่างไร


61

ฉันต้องการทำ:

cat update_via_sed.sh | sed 's/old_name/new_name/' > new_update_via_sed.sh

ในโปรแกรมของฉัน

แต่ฉันต้องการใช้ตัวแปรเช่น

old_run='old_name_952'
new_run='old_name_953'

ฉันลองใช้แล้ว แต่การทดแทนไม่ได้เกิดขึ้น (ไม่มีข้อผิดพลาด) ฉันเหนื่อย:

cat update_via_sed.sh | sed 's/old_run/new_run/'
cat update_via_sed.sh | sed 's/$old_run/$new_run/'
cat update_via_sed.sh | sed 's/${old_run}/${new_run}/'

2
คุณสามารถหาคำตอบในการใช้พารามิเตอร์ในการโต้เถียงคำสั่งที่
จัดการระหว่าง

คำตอบ:


45

คุณสามารถทำได้:

sed "s/$old_run/$new_run/" < infile > outfile

แต่ระวังว่า$old_runจะถูกนำมาเป็นปกติและการแสดงออกเพื่อให้ตัวอักษรพิเศษใด ๆ ที่ตัวแปรมีเช่น/หรือ.จะต้องหนี ในทำนองเดียวกันใน$new_runที่&และ\ตัวละครจะต้องได้รับการปฏิบัติเป็นพิเศษและคุณจะต้องหลบหนี/และการขึ้นบรรทัดใหม่ตัวละครในนั้น

ถ้าเนื้อหาของ$old_runหรือ$new_runไม่อยู่ภายใต้การควบคุมของคุณแล้วมันสำคัญที่จะดำเนินการหนีที่หรือรหัสที่มีจำนวนช่องโหว่ฉีดรหัส


3
ฉันใช้เครื่องหมายคำพูดเดี่ยวใน sed params เปลี่ยนเป็นเครื่องหมายคำพูดคู่และทำงานกับไฟล์ น่ากลัว
Aakash

ไม่ใช่sedเพียงอย่างเดียวที่จะไม่ใช้ regex แต่sed -Eจะ?
Kiwy

@ กีวี่ไม่ -eคือการระบุsed e xpressionเพื่อให้คุณสามารถส่งมากกว่าหนึ่ง (ในsed -e exp1 -e exp2) หรือการรวมกันของไฟล์และการแสดงออก ( sed -f file -e exp) คุณอาจสับสนกับ-Eการใช้งานบางอย่างบอกให้เกลี้ยกล่อมให้ใช้ ERE แทน BRE
Stéphane Chazelas

ฉันพิมพ์ผิด แต่ตกลงว่าอธิบายว่าทำไมฉันมีปัญหามากมายในการใช้ sed โดยที่-Eฉันไม่ได้ควบคุม regex แบบขยายไม่ใช่แบบปกติ ขอบคุณสำหรับคำอธิบาย
Kiwy

@ Kiwy ดูคำถามและคำตอบที่เชื่อมโยงเพื่อดูตัวเลือกเพิ่มเติมที่รองรับโดยsedการใช้งานบางอย่างสำหรับไวยากรณ์นิพจน์ทั่วไปที่มากขึ้น
Stéphane Chazelas

31

สิ่งนี้ได้ผล:

cat update_via_sed.sh | sed 's/'"$old_run"'/'"$new_run"'/'

ตามที่ฉันต้องการ 'นำมาใช้ซ้ำ' ไฟล์เดียวกันฉันใช้มันสำหรับทุกคนที่ต้องการแนวทางที่คล้ายกัน:

cat update_via_sed.sh | sed 's/'"$old_run"'/'"$new_run"'/' > new_update; mv -f new_update update_via_sed.sh

ด้านบนสร้างไฟล์ใหม่แล้วลบไฟล์ปัจจุบันกว่าเปลี่ยนชื่อไฟล์ใหม่เป็นไฟล์ปัจจุบัน


4
คุณไม่จำเป็นต้องใช้ cat, mv หรือส่วนเสริม '' เว้นแต่คุณจะมีอักขระพิเศษอยู่ในนั้น sed -i s/"$old"/"$new"/ update_via_sed.shดังนั้นจึงเป็น อย่างไรก็ตามโปรดทราบว่านี่ไม่สามารถใช้งานได้กับค่าเก่าและใหม่ เช่นมันจะต้องไม่มี / etc เนื่องจาก $ old ยังคงเป็นการค้นหาและ $ new เป็นรูปแบบการแทนที่ที่อักขระบางตัวมีความหมายพิเศษ
frostschutz

1
sed -i "s/$old/$new/"ก็โอเคด้วย (ข้อควรระวังด้านบน) sedโดยปกติจะถือว่าตัวคั่นเป็นอักขระตัวแรกหลังจากsคำสั่ง - เช่นถ้าตัวแปรของคุณมีเครื่องหมายทับ/แต่ไม่อนุญาตให้พูดที่ ( @) คุณสามารถใช้ "s @ $ old @ $ new @"
เตอร์

22

คุณสามารถใช้ตัวแปรใน sed ได้ตราบใดที่มันอยู่ในเครื่องหมายคำพูดคู่ (ไม่ใช่เครื่องหมายคำพูดเดี่ยว):

sed "s/$var/r_str/g" file_name >new_file

หากคุณมีเครื่องหมายทับ ( /) ในตัวแปรให้ใช้ตัวคั่นอื่นเช่นด้านล่าง

sed "s|$var|r_str|g" file_name >new_file

1
+1 สำหรับการกล่าวถึงจำเป็นต้องใช้เครื่องหมายคำพูดคู่ นั่นคือปัญหาของฉัน
speckledcarp

1
สิ่งที่ฉันกำลังมองหารวมถึงการใช้|เช่นเดียวกับในกรณีของฉันตัวแปรมีเส้นทางดังนั้นมันมี/อยู่ในนั้น
yorch

ด้วยเหตุผลบางอย่างท่อ|ทำงานสำหรับฉันใน MacOS 10.14 แต่ตัวคั่นอื่น ๆ ชอบ@หรือ#ไม่ ฉันมีเครื่องหมายทับใน env var ของฉันด้วย
davidenke

21

sed 'in-place' (usng the -i flag) คือคำตอบ ขอบคุณที่ปีเตอร์

sed -i "s@$old@$new@" file

2
คุณมีคำพูดในที่ผิด เครื่องหมายคำพูดจะต้องอยู่รอบ ๆ ตัวแปรไม่ใช่s@(ซึ่งไม่พิเศษสำหรับเชลล์ในทางใดทางหนึ่ง) sed -i "s@$old@$new@" fileที่ดีที่สุดคือการใส่คำพูดทุกอย่างภายในเช่น โปรดทราบว่า-iไม่ใช่sedตัวเลือกมาตรฐาน
Stéphane Chazelas

ฉันจะหลบหนีได้อย่างไร$ในคำสั่งนี้ เหมือนฉันกำลังจะเปลี่ยนเป็นทั้งค่าของตัวแปร$xxx $JAVA_HOMEฉันลองsed -i "s@\$xxx@$JAVA_HOME@" fileแล้ว แต่ข้อผิดพลาดบอกว่าno previous regular expression
hakunami

3

man bash ให้สิ่งนี้เกี่ยวกับข้อความเดียว

การใส่อักขระในเครื่องหมายคำพูดเดียวจะคงคุณค่าของตัวอักษรแต่ละตัวไว้ในเครื่องหมายคำพูด เครื่องหมายคำพูดเดี่ยวอาจไม่เกิดขึ้นระหว่างเครื่องหมายคำพูดเดี่ยวแม้ว่าจะนำหน้าด้วยเครื่องหมายทับขวา

อะไรก็ตามที่คุณพิมพ์บนบรรทัดคำสั่ง bash ตีความมันแล้วส่งผลลัพธ์ไปยังโปรแกรมที่ควรจะถูกส่งไปในกรณีนี้ถ้าคุณใช้sed 's/$old_run/$new_run/'bash จะเห็นเป็นครั้งแรกsedมันจะรับรู้ว่ามันเป็นสิ่งที่ปฏิบัติการได้ใน$PATHตัวแปร . sedปฏิบัติการต้องมีการป้อนข้อมูล 's/$old_run/$new_run/'ทุบตีดูสำหรับการป้อนข้อมูลและพบว่า คำพูดเดียวบอกว่าทุบตีจะไม่ตีความเนื้อหาในพวกเขาและส่งพวกเขาตามที่พวกเขาเป็น ดังนั้นทุบตีแล้วส่งพวกเขาไปยัง sed Sed ให้ข้อผิดพลาดเนื่องจาก$สามารถเกิดขึ้นได้เฉพาะตอนท้ายบรรทัด

แต่ถ้าเราใช้เครื่องหมายคำพูดคู่เช่น"s/$old_run/$new_run/"bash จะเห็นสิ่งนี้และตีความ$old_runว่าเป็นชื่อตัวแปรและทำการทดแทน (เฟสนี้เรียกว่าการขยายตัวของตัวแปร) นี่คือสิ่งที่เราต้องการ

แต่คุณต้องระวังการใช้เครื่องหมายอัญประกาศคู่เพราะพวกเขาจะตีความก่อนโดยทุบตีแล้วมอบให้กับ sed ดังนั้นสัญลักษณ์บางอย่างเช่น `ต้องถูกหลบหนีก่อนใช้งาน


1

มีความเสี่ยงต่อการเป็นคนคล่องแคล่ว - คุณได้พิจารณาperlแล้ว

ซึ่ง - เหนือสิ่งอื่นใด - ค่อนข้างดีในการดำเนินการ 'สไตล์แบบสบาย ๆ ' แต่ยังเป็นแบบเต็มรูปแบบที่น่าสนใจยิ่งขึ้น

มันมีลักษณะเหมือนในตัวอย่างของสิ่งที่คุณกำลังจริงพยายามที่จะทำคือการเพิ่มจำนวน

ดังนั้นวิธีการเกี่ยวกับ:

#!/usr/bin/env perl
use strict;
use warnings 'all'; 

while ( <DATA> ) {
    s/old_name_(\d+)/"old_name_".($1+1)/e;
    print;
}

__DATA__
old_name_932

หรือเป็นหนึ่งซับ - sed สไตล์:

perl -pe 's/old_name_(\d+)/"old_name_".($1+1)/e'

1
$ echo $d1 | sed -i 's/zkserver1/'${d1}'/g' deva_file
sed: -e expression #1, char 26: unterminated `s' command 

ในตัว$d1ฉันกำลังเก็บค่า

ฉันไม่สามารถแทนที่ค่าจากตัวแปรดังนั้นฉันลองคำสั่งต่อไปนี้ว่ามันทำงาน

echo $d1 | sed -i 's/zkserver1/'"${d1}"'/g' deva_file

ไม่มีเครื่องหมายคำพูดคู่ใน"${d1}"นั่นคือข้อผิดพลาด


-2

สันนิษฐาน$old_runและ$new_runมีการตั้งค่าแล้ว:

cat update_via_sed.sh | eval $(print "sed 's/$old_run/$new_run/g'")

แสดงการเปลี่ยนแปลงบนหน้าจอ คุณสามารถเปลี่ยนเส้นทางไปยังไฟล์หากจำเป็น


-2

สำหรับการใช้ตัวแปรใน sed ให้ใช้เครื่องหมายอัญประกาศคู่ "" เพื่อล้อมรอบตัวแปรในรูปแบบที่คุณใช้ ตัวอย่างเช่น: a = "Hi" หากคุณต้องการต่อท้ายตัวแปร 'a' กับรูปแบบคุณสามารถใช้ด้านล่าง: sed -n "1, / pattern" $ {a} "pattern / p" ชื่อไฟล์


2
การขยายตัวของ$aไม่ได้ยกมาที่นี่ซึ่งหมายความว่าช่องว่างใด ๆ ในค่าของมันจะก่อให้เกิดการแยกคำในเปลือก
Kusalananda

-2

คุณยังสามารถใช้ Perl:

perl -pi -e ""s/$val1/$val2/g"" file

พิจารณาตัวแปรที่อาจมีช่องว่างในค่าของพวกเขา สตริงว่างรอบการแสดงออก Perl ( "") จะไม่ทำอะไรเลย
Kusalananda

-2

ทำลายสตริง

bad => linux เห็นสตริง $ old_run:

sed 's/$old_run/$new_run/'

ดี => linux ดูตัวแปร $ old_run:

sed 's/'$old_run'/'$new_run'/'

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