Thay thế môi trường trong sed


202

Nếu tôi chạy các lệnh này từ một tập lệnh:

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

nó ổn

Nhưng, nếu tôi chạy:

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

Tôi đã đọc trong hướng dẫn rằng để thay thế các biến môi trường từ shell, bạn cần dừng lại và 'trích dẫn' $varnamephần để nó không được thay thế trực tiếp, đó là những gì tôi đã làm và chỉ hoạt động nếu biến được xác định ngay trước đó.

Làm thế nào tôi có thể có được sed để nhận ra một $varbiến môi trường như được định nghĩa trong shell?


4
$ PWD chứa a / đang kết thúc lệnh thay thế.
derobert

@derobert: tnx. Một trong những giải pháp giải quyết vấn đề này ...
RomanM

4
Sử dụng set -xtrong shell để lấy shell để lặp lại từng lệnh ngay trước khi nó thực thi chúng. Điều này có thể làm sáng tỏ rất nhiều nhầm lẫn. (Ngoài ra, tôi thường sử dụng set -uđể biến các biến unset tham chiếu thành một lỗi khó. (Xem thêm set -e.))
bobbogo

Tôi đã hy vọng tìm ra cách để sed xử lý các biến môi trường vì không rò rỉ các giá trị vào bảng quy trình, có vẻ như sed là công cụ sai để cài đặt bí mật theo tất cả các câu trả lời trong chuỗi này
ThorSummoner

Câu trả lời:


302

Hai ví dụ của bạn trông giống hệt nhau, điều này làm cho vấn đề khó chẩn đoán. Vấn đề tiềm ẩn:

  1. Bạn có thể cần báo giá gấp đôi, như trong sed 's/xxx/'"$PWD"'/'

  2. $PWDcó thể chứa dấu gạch chéo, trong trường hợp đó bạn cần tìm một ký tự không có trong đó $PWDđể sử dụng làm dấu phân cách.

Để giải quyết cả hai vấn đề cùng một lúc, có lẽ

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

Nhưng, sau đó tôi có thể sử dụng ký tự nào tôi biết chắc chắn sẽ không xuất hiện trong tên đường dẫn?
RomanM

5
Bạn có thể có một vài ứng cử viên như @ #%! và kiểm tra với một biểu thức trường hợp để tìm xem $ PWD có chúng không. Ví dụ: trường hợp "$ PWD" của @ ) ;; *) delim = "@" ;; esac; lặp lại cho đến khi $ delim không trống.
Norman Ramsey

Có một cách khác thay vì sử dụng dấu ngoặc kép. Xem câu trả lời của tôi dưới đây.
Thales Ceolin

Bạn có thể sử dụng một dấu phân cách khác cho sed. Bạn không cần sử dụng / bạn cũng có thể sử dụng, nếu biến môi trường của bạn là một url.
Guchelkaben

123

Ngoài câu trả lời của Norman Ramsey, tôi muốn nói thêm rằng bạn có thể trích dẫn toàn bộ chuỗi (điều này có thể khiến câu lệnh dễ đọc hơn và ít bị lỗi hơn).

Vì vậy, nếu bạn muốn tìm kiếm 'foo' và thay thế nó bằng nội dung của $ BAR, bạn có thể đặt lệnh sed trong dấu ngoặc kép.

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

Trong lần đầu tiên, $ BAR sẽ không mở rộng chính xác trong khi trong $ BAR thứ hai sẽ mở rộng chính xác.


4
Điều này sạch sẽ hơn so với lộn xộn với dấu ngoặc kép, dấu ngoặc đơn, v.v.
Vladislavs Dovgalecs

đây là những gì tôi đã phải sử dụng để có được biến môi trường để mở rộng chính xác trong lệnh này: sed -i "s / 127.0.0.1 / 127.0.0.1 localhost $ HOSTNAME /" hosts
izikandrw

