Làm thế nào tôi có thể sử dụng các biến trong LHS và RHS của một sự thay thế sed?


61

Tôi muốn làm:

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

trong chương trình của tôi.

Nhưng tôi muốn sử dụng các biến, ví dụ

old_run='old_name_952'
new_run='old_name_953'

Tôi đã thử sử dụng chúng nhưng sự thay thế không xảy ra (không có lỗi). Tôi đã thử:

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
Bạn có thể tìm thấy câu trả lời trong Sử dụng tham số trong đối số lệnh .
manatwork

Câu trả lời:


45

Bạn có thể làm:

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

Nhưng hãy cẩn thận, đó $old_runsẽ được coi là một biểu thức chính quy và vì vậy bất kỳ ký tự đặc biệt nào mà biến chứa, chẳng hạn như /hoặc .sẽ phải được thoát . Tương tự, trong $new_run, các ký tự &\ký tự sẽ cần được xử lý đặc biệt và bạn sẽ phải thoát các /ký tự và dòng mới trong đó.

Nếu nội dung của $old_runhoặc $new_runkhông nằm trong tầm kiểm soát của bạn, thì điều quan trọng là phải thực hiện việc thoát đó, hoặc nếu không thì mã đó gây ra lỗ hổng tiêm mã .


3
Tôi đã sử dụng dấu ngoặc đơn trong thông số sed, chuyển sang dấu ngoặc kép và tập tin đã hoạt động. Tuyệt vời.
Aakash

Không phải là sedmột mình sẽ không sử dụng regex mà là sed -Esao?
Kiwy

@Kiwy, không. -elà để xác định một sed e xpression , vì vậy bạn có thể vượt qua nhiều hơn một (như trong sed -e exp1 -e exp2) hoặc sự kết hợp các tập tin và các biểu thức ( sed -f file -e exp). Bạn có thể nhầm lẫn với -Eđiều đó với một số triển khai, nói với sed sử dụng ERE thay vì BREs.
Stéphane Chazelas

Tôi đã nhầm, nhưng OK, điều đó giải thích tại sao tôi có quá nhiều vấn đề khi sử dụng sed mà không -Elàm chủ được regex mở rộng chứ không phải thông thường. Cảm ơn các explanaiton.
Kiwy

@Kiwy, xem thêm Hỏi & Đáp được liên kết để biết thêm các tùy chọn được hỗ trợ bởi một số sedtriển khai cho các cú pháp biểu thức chính quy hơn.
Stéphane Chazelas

31

Điều này đã làm việc:

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

Vì tôi muốn 'sử dụng lại' cùng một tệp, tôi thực sự sử dụng tệp này cho bất kỳ ai muốn có cách tiếp cận tương tự:

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

Ở trên đã tạo một tệp mới sau đó xóa tệp hiện tại thay vì đổi tên tệp mới thành tệp hiện tại.


4
Bạn không cần mèo, mv hoặc thêm '' trừ khi bạn sẽ có các ký tự đặc biệt trong đó. Vậy đó sed -i s/"$old"/"$new"/ update_via_sed.sh. Tuy nhiên, hãy lưu ý rằng điều này không hoạt động đối với bất kỳ giá trị cũ và mới. ví dụ: không được chứa / v.v., vì $ old vẫn là một tìm kiếm và $ new một mẫu thay thế trong đó một số ký tự có ý nghĩa đặc biệt.
frostschutz

1
sed -i "s/$old/$new/"cũng ok (với các biện pháp phòng ngừa ở trên). sedthường coi dấu phân cách là ký tự đầu tiên sau slệnh - tức là nếu các biến của bạn chứa dấu gạch chéo /nhưng không cho phép tại ( @), bạn có thể sử dụng "s @ $ old @ $ new @".
peterph

22

Bạn có thể sử dụng các biến trong sed miễn là nó trong một trích dẫn kép (không phải là một trích dẫn):

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

Nếu bạn có dấu gạch chéo ( /) trong biến thì hãy sử dụng dấu phân cách khác như bên dưới

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

1
+1 để đề cập đến việc cần sử dụng dấu ngoặc kép. Đó là vấn đề của tôi.
speckledcarp

1
Chính xác những gì tôi đang tìm kiếm, bao gồm cả việc sử dụng |như trong trường hợp của tôi, các biến chứa một đường dẫn để nó có /trong đó
yorch

Vì một số lý do, đường ống |hoạt động với tôi trên MacOS 10.14, nhưng các trình phân tách khác có thích @hay #không. Tôi đã có dấu gạch chéo trong var env của tôi quá.
davidenke

21

'in-place' sed (usng the -i flag) là câu trả lời. Cảm ơn peterph.

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

