การแทนที่ตัวแปรสภาพแวดล้อมใน sed


213

หากฉันรันคำสั่งเหล่านี้จากสคริปต์:

#my.sh
PWD=bla
sed 's/xxx/'$PWD'/'
...
$ ./my.sh
xxx
bla

ไม่เป็นไร

แต่ถ้าฉันวิ่ง:

#my.sh
sed 's/xxx/'$PWD'/'
...
$ ./my.sh
$ sed: -e expression #1, char 8: Unknown option to `s' 

ฉันอ่านในบทช่วยสอนเกี่ยวกับการแทนที่ตัวแปรสภาพแวดล้อมจากเชลล์คุณต้องหยุดและ 'ออกใบเสนอราคา' $varnameส่วนนั้นเพื่อไม่ให้ถูกแทนที่โดยตรงซึ่งเป็นสิ่งที่ฉันทำและจะใช้ได้ผลก็ต่อเมื่อมีการกำหนดตัวแปรไว้ก่อน

ฉันจะทำให้ sed รับรู้ a $varเป็นตัวแปรสภาพแวดล้อมตามที่กำหนดไว้ในเชลล์ได้อย่างไร


5
$ PWD มี a / ซึ่งกำลังสิ้นสุดคำสั่งแทนที่
derobert

@derobert: tnx. หนึ่งในวิธีแก้ไขปัญหานี้ ...
RomanM

4
ใช้set -xในเชลล์เพื่อให้เชลล์สะท้อนคำสั่งแต่ละคำสั่งก่อนที่จะดำเนินการ สิ่งนี้สามารถล้างความสับสนได้มาก (นอกจากนี้ฉันมักใช้set -uเพื่อทำให้ตัวแปรที่ไม่ได้ตั้งค่าการอ้างอิงเป็นข้อผิดพลาดอย่างหนัก (ดูset -eด้วย))
bobbogo

ฉันหวังว่าจะหาวิธีให้ sed จัดการกับตัวแปรสภาพแวดล้อมเพื่อไม่ให้ค่าใช้จ่ายรั่วไหลลงในตารางกระบวนการดูเหมือนว่า sed เป็นเครื่องมือที่ไม่ถูกต้องในการติดตั้งความลับตามคำตอบทั้งหมดในหัวข้อนี้
ThorSummoner

คำตอบ:


325

ตัวอย่างทั้งสองของคุณมีลักษณะเหมือนกันซึ่งทำให้วินิจฉัยปัญหาได้ยาก ปัญหาที่อาจเกิดขึ้น:

  1. คุณอาจต้องใช้เครื่องหมายคำพูดคู่เช่นเดียวกับใน sed 's/xxx/'"$PWD"'/'

  2. $PWDอาจมีเครื่องหมายทับซึ่งในกรณีนี้คุณต้องหาอักขระที่ไม่มีอยู่$PWDเพื่อใช้เป็นตัวคั่น

อาจจะตอกทั้งสองประเด็นพร้อมกัน

sed 's@xxx@'"$PWD"'@'

แต่ฉันสามารถใช้อักขระใดที่ฉันรู้ว่าจะไม่ปรากฏในชื่อพา ธ
RomanM

5
คุณสามารถมีผู้สมัครหลายคนเช่น @ #%! และตรวจสอบด้วยนิพจน์กรณีเพื่อดูว่า $ PWD มีหรือไม่ เช่นกรณี "$ PWD" ของ@ ) ;; *) delim = "@" ;; esac; ทำซ้ำจนกว่า $ delim จะไม่ว่างเปล่า
Norman Ramsey

มีทางเลือกอื่นแทนการใช้เครื่องหมายคำพูดคู่ ดูคำตอบของฉันด้านล่าง
Thales Ceolin

คุณสามารถใช้ตัวคั่นอื่นสำหรับ sed คุณไม่จำเป็นต้องใช้ / คุณสามารถใช้ได้เช่นกันหากตัวแปรสภาพแวดล้อมของคุณเป็น url
Guchelkaben

จะเกิดอะไรขึ้นถ้าสตริงมี \ ตามด้วย n - จะหยุด sed ไม่ให้แปลงเป็นอักขระขึ้นบรรทัดเดียวได้อย่างไร
Max Waterman

141

