Những nhân vật nào tôi cần thoát khi sử dụng sed trong kịch bản sh?


248

Lấy đoạn script sau:

#!/bin/sh
sed 's/(127\.0\.1\.1)\s/\1/' [some file]

Nếu tôi cố chạy cái này trong sh( dashở đây), nó sẽ thất bại vì dấu ngoặc đơn cần được thoát. Nhưng tôi không cần phải tự thoát khỏi dấu gạch chéo ngược (giữa các octet hoặc trong \shoặc \1). Quy tắc ở đây là gì? Còn khi tôi cần sử dụng {...}hay [...]sao? Có một danh sách những gì tôi làm và không cần phải trốn thoát không?


1
Đây là một hàm bash để chuyển đổi các đường dẫn để sử dụng với SED:function sedPath { path=$((echo $1|sed -r 's/([\$\.\*\/\[\\^])/\\\1/g'|sed 's/[]]/\[]]/g')>&1) } #Escape path for use with sed
user2428118


Dura lex, sed sed
Nemo

Câu trả lời:


281

Có hai cấp độ giải thích ở đây: vỏ và sed.

Trong shell, mọi thứ giữa các trích dẫn đơn được diễn giải theo nghĩa đen, ngoại trừ các trích dẫn đơn. Bạn có thể có một trích dẫn duy nhất giữa các trích dẫn đơn bằng cách viết '\''(trích dẫn đơn, trích dẫn một chữ, trích dẫn đơn).

Sed sử dụng các biểu thức chính quy cơ bản . Trong BRE, để xử lý chúng theo nghĩa đen, các ký tự $.*[\^cần được trích dẫn bằng cách đặt trước chúng bằng dấu gạch chéo ngược, ngoại trừ bên trong bộ ký tự ( […]). Chữ cái, chữ số và (){}+?|không được trích dẫn (bạn có thể thoát khỏi việc trích dẫn một số trong số này trong một số triển khai). Các trình tự \(, \), \n, và trong một số triển khai \{, \}, \+, \?, \|và dấu chéo ngược khác + chữ cái và số có ý nghĩa đặc biệt. Bạn có thể thoát khỏi việc không trích dẫn $^ở một số vị trí trong một số triển khai.

Hơn nữa, bạn cần một dấu gạch chéo ngược trước /nếu nó xuất hiện trong biểu thức chính bên ngoài biểu thức ngoặc. Bạn có thể chọn một ký tự thay thế làm dấu phân cách bằng cách viết, ví dụ, s~/dir~/replacement~hoặc \~/dir~p; bạn sẽ 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 đưa nó vào BRE. Nếu bạn chọn một ký tự có ý nghĩa đặc biệt trong BRE và bạn muốn bao gồm nó theo nghĩa đen, bạn sẽ cần ba dấu gạch chéo ngược; Tôi không khuyến khích điều này, vì nó có thể hành xử khác đi trong một số triển khai.

Tóm lại, cho sed 's/…/…/':

  • Viết biểu thức chính giữa các dấu ngoặc đơn.
  • Sử dụng '\''để kết thúc với một trích dẫn trong regex.
  • Đặt dấu gạch chéo ngược trước $.*/[\]^và chỉ các ký tự đó (nhưng không nằm trong biểu thức ngoặc). (Về mặt kỹ thuật, bạn không nên đặt dấu gạch chéo ngược trước ]nhưng tôi không biết về cách triển khai xử lý ]\]khác với biểu thức dấu ngoặc.)
  • Trong một biểu thức ngoặc, -để được xử lý theo nghĩa đen, hãy đảm bảo rằng nó là đầu tiên hoặc cuối cùng ( [abc-]hoặc [-abc], không [a-bc]).
  • Bên trong một biểu thức khung, ^để được xử lý theo nghĩa đen, hãy chắc chắn rằng nó không phải là đầu tiên (sử dụng [abc^], không phải [^abc]).
  • Để đưa ]vào danh sách các ký tự khớp với biểu thức ngoặc, hãy đặt nó thành ký tự đầu tiên (hoặc đầu tiên sau ^cho một tập phủ định): []abc]hoặc [^]abc](không phải [abc]]cũng không[abc\]] ).

Trong văn bản thay thế:

  • &\cần được trích dẫn bằng cách đặt trước chúng bằng dấu gạch chéo ngược, cũng như dấu phân cách (thường /) và dòng mới.
  • \theo sau là một chữ số có ý nghĩa đặc biệt. \theo sau là một chữ cái có một ý nghĩa đặc biệt (các ký tự đặc biệt) trong một số triển khai và \theo sau là một số ký tự khác có nghĩa \choặc ctùy thuộc vào việc thực hiện.
  • Với các trích dẫn đơn xung quanh đối số ( sed 's/…/…/'), sử dụng '\''để đặt một trích dẫn trong văn bản thay thế.