2
Điều này là gọn gàng nhưng nó không hoạt động khi bạn muốn thực hiện một số thay thế phức tạp hơn, chẳng hạn "2,$s/^/$foo/"như $sđược hiểu là một biến quá và nó không nên.
mjp

59

Bạn có thể sử dụng các ký tự khác ngoài "/" thay thế:

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

Trong trường hợp cụ thể của tôi, $ 2 là một đường dẫn tệp, do đó, sedbarfing do giải thích /nội dung của $ 2. Đây chính xác là những gì tôi cần để vượt qua nó. Cảm ơn fora mẹo tuyệt vời!
Boyd Hemphill

54

Một cách khác dễ dàng:

$PWDthường sẽ chứa một dấu gạch chéo /, hãy sử dụng |thay /cho câu lệnh sed:

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

4
Bạn đã nói "một cách thay thế cho việc sử dụng dấu ngoặc kép" và ví dụ của bạn sử dụng dấu ngoặc kép?
Jeach

Tôi đoán vấn đề là nó không sử dụng dấu ngoặc kép trực tiếp xung quanh $PWD...?
Christian

14

Với câu hỏi của bạn chỉnh sửa, tôi thấy vấn đề của bạn. Giả sử thư mục hiện tại là /home/yourname ... trong trường hợp này, lệnh của bạn bên dưới:

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

sẽ được mở rộng đến

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

không hợp lệ Bạn cần đặt một \ký tự trước mỗi ký tự trong /$ PWD nếu bạn muốn làm điều này.


nhưng PWD được xác định bởi shell ... nếu tôi lặp lại $ PWD tôi sẽ nhận được pwd
RomanM

1
Vì vậy, làm thế nào để bạn thoát khỏi các biến môi trường?
einpoklum

1
Câu trả lời được chọn mô tả một cách giải quyết ... không sử dụng dấu gạch chéo cho dấu phân cách
Eddie

Điều này sẽ trở thành một vấn đề ngay khi thư mục làm việc hiện tại của bạn có chứa ký tự khác đó.
blubberdiblub

6

cách xấu: thay đổi dấu phân cách

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

có lẽ những người không phải là câu trả lời cuối cùng

bạn không thể biết nhân vật nào sẽ xuất hiện trong $PWD, / :HOẶC @.

cách tốt là thay thế nhân vật đặc biệt trong $PWD.

cách tốt: thoát khỏi dấu phân cách

ví dụ: cố gắng thay thế URLdưới dạng $ url (có : /nội dung)

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

trong chuỗi $ tmp

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

a. sử dụng /như dấu phân cách

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>

HOẶC LÀ

b. sử dụng :như dấu phân cách (dễ đọc hơn /)

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

Trên thực tế, điều đơn giản nhất (ít nhất là trong gnu sed) là sử dụng một dấu phân cách khác cho lệnh thay thế sed (s). Vì vậy, thay vì s / mẫu / '$ mypath' / được mở rộng thành s / mẫu // my / path / tất nhiên sẽ gây nhầm lẫn cho lệnh s, hãy sử dụng s! Pattern! '$ Mypath'! sẽ được mở rộng thành s! mẫu! / my / path! Tôi đã sử dụng ký tự bang (!) (Hoặc sử dụng bất cứ thứ gì bạn thích) để tránh dấu gạch chéo thông thường, nhưng không có nghĩa là lựa chọn duy nhất của bạn làm dấu phân cách.


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

trong đó VAR chứa những gì bạn muốn thay thế trường bằng


3

Xử lý VARIABLES trong 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

Tôi đã vật lộn để làm việc này trong các đường ống YAML của Azure DevOps. Nhận xét này đã giúp tôi sử dụng thành công thủ thuật này để mã hóa một số tệp cấu hình.
andov

1

Tôi gặp vấn đề tương tự, tôi đã có một danh sách và tôi phải xây dựng một tập lệnh SQL dựa trên khuôn mẫu (có chứa @INPUT@phần tử để thay thế):

for i in LIST 
do
    awk "sub(/\@INPUT\@/,\"${i}\");" template.sql >> output
done
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.