Sự khác nhau giữa sed trên Mac OSX và sed sed khác của tiêu chuẩn?


61

Tôi gặp một số vấn đề khi sử dụng câu trả lời được cung cấp trên trang web này cho câu hỏi này về lệnh sed để thay thế một dòng trống bằng hai dòng nội dung khác và nó đã được đưa lên nếu lệnh sed trên Mac OS (10.6.7 cho tôi ) khác. Tôi không nghĩ rằng đó là, nhưng tự hỏi nếu những người khác trên trang web này nghĩ khác.

Câu trả lời:


43

Hành vi của các tiện ích shell không khác nhau theo các cách nhỏ giữa các biến thể unix. Có nhiều biến thể unix , với một lịch sử phức tạp . Có những nỗ lực tiêu chuẩn hóa như tiêu chuẩn POSIX và thay thế cho đặc tả UNIX đơn . Hầu hết các hệ thống hiện nay đều triển khai POSIX: 2001, còn được gọi là Thông số kỹ thuật UNIX đơn phiên bản 3 , với độ lệch nhỏ và nhiều phần mở rộng. Đặc tả Unix đơn không phải là một hướng dẫn, nhưng phiên bản 3 có thể đọc được nếu bạn đã có ý tưởng về những gì một lệnh đang làm. Bạn có thể tham khảo nó để biết liệu một số tính năng là tiêu chuẩn hoặc một phần mở rộng của một hệ thống cụ thể.

Phần lớn người dùng unix sử dụng Linux và chưa sử dụng bất kỳ biến thể nào khác. Linux đi kèm với các tiện ích GNU , thường có nhiều phần mở rộng theo tiêu chuẩn. Vì vậy, bạn sẽ tìm thấy khá nhiều mã ngoài đó hoạt động trên Linux nhưng không phải trên các thông báo khác, vì nó phụ thuộc vào các tiện ích mở rộng đó.

Về sed, hãy tham khảo thông số kỹ thuật của sed Một Unix tối thiểu mà mọi hệ thống phải hỗ trợ, trang hướng dẫn trên hệ thống của bạn để biết cách triển khai của bạn hỗ trợ và hướng dẫn sử dụng GNU sed cho hầu hết mọi người sử dụng.

Một trong những phần mở rộng không chuẩn trong GNU sed là hỗ trợ nhiều lệnh chạy cùng nhau. Ví dụ, chương trình sed GNU này in tất cả các dòng có chứa một a, nhưng thay đổi bthành cđầu tiên:

sed -ne '/a/ {s/b/c/g; p}'

{}thực sự là các lệnh riêng biệt, vì vậy để có tính di động đầy đủ, bạn cần chỉ định chúng trên các dòng riêng biệt (trong một tệp) hoặc trong các -eđối số riêng biệt (trên dòng lệnh). Việc thiếu một dấu tách lệnh sau {và sử dụng ;như một dấu tách lệnh là các phần mở rộng phổ biến. Việc thiếu một dấu phân cách lệnh trước đây }là một phần mở rộng ít phổ biến hơn. Đây là tiêu chuẩn tuân thủ:

sed -n -e '/a/ {' -e 's/b/c/g' -e p -e '}'

Điều này là không chuẩn nhưng thường được chấp nhận:

sed -ne '/a/ { s/b/c/g; p; }'

Một phần mở rộng không chuẩn nhưng phổ biến khác là việc sử dụng \ncó nghĩa là một dòng mới trong svăn bản thay thế (việc sử dụng trong biểu thức chính quy là tiêu chuẩn). Phương thức di động là bao gồm dấu gạch chéo ngược mới trong tập lệnh sed. Một phần mở rộng phổ biến khác là \+, \?\|trong regexps có nghĩa là một hoặc nhiều, nhiều nhất là một và xen kẽ; biểu thức cơ bản thường xuyên di động không có trong số này. Ví dụ, lệnh đầu tiên là một cách không di động để thay thế các chuỗi liền kề của khoảng trắng bằng một dòng mới; lệnh thứ hai là tương đương tuân thủ tiêu chuẩn.

sed -e 's/ \+/\n/'
sed -e 's/  */\
/'

Lưu ý rằng trong tất cả các trường hợp về phần mở rộng GNU, đó là cách sử dụng không chuẩn. GNU sedtự tuân thủ vì nó làm những điều được cho phép (nhưng không bắt buộc, không xác định) theo tiêu chuẩn. Có những trường hợp nó không tuân thủ và việc chạy nó POSIXLY_CORRECTtrong môi trường có thể giúp ích. Giống như với s/[\n]//gđiều đó phải loại bỏ phản ứng dữ dội và nký tự nhưng thay vào đó loại bỏ dòng mới. Hoặc hành vi của Nlệnh trên dòng cuối cùng.
Stéphane Chazelas

