Cách chuyển một biến có chứa dấu gạch chéo sang sed


100

Làm thế nào để bạn chuyển một biến có chứa dấu gạch chéo dưới dạng một mẫu cho sed?

Ví dụ: nếu tôi có biến sau:

var="/Users/Documents/name/file"

Tôi muốn chuyển nó thành sednhư vậy:

sed "s/$var/replace/g" "$file"

Tuy nhiên tôi nhận được lỗi. Làm cách nào để tránh được vấn đề?

Câu trả lời:


193

Sử dụng dấu phân cách regex thay thế vì sedcho phép bạn sử dụng bất kỳ dấu phân cách nào ( bao gồm cả các ký tự điều khiển ):

sed "s~$var~replace~g" $file

2
Không hoạt động. Tôi thử tệp sed -i "s ~ blah ~ $ var ~ g" và nhận được: "sed -e expression # 1, char 70: untermina` s 'command ". Tôi đã xác nhận rằng thủ phạm là một dấu gạch chéo trong $ var. Các biến thể của giải pháp này cho điều này ở khắp nơi và không có phương pháp nào hoạt động. Sed có được cập nhật gần đây không? Tôi đang sử dụng v4.2.2 trên Ubuntu 14.04.4 LTS.
Paul Ericson

Điều đó phụ thuộc vào những gì là giá trị của$var
anubhava

1
Không hoạt động với ~ như @PaulEricson đã quan sát, nhưng vẫn hoạt động với |
Kenny Ho

1
Cuối cùng "~" (sau khi g) có vẻ như không cần thiết, ít nhất là cho AWS của Amazon Linux (CentOS dựa)
Saul Martínez Vidals

1
Bạn đã lưu ngày của tôi
user7131571

61

Một câu trả lời cơ bản thuần túy: sử dụng mở rộng tham số để thoát khỏi dấu gạch chéo ngược bất kỳ dấu gạch chéo nào trong biến:

var="/Users/Documents/name/file"
sed "s/${var//\//\\/}/replace/g" $file

1
Đây là một cách tốt, bởi vì bạn không phải lúc nào cũng biết những ký tự nào chứa biến. Vì vậy, bạn luôn có thể thoát khỏi dấu phân cách sed nếu có nghi ngờ. Ví dụ: sed "s: $ {var //: / \\:}: Replace: g" $ file
Stephane L

Xinh đẹp. Làm cho một số tập lệnh đổi tên của tôi gọn gàng hơn nhiều.
Đám mây

Hôm nay học được một điều hay, cảm ơn. Nên là câu trả lời được chấp nhận.
Steve Kehlet

6
Một số rõ ràng về mô hình đang được sử dụng ở đây: ${parameter/pattern/string}. Vì vậy, trong trường hợp này, tham số là var, mẫu là /\/và chuỗi là \\/. Tất cả các bản sao của mẫu được thay thế vì mẫu bắt đầu bằng a /.
Josh

1
@josh, và vì vậy dấu gạch chéo ở đầu của mẫu không thực sự là một phần của mẫu để thay thế.
glenn jackman

32

Một cách khác để làm điều đó, mặc dù xấu hơn câu trả lời của anubhava, là thoát tất cả các dấu gạch chéo ngược varbằng cách sử dụng một sedlệnh khác :

var=$(echo "$var" | sed 's/\//\\\//g')

sau đó, điều này sẽ hoạt động:

sed "s/$var/replace/g" $file

12

Sử dụng /sed làm dấu phân tách sẽ xung đột với các dấu gạch chéo trong biến khi được thay thế và kết quả là bạn sẽ gặp lỗi. Một cách để tránh điều này là sử dụng một dấu phân tách khác là duy nhất từ ​​bất kỳ ký tự nào có trong biến đó.

var="/Users/Documents/name/file"

bạn có thể sử dụng ký tự octothorpe phù hợp với dịp (hoặc bất kỳ ký tự nào khác không phải là ký tự /để dễ sử dụng)

sed "s#$var#replace#g" 

hoặc là

sed 's#$'$var'#replace#g'

điều này phù hợp khi biến không chứa khoảng trắng

hoặc là

sed 's#$"'$var'"#replace#g'

Sẽ khôn ngoan hơn khi sử dụng cách trên vì chúng ta chỉ quan tâm đến việc thay thế bất cứ thứ gì có trong biến đó so với việc trích dẫn kép toàn bộ lệnh có thể khiến shell của bạn xen vào bất kỳ ký tự nào có thể được coi là ký tự shell đặc biệt vào shell.


Không hoạt động. Tôi thử tệp sed -i "s ~ blah ~ $ var ~ g" và nhận được: "sed -e expression # 1, char 70: untermina` s 'command ". Tôi đã xác nhận rằng thủ phạm là một dấu gạch chéo trong $ var. Các biến thể của giải pháp này cho điều này ở khắp nơi và không có phương pháp nào hoạt động. Sed có được cập nhật gần đây không? Tôi đang sử dụng v4.2.2 trên Ubuntu 14.04.4 LTS.
Paul Ericson