นอกเหนือจากคำตอบของ Norman Ramsey แล้วฉันต้องการเพิ่มว่าคุณสามารถอ้างถึงสตริงทั้งหมดสองครั้งได้ (ซึ่งอาจทำให้ข้อความอ่านง่ายขึ้นและมีข้อผิดพลาดน้อยลง)

ดังนั้นหากคุณต้องการค้นหา 'foo' และแทนที่ด้วยเนื้อหาของ $ BAR คุณสามารถใส่คำสั่ง sed ในเครื่องหมายคำพูดคู่ได้

sed 's/foo/$BAR/g'
sed "s/foo/$BAR/g"

ในครั้งแรก $ BAR จะขยายไม่ถูกต้องในขณะที่ $ BAR ที่สองจะขยายได้อย่างถูกต้อง


4
สิ่งนี้สะอาดกว่าการใช้คำพูดคู่คำพูดเดี่ยว ฯลฯ
Vladislavs Dovgalecs

นี่คือสิ่งที่ฉันต้องใช้เพื่อให้ตัวแปรสภาพแวดล้อมขยายอย่างถูกต้องในคำสั่งนี้: sed -i "s / 127.0.0.1 / 127.0.0.1 localhost $ HOSTNAME /" hosts
izikandrw

3
นี้เป็นที่เรียบร้อย แต่มันไม่ทำงานเมื่อคุณต้องการที่จะทำบางแทนที่ซับซ้อนมากขึ้นเช่น"2,$s/^/$foo/"เป็น$sได้รับการตีความว่าเป็นตัวแปรเกินไปและมันไม่ควร
mjp

63

คุณสามารถใช้อักขระอื่นนอกเหนือจาก "/" ในการทดแทน:

sed "s#$1#$2#g" -i FILE

ในกรณีเฉพาะของฉัน $ 2 เป็นเส้นทางไฟล์ดังนั้นจึงsedถูก จำกัด เนื่องจากการตีความ/ในเนื้อหาของ $ 2 นี่คือสิ่งที่ฉันต้องการเพื่อผ่านมันไปให้ได้ ขอบคุณสำหรับเคล็ดลับที่ยอดเยี่ยม!
Boyd Hemphill

57

อีกทางเลือกง่ายๆ:

เนื่องจาก$PWDมักจะมีเครื่องหมายทับ/ให้ใช้|แทน/คำสั่ง sed:

sed -e "s|xxx|$PWD|"

5
คุณพูดว่า "ทางเลือกในการใช้เครื่องหมายคำพูดคู่" แต่ตัวอย่างของคุณใช้เครื่องหมายคำพูดคู่หรือไม่?
Jeach

ฉันเดาว่าประเด็นคือมันไม่ได้ใช้เครื่องหมายคำพูดคู่โดยตรง$PWD... ?
คริสเตียน

15

เมื่อแก้ไขคำถามของคุณฉันเห็นปัญหาของคุณ สมมติว่าไดเร็กทอรีปัจจุบันคือ/home/yourname ... ในกรณีนี้คำสั่งของคุณด้านล่าง:

sed 's/xxx/'$PWD'/'

จะขยายเป็น