Nếu regex hoặc văn bản thay thế đến từ một biến shell, hãy nhớ rằng

  • Regex là một BRE, không phải là một chuỗi chữ.
  • Trong regex, một dòng mới cần được thể hiện dưới dạng \n(sẽ không bao giờ khớp với trừ khi bạn có sedmã khác thêm các ký tự dòng mới vào không gian mẫu). Nhưng lưu ý rằng nó sẽ không hoạt động trong biểu thức ngoặc với một số sedtriển khai.
  • Trong văn bản thay thế &, \và các dòng mới cần được trích dẫn.
  • Dấu phân cách cần được trích dẫn (nhưng không nằm trong biểu thức ngoặc).
  • Sử dụng dấu ngoặc kép để nội suy : sed -e "s/$BRE/$REPL/".

Thoát khỏi ký tự đại diện thực tế (*), bạn có thể sử dụng dấu gạch chéo ngược kép ( \\*). Ví dụ:echo "***NEW***" | sed /\\*\\*\\*NEW\\*\\*\\*/s/^/#/
nguy hiểm89

43

Vấn đề bạn gặp phải không phải do nội suy và thoát vỏ - đó là do bạn đang cố sử dụng cú pháp biểu thức chính quy mở rộng mà không chuyển qua sed -rhoặc --regexp-extendedtùy chọn.

Thay đổi dòng sed của bạn từ

sed 's/(127\.0\.1\.1)\s/\1/' [some file]

đến

sed -r 's/(127\.0\.1\.1)\s/\1/' [some file]

và nó sẽ hoạt động như tôi tin bạn dự định.

Theo mặc định, sed sử dụng các biểu thức chính quy cơ bản (nghĩ kiểu grep), sẽ yêu cầu cú pháp sau:

sed 's/\(127\.0\.1\.1\)[ \t]/\1/' [some file]

Tôi gặp vấn đề này một lần nữa và quên cuộn xuống để tìm giải pháp tôi đưa ra lần trước. Cảm ơn một lần nữa.
isaaclw

Cảm ơn rất nhiều. Thêm vào -rnhư một tùy chọn là những gì cần thiết trong trường hợp của tôi.
HelloGoodbye

15

Trừ khi bạn muốn nội suy một biến shell thành biểu thức sed, hãy sử dụng các dấu ngoặc đơn cho toàn bộ biểu thức vì chúng khiến mọi thứ giữa chúng được hiểu như nguyên trạng, bao gồm cả dấu gạch chéo ngược.

Vì vậy, nếu bạn muốn sed xem s/\(127\.0\.1\.1\)\s/\1/đặt các trích dẫn đơn xung quanh nó và shell sẽ không chạm vào dấu ngoặc đơn hoặc dấu gạch chéo ngược trong đó. Nếu bạn cần nội suy một biến shell, chỉ đặt phần đó trong dấu ngoặc kép. Ví dụ

sed 's/\(127\.0\.1\.1\)/'"$ip"'/'

Điều này sẽ giúp bạn tránh những rắc rối khi nhớ những ký tự đại diện hệ vỏ nào không được thoát bằng dấu ngoặc kép.


Tôi muốn sedxem s/(127\.0\.1\.1)/..., nhưng đặt nó trong một kịch bản shell như là không hoạt động. Những gì bạn đang nói về vỏ không chạm vào dấu ngoặc có vẻ sai. Tôi đã chỉnh sửa câu hỏi của mình để giải thích.
ghê tởm

3
Vỏ không chạm vào dấu ngoặc đơn. Bạn cần các dấu gạch chéo ngược vì sed cần nhìn thấy chúng. sed 's/(127\.0\.1\.1)/IP \1/'thất bại vì sed cần xem \(\)cho cú pháp nhóm, không ().
Kyle Jones

facepalm Nó không có trong trang người đàn ông, nhưng nó có trong một số hướng dẫn trực tuyến tôi tìm thấy. Điều này có bình thường đối với regex không, vì tôi chưa bao giờ phải sử dụng nó trong các thư viện regex (ví dụ: Python)?
gièm pha

3
Đối với các lệnh Unix truyền thống, có các biểu thức chính quy cơ bản và các biểu thức chính quy mở rộng. Chi tiết . sed sử dụng các biểu thức chính quy cơ bản, do đó, dấu gạch chéo ngược là cần thiết cho cú pháp nhóm. Perl và Python đã vượt xa các biểu thức chính quy mở rộng. Trong khi tôi đang chọc ngoáy tôi đã tìm thấy một biểu đồ cực kỳ nhiều thông tin minh họa cho những gì một kẻ phá rối khó hiểu mà chúng ta gợi lên khi chúng ta nói một cách rõ ràng "biểu hiện thông thường".
Kyle Jones

1
Tôi cũng sẽ thêm rằng ký tự duy nhất không thể được sử dụng trong các trích dẫn đơn là một trích dẫn.
enzotib
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.