@PaulEricson Tôi không thể repro cái này trên bất kỳ Ubuntu nào có sẵn cho tôi. Nếu $varchứa của bạn ~, rõ ràng bạn sẽ cần phải chọn một dấu phân cách khác. Lỗi "chưa chấm dứt" có vẻ như của bạn $varchứa một dòng mới; bạn có thể sửa nó bằng cách thoát dấu gạch chéo ngược mỗi dòng mới trong giá trị, nhưng tôi không nghĩ rằng điều này là hoàn toàn di động. Điều này nói chung không liên quan đến vấn đề giảm giá trị.
tripleee

@PaulEricson Ồ và trích dẫn của bạn không chính xác, bạn không nên đặt tên tệp bên trong dấu ngoặc kép. sed -i "s~blah~$var~g" filechỉ với trích dẫn xung quanh sedkịch bản thực tế .
tripleee

5

Đây là một câu hỏi cũ, nhưng không có câu trả lời nào ở đây thảo luận về các hoạt động ngoài s/from/to/chi tiết.

Hình thức chung của một sedtuyên bố là

*address* *action*

trong đó địa chỉ có thể là phạm vi regex hoặc phạm vi số dòng (hoặc trống, trong trường hợp đó, hành động được áp dụng cho mọi dòng đầu vào). Ví dụ

sed '1,4d' file

sẽ xóa các dòng từ 1 đến 4 ( địa chỉ là dải số dòng 1,4hành độngdlệnh xóa); và

sed '/ick/,$s/foo/bar/' file

sẽ thay thế lần xuất hiện đầu tiên của foobằng bartrên bất kỳ dòng nào giữa khớp đầu tiên trên regex ickvà phần cuối của tệp ( địa chỉ là phạm vi /ick/,$hành độngslệnh thay thế s/foo/bar/).

Trong bối cảnh này, nếu ickđến từ một biến, bạn có thể làm

sed "/$variable/,\$s/foo/bar/"

(lưu ý việc sử dụng dấu ngoặc kép thay vì dấu đơn, để trình bao có thể nội suy biến và sự cần thiết phải trích dẫn ký hiệu đô la bên trong dấu ngoặc kép) nhưng nếu biến chứa dấu gạch chéo, bạn sẽ gặp lỗi cú pháp. (Trình bao mở rộng biến, sau đó chuyển chuỗi kết quả đến sed; vì vậy sedchỉ nhìn thấy văn bản theo nghĩa đen - nó không có khái niệm về các biến của trình bao.)

Cách chữa là sử dụng một dấu phân cách khác (trong đó rõ ràng là bạn cần có thể sử dụng một ký tự không thể xuất hiện trong giá trị của biến), nhưng không giống như trong s%foo%bar%trường hợp này, bạn cũng cần một dấu gạch chéo ngược trước dấu phân cách nếu bạn muốn sử dụng một ký tự khác dấu phân cách so với mặc định /:

sed "\\%$variable%,\$s/foo/bar/" file

(bên trong dấu nháy đơn, một dấu gạch chéo ngược rõ ràng là đủ); hoặc bạn có thể thoát riêng từng dấu gạch chéo trong giá trị. Cú pháp cụ thể này chỉ là Bash:

sed "/${variable//\//\\/}/,\$s/foo/bar/" file

hoặc nếu bạn sử dụng một vỏ khác, hãy thử

escaped=$(echo "$variable" | sed 's%/%\\/%g')
sed "s/$escaped/,\$s/foo/bar/" file

Để rõ ràng, nếu $variablechứa chuỗi 1/2thì các lệnh trên sẽ tương đương với

sed '\%1/2%,$s/foo/bar/' file

trong trường hợp đầu tiên, và

sed '/1\/2/,$s/foo/bar/' file

trong lần thứ hai.


4

Sử dụng Perl, trong đó các biến là công dân hạng nhất, không chỉ mở rộng macro:

var=/Users/Documents/name/file perl -pe 's/\Q$ENV{var}/replace/g' $file
  • -p đọc từng dòng đầu vào và in dòng sau khi xử lý
  • \Qtrích dẫn tất cả các siêu ký tự trong chuỗi sau (không cần thiết cho giá trị được trình bày ở đây, nhưng cần thiết nếu giá trị chứa [hoặc một số giá trị khác đặc biệt đối với các phép tính thông thường)

Câu trả lời chung duy nhất hữu ích cho hàng tá câu hỏi "sử dụng biến trong biểu thức sed" trên Stack Exchange. Mọi thứ khác có hiệu quả là "thoát khỏi bất kỳ thứ gì đặc biệt đối với sed", điều này thực tế vô dụng với sự thay đổi của các biến và của sed.
Heath Raftery
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.