Tại sao `sed expr1 | sed expr2` khác với `sed -e expr1 -e expr2`


10

Tôi đã tách đầu ra từ idđể cung cấp danh sách các nhóm theo danh sách dễ đọc hơn của các nhóm mà người dùng là thành viên:

id roaima | sed 's/,/\n\t/g'
uid=1001(roaima) gid=1001(roaima) groups=1001(roaima)
    24(cdrom)
    25(floppy)
    ...
    822413650 (international (uk) location)

Tôi muốn tách số nhóm khỏi tên được đặt trong ngoặc để tôi mở rộng biểu thức như thế này

id roaima | sed -e 's/,/\n\t/g' -e '2,$s/(/ (/'

Tuy nhiên, điều này không hoạt động như tôi mong đợi ban đầu. Biểu thức thứ hai dường như không có hiệu lực.

Thay vào đó, để có được kết quả tôi muốn, tôi cần chạy hai sedlệnh riêng biệt , như thế này:

id roaima | sed -e 's/,/\n\t/g' | sed '2,$s/(/ (/'
uid=1001(roaima) gid=1001(roaima) groups=1001(roaima)
    24 (cdrom)
    25 (floppy)
    ...
    822413650 (international (uk) location)

Tại sao tôi cần hai sedlệnh trong một ống chứ không phải một lệnh có nhiều lệnh? Hoặc nếu tôi có thể làm điều này với một sed, tôi sẽ làm như thế nào?

Điều tôi đặc biệt muốn là có khoảng trắng duy nhất giữa giá trị UID / GID và tên được đặt trong ngoặc đơn cho mỗi mục duy nhất (bao gồm cả UID và GID trên dòng đầu tiên), nhưng lưu ý là trong dữ liệu thực của tôi, tôi có thể có các nhóm chứa dấu ngoặc trong tên của họ và tôi không muốn tên mình bị sai lệch.

Câu trả lời:


14

sed, Như awkhoặc cuthoặc perl -necông trình trên mỗi dòng riêng một sau khi khác.

sed -e code1 -e code2

thực sự được chạy như:

while(patternspace = getline()) {
  linenumber++
  code1
  code2
} continue {print patternspace}

Nếu code2 của bạn là 2,$ s/foo/bar/, đó là:

if (linenumber >= 2) sub(/foo/, "bar", patternspace)

Vì đầu vào của bạn chỉ có một dòng, nên sub()sẽ không bao giờ được chạy.

Chèn các ký tự dòng mới vào không gian mẫu trong code1không làm linenumbertăng.

Thay vào đó, bạn có một không gian mẫu có nhiều dòng trong đó xử lý dòng đầu tiên và duy nhất. Nếu bạn muốn thực hiện sửa đổi trên dòng thứ hai và trên không gian mẫu nhiều dòng đó, bạn cần thực hiện một số thứ như:

s/\(\n[^(]*\)(/\1 (/g

Mặc dù ở đây tất nhiên, bạn cũng có thể thực hiện hai thao tác trong một lần:

id | sed 's/,\([^(]*\)(/\n\t\1 (/g'

awk và perl -n / p, hoạt động trên mỗi bản ghi mặc định thành một dòng nhưng có thể thay đổi; trong trường hợp này -vRS=,hoặc -054có thể giúp đỡ.
dave_thndry_085

5

Nếu bạn có GNU sed, bạn có thể sử dụng

id username | sed 's/(/ (/4g; s/,/\n\t/g'

trong đó thêm một khoảng trắng trước dấu ngoặc đơn mở thứ 4 và tiếp theo, sau đó thay thế dấu phẩy.


1
Điều đó có vẻ thú vị. Thật không may, nó cũng ảnh hưởng đến các tên nhóm có chứa dấu ngoặc như ví dụ của tôi international (uk) location, bằng cách chèn một khoảng trắng không mong muốn vào chính tên đó.
roaima

Sau đó sử dụng s/\([[:digit:]]\+\)(/\1 (/4gsẽ chỉ thêm một khoảng trắng nếu có các chữ số trước dấu ngoặc đơn.
glenn jackman

1

Những gì @ stéphane-chazelas nói là đúng, nhưng bạn luôn có thể thêm khoảng trắng trước và chia thành các dòng sau như thế này:

sed -e 's:\([,=][0-9]*\):\1 :g' -e 's:,:\n\t:g'

Hoặc trong một tập lệnh sed duy nhất (không có -e):

sed 's:\([,=][0-9]*\):\1 :g; s:,:\n\t:g'

Chúng ta thường sử dụng " /" làm dấu phân tách của tìm kiếm lệnh, nhưng nó cũng chấp nhận bất kỳ ký tự nào, vì vậy đôi khi dễ đọc hơn khi sử dụng char khác như " :" để tránh các kết hợp như " /\".

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.