sed -ne '/a/ { s/b/c/g; p; }'là tiêu chuẩn kể từ phiên bản 2016 của tiêu chuẩn. Nó luôn luôn di động. Xem austingroupbugs.net/view.php?id=944&nbn=7
Stéphane Chazelas

60

OS X hiện đi kèm với một sed FreeBSD từ năm 2005. Hầu hết các khác biệt dưới đây cũng áp dụng cho các phiên bản sed BSD khác.

Sử dụng sed của OS X cho sử dụng sed -EERE và GNU -r. -Elà một bí danh cho -rGNU sed (được thêm vào 4.2, không được ghi lại cho đến 4.3). Các phiên bản mới hơn của FreeBSD và NetBSD sed hỗ trợ cả -E-r. OpenBSD sed chỉ hỗ trợ -E.

-i ''hoạt động với sed của OS X nhưng không phải GNU sed. -ihoạt động với GNU sed, các phiên bản gần đây của NetBSD, OpenBSD sed, nhưng không phải là sed của OS X. -i -ehoạt động với cả hai nhưng trong trường hợp FreeBSD sedtạo bản sao lưu của tệp gốc có -egắn tên tệp (và bạn cần chuyển không quá một biểu thức cho sed).

GNU diễn giải sed chuỗi escape như \t, \n, \001, \x01, \w, và \b. Sed của OS X và sed POSIX chỉ diễn giải \n(nhưng không phải trong phần thay thế của s).

GNU sed phiên dịch \|, \+\?trong BRE nhưng sed của POS X và sed POSIX thì không. \(, \), \{, Và \}là POSIX BRE.

GNU sed cho phép bỏ qua ;hoặc một dòng mới trước }nhưng sed của OS X thì không.

i(chèn), a(nối thêm) và c(thay đổi) phải được theo sau bởi dấu gạch chéo ngược và dòng mới trong sed và POSIX của OS X nhưng không phải trong GNU sed. GNU sed thêm một dòng mới mất tích sau khi văn bản chèn vào bởi i, ahoặc cnhưng OS X sed không. Ví dụ sed 1ialà một thay thế GNU cho sed $'1i\\\na\n'.

Ví dụ: printf a|sed -n pthêm một dòng mới trong sed của OS X nhưng không có trong GNU sed.

Sed X của OS X không hỗ trợ các bộ sửa đổi I(không phân biệt chữ hoa chữ thường) hoặc M(nhiều dòng). Các phiên bản mới hơn của hỗ trợ sed FreeBSD I.

Sed X của OS X không hỗ trợ -s( --separate), -u( --unbuffered) hoặc -z( --null-data).

Một tùy chọn BSD không được GNU sed hỗ trợ là -a, điều này làm cho việc wthêm vào một tệp thay vì cắt bớt một tệp.

Ví dụ về các lệnh GNU sed không hoạt động với sed của OS X:

sed /pattern/,+2d # like `sed '/pattern/{N;N;d;}'`
sed -n 0~3p # like `awk NR%3==0`
sed /pattern/Q # like `awk '/pattern/{exit}1'` or `sed -n '/pattern/,$!p'`
sed 's/\b./\u&/g' # \u converts the next character to uppercase
sed 's/^./\l&/' # \l converts the next character to lowercase
sed -i '1ecat file_to_prepend' file # e executes a shell command
sed -n l0 # 0 disables wrapping

4
-i -ekhông hoạt động trên OSX. Nó xen -evào như hậu tố.
Chris Martin

3
@ChrisMartin có, trong phiên bản OS X -iluôn yêu cầu hậu tố, ngay cả khi một chuỗi trống. vì thế -i '' -enên làm việc.
waldyrious

@waldyrious Nó chỉ hoạt động trên OSX.
Chris Martin

vâng, đó là một sự
châm biếm

3
Câu " -i -ehoạt động với cả hai." trong câu trả lời của bạn cho thấy có một giải pháp đa nền tảng. Rõ ràng là không có.
leondepeon

5

Cách tốt nhất mà tôi đã tìm thấy để có cùng một kịch bản hoạt động trên cả Linux và Mac là:

sed -i.bak -e 's/foo/bar/' -- "${TARGET}" &&
  rm -- "${TARGET}.bak"

Hoặc sử dụng perlnơi mà -iđến từ. perl -Tpi -e 's/foo/bar/' -- "$TARGET"
Stéphane Chazelas
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.