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?

RomanM
sumber
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"'@'
Norman Ramsey
sumber
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.

Jeach
sumber
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
Paulo Fidalgo
sumber
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|"
Thales Ceolin
sumber
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.

Eddie
sumber
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>
yurenchen
sumber
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.

Adeger
sumber
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

PsychoData
sumber
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
Mamadou Lamine Diatta
sumber
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
aniskop
sumber