2
Bạn có dấu ngoặc kép ở sai chỗ. dấu ngoặc kép phải xoay quanh các biến, không s@(không đặc biệt đối với shell theo bất kỳ cách nào). Tốt nhất là đặt mọi thứ bên trong trích dẫn như thế nào sed -i "s@$old@$new@" file. Lưu ý rằng đó -ikhông phải là một sedlựa chọn tiêu chuẩn .
Stéphane Chazelas

Làm thế nào tôi có thể thoát $trong lệnh này. Giống như, tôi sẽ thay đổi $xxxtoàn bộ thành giá trị của một biến $JAVA_HOME. Tôi đã thử sed -i "s@\$xxx@$JAVA_HOME@" file, nhưng lỗi nóino previous regular expression
hakunami

3

man bash đưa ra điều này về trích dẫn duy nhất

Các ký tự kèm theo trong dấu ngoặc đơn duy trì giá trị theo nghĩa đen của từng ký tự trong dấu ngoặc kép. Một trích dẫn có thể không xảy ra giữa các trích dẫn đơn, ngay cả khi trước dấu gạch chéo ngược.

Bất cứ điều gì bạn gõ vào dòng lệnh, bash giải thích nó và sau đó nó sẽ gửi kết quả cho chương trình nó là vụ phải được gửi to.In trường hợp này, nếu bạn sử dụng sed 's/$old_run/$new_run/', bash đầu tiên nhìn thấy sed, nó nhận ra nó như là một món quà thực thi trong $PATHbiến . Việc sedthực thi đòi hỏi một đầu vào. Bash tìm kiếm đầu vào và tìm thấy 's/$old_run/$new_run/'. Các trích dẫn đơn nói bash không diễn giải nội dung trong đó và vượt qua chúng như hiện tại. Vì vậy, bash sau đó chuyển chúng cho sed. Sed đưa ra lỗi vì chỉ $có thể xảy ra ở cuối dòng.

Thay vào đó, nếu chúng ta sử dụng dấu ngoặc kép, nghĩa là, "s/$old_run/$new_run/"sau đó bash xem điều này và diễn giải $old_runnhư một tên biến và thực hiện thay thế (giai đoạn này được gọi là mở rộng biến). Đây thực sự là những gì chúng tôi yêu cầu.

Nhưng, bạn phải cẩn thận khi sử dụng dấu ngoặc kép bởi vì, chúng được giải thích trước tiên bằng bash và sau đó được trao cho sed. Vì vậy, một số ký hiệu như `phải được thoát trước khi sử dụng chúng.


1

Có nguy cơ bị flippant - bạn đã xem xét perl.

Mà - trong số những thứ khác - khá giỏi trong việc thực hiện các thao tác 'kiểu sed', nhưng cũng là một thứ lập trình đầy đủ tính năng hơn.

vẻ như trong ví dụ của bạn, những gì bạn thực sự đang cố gắng làm là tăng số.

Vậy làm thế nào về:

#!/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

hoặc như một lớp lót - phong cách 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 

Trong $d1tôi đang lưu trữ các giá trị

Tôi không thể thay thế giá trị từ biến, vì vậy tôi đã thử lệnh sau nó đang hoạt động

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

thiếu dấu ngoặc kép trong "${d1}"đó, đó là lỗi


-2

Giả sử $old_run$new_runđã được thiết lập:

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

Điều này cho thấy sự thay đổi trên màn hình. Bạn có thể chuyển hướng đầu ra thành tập tin nếu cần.


-2

Để sử dụng một biến trong sed, sử dụng dấu ngoặc kép "", để đặt biến đó, trong mẫu mà bạn đang sử dụng. Ví dụ: a = "Hi" Nếu bạn muốn thêm biến 'a' vào mẫu, bạn có thể sử dụng tên tệp dưới đây: sed -n "1, / mẫu" $ {a} "mẫu / p"


2
Việc mở rộng không $ađược trích dẫn ở đây, điều đó có nghĩa là bất kỳ khoảng trắng nào trong giá trị của nó sẽ gọi từ tách trong vỏ.
Kusalananda

-2

Bạn cũng có thể sử dụng Perl:

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

Xem xét các biến có thể có khoảng trắng trong giá trị của chúng. Các chuỗi trống xung quanh biểu thức Perl ( "") sẽ không làm gì cả.
Kusalananda

-2

phá vỡ chuỗi

xấu => linux thấy một chuỗi $ old_run:

sed 's/$old_run/$new_run/'

tốt => linux thấy một biến $ old_run:

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

1
Và nếu $old_runhoặc $new_runchứa không gian?
Kusalananda
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.