sed `s/xxx//home/yourname//

ซึ่งไม่ถูกต้อง คุณต้องใส่\อักขระไว้หน้าแต่ละตัว/ใน $ PWD ของคุณหากคุณต้องการทำสิ่งนี้


แต่ PWD ถูกกำหนดโดยเชลล์ ... ถ้าฉันไป echo $ PWD ฉันจะได้ pwd
RomanM

1
แล้วคุณจะหลีกหนีตัวแปรสภาพแวดล้อมได้อย่างไร?
einpoklum

1
คำตอบที่เลือกอธิบายวิธีแก้ปัญหา ... อย่าใช้เครื่องหมายทับสำหรับตัวคั่น
Eddie

ซึ่งจะกลายเป็นปัญหาทันทีที่ไดเร็กทอรีการทำงานปัจจุบันของคุณมีอักขระอื่นนั้น
blubberdiblub

7

วิธีที่ไม่ดี: เปลี่ยนตัวคั่น

sed 's/xxx/'"$PWD"'/'
sed 's:xxx:'"$PWD"':'
sed 's@xxx@'"$PWD"'@'

บางทีนั่นอาจไม่ใช่คำตอบสุดท้าย

คุณไม่สามารถเป็นที่รู้จักในสิ่งที่ตัวละครจะเกิดขึ้นใน$PWD, / :หรือ@หรือ

$PWDวิธีที่ดีคือแทนที่อักขระพิเศษใน

วิธีที่ดี: หลีกเลี่ยงตัวคั่น

ตัวอย่างเช่น: พยายามแทนที่URLเป็น $ url (มี: /ในเนื้อหา)

x.com:80/aa/bb/aa.js

ในสตริง $ tmp

<a href="URL">URL</a>

ก. ใช้/เป็นตัวคั่น

echo ${url//\//\\/}
x.com:80\/aa\/bb\/aa.js

echo ${url//\//\/}
x.com:80/aa/bb/aa.js

echo "${url//\//\/}"
x.com:80\/aa\/bb\/aa.js

echo $tmp | sed "s/URL/${url//\//\\/}/"
<a href="x.com:80/aa/bb/aa.js">URL</a>

echo $tmp | sed "s/URL/${url//\//\/}/"
<a href="x.com:80/aa/bb/aa.js">URL</a>

หรือ

ข. ใช้:เป็นตัวคั่น (อ่านได้มากกว่า/)

echo ${url//:/\:}
x.com:80/aa/bb/aa.js

echo "${url//:/\:}"
x.com\:80/aa/bb/aa.js

echo $tmp | sed "s:URL:${url//:/\:}:g"
<a href="x.com:80/aa/bb/aa.js">x.com:80/aa/bb/aa.js</a>

3

อันที่จริงสิ่งที่ง่ายที่สุด (ใน gnu sed อย่างน้อยที่สุด) คือการใช้ตัวคั่นอื่นสำหรับคำสั่ง sed substitution (s) ดังนั้นแทนที่จะใช้ s / pattern / '$ mypath' / ถูกขยายเป็น s / pattern // my / path / ซึ่งแน่นอนว่าจะทำให้คำสั่ง s สับสนให้ใช้ s! pattern! '$ mypath'! ซึ่งจะขยายเป็น s! pattern! / my / path! ฉันใช้ตัวละครปัง (!) (หรือใช้อะไรก็ได้ที่คุณชอบ) ซึ่งจะหลีกเลี่ยงการใช้เครื่องหมายทับไปข้างหน้าตามปกติ แต่โดยไม่หมายถึงตัวเลือกเดียวเป็นตัวคั่น


3
VAR=8675309
echo "abcde:jhdfj$jhbsfiy/.hghi$jh:12345:dgve::" |\
sed 's/:[0-9]*:/:'$VAR':/1' 

โดยที่ VAR ประกอบด้วยสิ่งที่คุณต้องการแทนที่ฟิลด์ด้วย


3

การจัดการกับตัวแปรภายใน sed

[root@gislab00207 ldom]# echo domainname: None > /tmp/1.txt

[root@gislab00207 ldom]# cat /tmp/1.txt

domainname: None

[root@gislab00207 ldom]# echo ${DOMAIN_NAME}

dcsw-79-98vm.us.oracle.com

[root@gislab00207 ldom]# cat /tmp/1.txt | sed -e 's/domainname: None/domainname: ${DOMAIN_NAME}/g'

 --- Below is the result -- very funny.

domainname: ${DOMAIN_NAME}

 --- You need to single quote your variable like this ... 

[root@gislab00207 ldom]# cat /tmp/1.txt | sed -e 's/domainname: None/domainname: '${DOMAIN_NAME}'/g'


--- The right result is below 

domainname: dcsw-79-98vm.us.oracle.com

ฉันกำลังดิ้นรนเพื่อให้สิ่งนี้ทำงานในไปป์ไลน์ Azure DevOps YAML ความคิดเห็นนี้ช่วยให้ฉันสามารถใช้เคล็ดลับนี้ในการโทเค็นไฟล์กำหนดค่าบางไฟล์ได้สำเร็จ
andov

1

ฉันมีปัญหาที่คล้ายกันฉันมีรายการและฉันต้องสร้างสคริปต์ SQL ตามเทมเพลต (ที่มี@INPUT@เป็นองค์ประกอบที่จะแทนที่):

for i in LIST 
do
    awk "sub(/\@INPUT\@/,\"${i}\");" template.sql >> output
done
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.