Một cái nhìn tổng quan của nhiều hữu ích câu trả lời hiện có , bổ sung với lời giải thích :
Các ví dụ ở đây sử dụng trường hợp sử dụng được đơn giản hóa: thay thế từ 'foo' bằng 'bar' trong dòng khớp đầu tiên.
Do sử dụng các chuỗi ANSI C-trích dẫn ( $'...'
) để cung cấp các dòng đầu vào mẫu, bash
, ksh
, hoặc zsh
được giả định như vỏ.
sed
Chỉ GNU :
Anwswer của Ben Hoffstein cho chúng ta thấy rằng GNU cung cấp một phần mở rộng cho đặc tả POSIX chosed
phép biểu mẫu 2 địa chỉ sau: 0,/re/
( re
biểu thị một biểu thức chính quy tùy ý ở đây).
0,/re/
cho phép regex khớp trên dòng đầu tiên . Nói cách khác: một địa chỉ như vậy sẽ tạo ra một phạm vi từ dòng thứ 1 trở lên và bao gồm cả dòng phù hợp re
- cho dù re
xảy ra trên dòng thứ nhất hoặc trên bất kỳ dòng tiếp theo nào.
- Ngược lại điều này với các hình thức POSIX-compliant
1,/re/
, mà tạo ra một phạm vi mà các trận đấu từ dòng 1 lên đến và bao gồm cả dòng đó trận đấu re
trên tiếp theo dòng; nói cách khác: điều này sẽ không phát hiện sự xuất hiện đầu tiên của re
trận đấu nếu nó xảy ra trên dòng thứ nhất và cũng ngăn chặn việc sử dụng tốc ký//
để sử dụng lại biểu thức chính được sử dụng gần đây nhất (xem điểm tiếp theo). 1
Nếu bạn kết hợp một 0,/re/
địa chỉ với một s/.../.../
cuộc gọi (thay thế) sử dụng cùng một biểu thức chính quy, lệnh của bạn sẽ chỉ thực hiện thay thế trên dòng đầu tiên phù hợp re
.
sed
cung cấp một lối tắt thuận tiện để sử dụng lại biểu thức chính quy được áp dụng gần đây nhất : một cặp dấu phân cách trống ,//
.
$ sed '0,/foo/ s//bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar # only 1st match of 'foo' replaced
Unrelated
2nd foo
3rd foo
Chỉ có các tính năng POSIX sed
như BSD (macOS)sed
(cũng sẽ hoạt động với GNU sed
):
Vì 0,/re/
không thể được sử dụng và biểu mẫu 1,/re/
sẽ không phát hiện re
nếu nó xảy ra trên dòng đầu tiên (xem ở trên), nên cần xử lý đặc biệt cho dòng thứ nhất .
Câu trả lời của MikhailVS đề cập đến kỹ thuật, đưa vào một ví dụ cụ thể ở đây:
$ sed -e '1 s/foo/bar/; t' -e '1,// s//bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar # only 1st match of 'foo' replaced
Unrelated
2nd foo
3rd foo
Ghi chú:
//
Phím tắt regex trống được sử dụng hai lần ở đây: một lần cho điểm cuối của phạm vi và một lần trong s
cuộc gọi; trong cả hai trường hợp, regex foo
được sử dụng lại hoàn toàn, cho phép chúng tôi không phải sao chép nó, điều này làm cho cả hai mã ngắn hơn và dễ bảo trì hơn.
POSIX sed
cần các dòng mới thực sự sau một số chức năng nhất định, chẳng hạn như sau tên của nhãn hoặc thậm chí thiếu sót của nó, như trường hợp t
ở đây; chiến lược phân tách tập lệnh thành nhiều -e
tùy chọn là một cách thay thế cho việc sử dụng một dòng mới thực sự: kết thúc mỗi -e
đoạn kịch bản mà một dòng mới thường cần phải đi.
1 s/foo/bar/
chỉ thay thế foo
trên dòng 1, nếu tìm thấy ở đó. Nếu vậy, t
các nhánh đến cuối tập lệnh (bỏ qua các lệnh còn lại trên dòng). ( t
Hàm chỉ phân nhánh nhãn nếu s
cuộc gọi gần đây nhất thực hiện thay thế thực tế; trong trường hợp không có nhãn, như trường hợp ở đây, phần cuối của tập lệnh được phân nhánh).
Khi điều đó xảy ra, địa chỉ phạm vi 1,//
, thường tìm thấy lần xuất hiện đầu tiên bắt đầu từ dòng 2 , sẽ không khớp và phạm vi sẽ không được xử lý, vì địa chỉ được đánh giá khi dòng hiện tại đã có 2
.
Ngược lại, nếu không có trận đấu nào trên dòng thứ nhất, 1,//
sẽ được nhập và sẽ tìm thấy kết quả khớp đầu tiên thực sự.
Ảnh hưởng thực là giống như với GNU sed
's 0,/re/
: chỉ có sự xuất hiện lần đầu tiên được thay thế, cho dù nó xảy ra trên dòng 1 hoặc bất kỳ khác.
Phương pháp không phạm vi
câu trả lời Potong của chứng minh vòng lặp kỹ thuật mà bỏ qua sự cần thiết của một loạt ; vì anh ta sử dụng cú pháp GNU sed
, đây là các tương đương POSIX :
Kỹ thuật lặp 1: Trong trận đấu đầu tiên, thực hiện thay thế, sau đó nhập một vòng lặp chỉ đơn giản là in các dòng còn lại như vốn có :
$ sed -e '/foo/ {s//bar/; ' -e ':a' -e '$!{n;ba' -e '};}' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar
Unrelated
2nd foo
3rd foo
Kỹ thuật lặp 2, chỉ dành cho các tệp nhỏ : đọc toàn bộ đầu vào vào bộ nhớ, sau đó thực hiện một thay thế duy nhất trên đó .
$ sed -e ':a' -e '$!{N;ba' -e '}; s/foo/bar/' <<<$'1st foo\nUnrelated\n2nd foo\n3rd foo'
1st bar
Unrelated
2nd foo
3rd foo
1 1.61804 cung cấp các ví dụ về những gì xảy ra với 1,/re/
, có và không có tiếp theo s//
:
- sed '1,/foo/ s/foo/bar/' <<<$'1foo\n2foo'
sản lượng $'1bar\n2bar'
; tức là, cả hai dòng đã được cập nhật, vì số dòng 1
khớp với dòng thứ nhất và regex /foo/
- phần cuối của phạm vi - sau đó chỉ được tìm kiếm để bắt đầu trên dòng tiếp theo . Do đó, cả hai dòng được chọn trong trường hợp này và sự s/foo/bar/
thay thế được thực hiện trên cả hai dòng.
- sed '1,/foo/ s//bar/' <<<$'1foo\n2foo\n3foo'
thất bại : với sed: first RE may not be empty
(BSD / macOS) và sed: -e expression #1, char 0: no previous regular expression
(GNU), bởi vì, tại thời điểm dòng thứ 1 đang được xử lý (do số dòng 1
bắt đầu phạm vi), chưa có regex nào được áp dụng, vì vậy//
không đề cập đến bất cứ điều gì.
Ngoại trừ cú pháp sed
đặc biệt của GNU 0,/re/
, bất kỳ phạm vi nào bắt đầu bằng số dòng đều không được sử dụng //
.