Substitusi variabel lingkungan dalam sed


202

Jika saya menjalankan perintah ini dari skrip:

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

itu baik-baik saja.

Tapi, jika saya jalankan:

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

Saya membaca dalam tutorial bahwa untuk mengganti variabel lingkungan dari shell Anda harus berhenti, dan 'mengutip' $varnamebagian sehingga tidak diganti secara langsung, yang saya lakukan, dan yang berfungsi hanya jika variabel didefinisikan segera sebelumnya.

Bagaimana saya bisa mendapatkan sed untuk mengenali $varvariabel lingkungan seperti yang didefinisikan dalam shell?


4
$ PWD berisi / yang mengakhiri perintah pengganti.
derobert

@derobert: tnx. Salah satu solusi mengatasi ini ...
RomanM

4
Gunakan set -xdi shell untuk mendapatkan shell untuk mengulangi setiap perintah sebelum dieksekusi. Ini dapat menghilangkan banyak kebingungan. (Juga, saya sering menggunakan set -uuntuk membuat de-referensi variabel yang tidak disetel kesalahan yang sulit. (Lihat set -ejuga.))
bobbogo

Saya berharap menemukan cara bagi sed untuk menangani variabel lingkungan agar tidak membocorkan nilai ke dalam tabel proses, sepertinya sed adalah alat yang salah untuk memasang rahasia sesuai dengan semua jawaban di utas ini
ThorSummoner

Jawaban:


302

Dua contoh Anda terlihat identik, yang membuat masalah sulit didiagnosis. Masalah potensial:

  1. Anda mungkin perlu tanda kutip ganda, seperti pada sed 's/xxx/'"$PWD"'/'

  2. $PWDmungkin mengandung garis miring, dalam hal ini Anda perlu menemukan karakter yang tidak terkandung$PWD untuk digunakan sebagai pembatas.

Untuk mengatasi kedua masalah sekaligus, mungkin

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

tapi, lalu karakter apa yang bisa saya gunakan saya tahu pasti tidak akan muncul dalam nama path
RomanM

5
Anda dapat memiliki beberapa kandidat seperti @ #%! dan periksa dengan ekspresi case untuk menemukan apakah $ PWD memilikinya. Misalnya, huruf "$ PWD" dari @ ) ;; *) delim = "@"; esac; ulangi hingga $ delim tidak kosong.
Norman Ramsey

Ada alternatif lain daripada menggunakan tanda kutip ganda. Lihat jawaban saya di bawah ini.
Thales Ceolin

Anda dapat menggunakan pembatas lain untuk sed. Anda tidak perlu menggunakan / Anda dapat menggunakan, juga jika variabel lingkungan Anda adalah url.
Guchelkaben

123

Selain jawaban Norman Ramsey, saya ingin menambahkan bahwa Anda dapat mengutip dua kali seluruh string (yang dapat membuat pernyataan lebih mudah dibaca dan lebih sedikit kesalahan cenderung).

Jadi jika Anda ingin mencari 'foo' dan menggantinya dengan konten $ BAR, Anda dapat menyertakan perintah sed dalam tanda kutip ganda.

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

Dalam yang pertama, $ BAR tidak akan berkembang dengan benar sedangkan di $ BAR kedua akan berkembang dengan benar.


4
Ini lebih bersih daripada bermain-main dengan tanda kutip ganda, tanda kutip tunggal dll.
Vladislavs Dovgalecs

inilah yang harus saya gunakan untuk mendapatkan variabel lingkungan untuk berkembang dengan benar dalam perintah ini: sed -i "s / 127.0.0.1 / 127.0.0.1 localhost $ HOSTNAME /" hosts
izikandrw

2
Ini adalah rapi tetapi tidak bekerja ketika Anda ingin melakukan beberapa pergantian pemain yang lebih kompleks, seperti "2,$s/^/$foo/"yang $sakan ditafsirkan sebagai variabel juga dan seharusnya tidak.
mjp

59

Anda dapat menggunakan karakter lain selain "/" sebagai pengganti:

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

Dalam kasus khusus saya, $ 2 adalah path file, jadi sedmuntah karena menafsirkan /dalam isi $ 2. Inilah tepatnya yang saya butuhkan untuk melewatinya. Terima kasih atas tip yang bagus!
Boyd Hemphill

54

Alternatif mudah lainnya:

Karena $PWDbiasanya akan berisi garis miring /, gunakan |sebagai ganti /untuk pernyataan sed:

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

4
Anda mengatakan "alternatif untuk menggunakan tanda kutip ganda" namun contoh Anda menggunakan tanda kutip ganda?
Jeach

Saya kira intinya adalah tidak menggunakan tanda kutip ganda langsung di sekitar $PWD...?
Kristen

14

Dengan edit pertanyaan Anda, saya melihat masalah Anda. Katakanlah direktori saat ini adalah /home/yourname ... dalam hal ini, perintah Anda di bawah ini:

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

akan diperluas ke

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

yang tidak valid Anda harus meletakkan \karakter di depan masing-masing /dalam $ PWD Anda jika Anda ingin melakukan ini.


tapi PWD didefinisikan oleh shell ... jika saya pergi echo $ PWD saya mendapatkan pwd yang
RomanM

1
Jadi, bagaimana Anda lolos dari variabel lingkungan?
einpoklum

1
Jawaban yang dipilih menjelaskan pemecahan masalah ... jangan gunakan garis miring untuk pembatas
Eddie

Yang akan menjadi masalah segera setelah direktori kerja Anda saat ini berisi karakter lain itu.
blubberdiblub

6

cara buruk: ubah pembatas

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

mungkin itu bukan jawaban terakhir,

Anda tidak dapat mengetahui karakter apa yang akan muncul $PWD, / :ATAU @.

cara yang baik adalah mengganti karakter spesial di $PWD.

cara yang baik: melarikan diri pembatas

misalnya: coba ganti URLsebagai $ url (ada : /dalam konten)

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

dalam string $ tmp

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

Sebuah. gunakan /sebagai pembatas

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>

ATAU

b. gunakan :sebagai pembatas (lebih mudah dibaca daripada /)

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

Sebenarnya, hal paling sederhana (setidaknya di gnu sed) adalah menggunakan pemisah yang berbeda untuk perintah substitusi sed. Jadi alih-alih s / pattern / '$ mypath' / sedang diperluas ke s / pattern // my / path / yang tentu saja akan membingungkan perintah s, gunakan s! Pattern! '$ Mypath'! yang akan diperluas ke s! pattern! / my / path! Saya telah menggunakan karakter bang (!) (Atau menggunakan apa pun yang Anda suka) yang menghindari potongan garis depan yang biasa, tetapi tidak berarti hanya pilihan Anda sebagai pemisah.


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

di mana VAR berisi bidang yang ingin Anda ganti


3

Berurusan dengan VARIABEL dalam 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

Saya sedang berjuang untuk membuatnya bekerja di jaringan pipa Azure DevOps YAML. Komentar ini membantu saya untuk berhasil menggunakan trik ini untuk menandai beberapa file konfigurasi.
andov

1

Saya memiliki masalah yang sama, saya punya daftar dan saya harus membangun skrip SQL berdasarkan template (yang berisi @INPUT@elemen untuk menggantikan):

for i in LIST 
do
    awk "sub(/\@INPUT\@/,\"${i}\");" template.sql >> output
done
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.