Kết hợp regex không tham lam (miễn cưỡng) trong sed?


406

Tôi đang cố gắng sử dụng sed để dọn sạch các dòng URL để chỉ trích xuất tên miền.

Từ đó:

http://www.suepearson.co.uk/product/174/71/3816/

Tôi muốn:

http://www.suepearson.co.uk/

(có hoặc không có dấu gạch chéo, không thành vấn đề)

Tôi đã thử:

 sed 's|\(http:\/\/.*?\/\).*|\1|'

và (thoát khỏi bộ định lượng không tham lam)

sed 's|\(http:\/\/.*\?\/\).*|\1|'

nhưng dường như tôi không thể làm cho bộ định lượng không tham lam ( ?) hoạt động, vì vậy nó luôn kết thúc khớp với toàn bộ chuỗi.


54
Lưu ý phụ: nếu bạn phân định biểu thức chính của mình bằng "|", bạn không cần thoát "/" s. Trong thực tế, hầu hết mọi người phân định bằng "|" thay vì "/" s để tránh "hàng rào picket".
AttishOculus

12
@AttishOculus Ký tự đầu tiên sau 's' trong biểu thức thay thế trong sed là dấu phân cách. Do đó, ^ foo ^ bar ^ 'hoặc' s! Foo! Bar! ' cũng hoạt động
Squidly

1
Đối với regex mở rộng, sử dụng sed -E 's.... Tuy nhiên, không có nhà điều hành miễn cưỡng.
Ondra ižka

Không trả lời cho tiêu đề câu hỏi nhưng trong trường hợp cụ thể này cut -d'/' -f1-3hoạt động đơn giản .
Petr Javorik

Câu trả lời:


421

Cả regex / GNU regex cơ bản cũng không mở rộng đều nhận ra bộ định lượng không tham lam; bạn cần một regex sau này. May mắn thay, Perl regex cho bối cảnh này khá dễ dàng để có được:

perl -pe 's|(http://.*?/).*|\1|'

12
Để làm điều đó tại chỗ tùy chọn sử dụng -pi -e.
thực sự là

11
Những người hút thuốc thần thánh tôi không thể tin rằng nó đã hoạt động :-) Điều duy nhất hiện tại là kịch bản của tôi có sự phụ thuộc Perl :-( Về mặt tích cực, hầu như mọi bản phân phối Linux đều có Perl nên có lẽ không phải là vấn đề :-)
Freedom_Ben

6
@Freedom_Ben: IIRC perlđược yêu cầu bởi POSIX
MestreLion

4
@ dolphus333: "Cả regex / GNU regex cơ bản cũng không mở rộng đều nhận ra bộ định lượng không tham lam" có nghĩa là "bạn không thể sử dụng bộ định lượng không tham lam trong sed".
hỗn loạn

3
@ Sérgio đó là cách bạn thực hiện điều được yêu cầu, điều này là không thể sed, bằng cách sử dụng một cú pháp về cơ bản giống vớised
hỗn loạn

250

Trong trường hợp cụ thể này, bạn có thể hoàn thành công việc mà không cần sử dụng regex không tham lam.

Hãy thử regex không tham lam này [^/]*thay vì .*?:

sed 's|\(http://[^/]*/\).*|\1|g'

3
Làm thế nào để làm cho sed kết hợp không tham lam một cụm từ bằng cách sử dụng kỹ thuật này?
dùng3694243

6
Thật không may, bạn không thể; xem câu trả lời của sự hỗn loạn .
Daniel H

Rất cám ơn ... vì perl không còn trong cơ sở cài đặt mặc định trong nhiều bản phân phối linux!
st0ne


@DanielH Trong thực tế có thể kết hợp các cụm từ không tham lam sử dụng kỹ thuật này theo yêu cầu. Nó có thể mất một chút đau đớn để viết một trong hai mẫu với độ chính xác đủ. Ví dụ: khi phân tích cú pháp khóa-giá trị trong truy vấn của URL, nó có thể yêu cầu tìm kiếm gán bằng cách sử dụng ([^&=#]+)=([^&#]*). Chắc chắn có những trường hợp không hoạt động theo cách này, ví dụ như khi phân tích cú pháp URL cho phần lưu trữ và tên đường dẫn của nó bằng dấu gạch chéo cuối cùng được cho là tùy chọn để loại trừ khỏi việc bắt giữ:^(http:\/\/.+?)/?$
Thomas Urban

121

Với sed, tôi thường thực hiện tìm kiếm không tham lam bằng cách tìm kiếm bất cứ thứ gì ngoại trừ dải phân cách cho đến khi dải phân cách:

echo "http://www.suon.co.uk/product/1/7/3/" | sed -n 's;\(http://[^/]*\)/.*;\1;p'

Đầu ra:

http://www.suon.co.uk

đây là:

  • không đầu ra -n
  • tìm kiếm, khớp mẫu, thay thế và in s/<pattern>/<replace>/p
  • sử dụng ;dấu tách lệnh tìm kiếm thay vì /để dễ gõ hơns;<pattern>;<replace>;p
  • nhớ khớp giữa các dấu ngoặc \(... \), sau này có thể truy cập bằng \1, \2...
  • trận đấu http://
  • theo sau bởi bất cứ điều gì trong ngoặc [], [ab/]có nghĩa là ahoặc bhoặc/
  • đầu tiên ^trong []phương tiện not, vì vậy sau đó bất cứ điều gì nhưng điều trong[]
  • vì thế [^/]có nghĩa là bất cứ điều gì ngoại trừ /nhân vật
  • *là lặp lại nhóm trước đó [^/]*có nghĩa là các ký tự ngoại trừ /.
  • cho đến nay sed -n 's;\(http://[^/]*\)có nghĩa là tìm kiếm và ghi nhớ http://theo sau bởi bất kỳ ký tự nào ngoại trừ /và nhớ những gì bạn đã tìm thấy
  • chúng tôi muốn tìm kiếm cho đến khi kết thúc tên miền, vì vậy hãy dừng lại ở phần tiếp theo /để thêm phần khác /vào cuối: sed -n 's;\(http://[^/]*\)/'nhưng chúng tôi muốn khớp phần còn lại của dòng sau tên miền để thêm.*
  • bây giờ, trận đấu được ghi nhớ trong nhóm 1 ( \1) là tên miền, vì vậy hãy thay thế dòng trùng khớp bằng nội dung được lưu trong nhóm \1và in:sed -n 's;\(http://[^/]*\)/.*;\1;p'

Nếu bạn cũng muốn bao gồm dấu gạch chéo ngược sau tên miền, thì hãy thêm một dấu gạch chéo ngược trong nhóm để ghi nhớ:

echo "http://www.suon.co.uk/product/1/7/3/" | sed -n 's;\(http://[^/]*/\).*;\1;p'

đầu ra:

http://www.suon.co.uk/

8
Về các chỉnh sửa gần đây: Dấu ngoặc đơn là một loại ký tự dấu ngoặc, do đó, không thể gọi chúng là dấu ngoặc, đặc biệt nếu bạn làm theo từ với các ký tự thực tế, như tác giả đã làm. Ngoài ra, đó là cách sử dụng ưa thích trong một số nền văn hóa, do đó, việc thay thế nó bằng cách sử dụng ưa thích trong văn hóa của bạn có vẻ hơi thô lỗ, mặc dù tôi chắc chắn đó không phải là mục đích của biên tập viên. Cá nhân, tôi nghĩ tốt nhất nên sử dụng các tên mô tả thuần túy như ngoặc tròn , ngoặc vuôngngoặc vuông .
Alan Moore

2
Có thể thay thế dấu phân cách bằng một chuỗi?
Giải tích

37

sed không hỗ trợ toán tử "không tham lam".

Bạn phải sử dụng toán tử "[]" để loại trừ "/" khỏi kết quả khớp.

sed 's,\(http://[^/]*\)/.*,\1,'

PS không cần phải gạch chéo "/".


không hẳn nếu dấu phân cách có thể là một trong nhiều ký tự có thể (chỉ nói một chuỗi số) thì kết quả phủ định của bạn có thể ngày càng phức tạp hơn. điều đó tốt nhưng chắc chắn sẽ rất tốt nếu có một lựa chọn để thực hiện. * không tham lam
gesell

1
Câu hỏi đã chung chung hơn. Các giải pháp này hoạt động cho các URL nhưng không (ví dụ) cho trường hợp sử dụng của tôi là tước các số 0 ở cuối. s/([[:digit:]]\.[[1-9]]*)0*/\1/Rõ ràng sẽ không làm việc tốt cho 1.20300. Vì câu hỏi ban đầu là về URL, tuy nhiên, chúng nên được đề cập trong câu trả lời được chấp nhận.
Daniel H

33

Mô phỏng định lượng lười biếng (không tham lam) trong sed

Và tất cả các hương vị regex khác!

  1. Tìm sự xuất hiện đầu tiên của một biểu thức:

    • POSIX ERE (sử dụng -rtùy chọn)

      Chế độ:

      (EXPRESSION).*|.

      Trầm tích

      sed -r 's/(EXPRESSION).*|./\1/g' # Global `g` modifier should be on

      Ví dụ (tìm chuỗi chữ số đầu tiên) Bản demo trực tiếp :

      $ sed -r 's/([0-9]+).*|./\1/g' <<< 'foo 12 bar 34'
      12

      Nó hoạt động như thế nào?

      Regex này được hưởng lợi từ một sự thay thế |. Tại mỗi vị trí, động cơ cố gắng chọn trận đấu dài nhất (đây là tiêu chuẩn POSIX, theo sau là một vài động cơ khác) có nghĩa là nó đi cùng .cho đến khi tìm thấy kết quả khớp ([0-9]+).*. Nhưng trật tự cũng quan trọng.

      nhập mô tả hình ảnh ở đây

      Vì cờ toàn cầu được đặt, công cụ sẽ cố gắng tiếp tục khớp ký tự theo ký tự cho đến hết chuỗi đầu vào hoặc mục tiêu của chúng tôi. Ngay khi nhóm bắt đầu đầu tiên và duy nhất của bên trái của sự thay thế được khớp, (EXPRESSION)phần còn lại của dòng cũng được tiêu thụ ngay lập tức .*. Bây giờ chúng tôi giữ giá trị của chúng tôi trong nhóm bắt giữ đầu tiên.

    • BREIX BRE

      Chế độ:

      \(\(\(EXPRESSION\).*\)*.\)*

      Trầm tích

      sed 's/\(\(\(EXPRESSION\).*\)*.\)*/\3/'

      Ví dụ (tìm dãy số đầu tiên):

      $ sed 's/\(\(\([0-9]\{1,\}\).*\)*.\)*/\3/' <<< 'foo 12 bar 34'
      12

      Phiên bản này giống như phiên bản ERE nhưng không có sự thay thế liên quan. Đó là tất cả. Tại mỗi vị trí động cơ duy nhất cố gắng khớp một chữ số.

      nhập mô tả hình ảnh ở đây

      Nếu nó được tìm thấy, các chữ số sau đây được tiêu thụ và bắt giữ và phần còn lại của dòng được khớp ngay lập tức nếu không *có nghĩa là nhiều hơn hoặc bằng không, nó bỏ qua nhóm bắt thứ hai \(\([0-9]\{1,\}\).*\)*và đến một dấu chấm .để khớp với một ký tự và quá trình này tiếp tục.

  2. Tìm sự xuất hiện đầu tiên của một biểu thức phân định :

    Cách tiếp cận này sẽ khớp với lần xuất hiện đầu tiên của một chuỗi được phân định. Chúng ta có thể gọi nó là một chuỗi chuỗi.

    sed 's/\(END-DELIMITER-EXPRESSION\).*/\1/; \
         s/\(\(START-DELIMITER-EXPRESSION.*\)*.\)*/\1/g'

    Chuỗi đầu vào:

    foobar start block #1 end barfoo start block #2 end

    -EDE: end

    -SDE: start

    $ sed 's/\(end\).*/\1/; s/\(\(start.*\)*.\)*/\1/g'

    Đầu ra:

    start block #1 end

    Regex đầu tiên \(end\).*khớp và bắt các dấu phân cách kết thúc đầu tiên endvà thay thế tất cả khớp với các ký tự được bắt gần đây là dấu phân cách kết thúc. Ở giai đoạn này, đầu ra của chúng tôi là : foobar start block #1 end.

    nhập mô tả hình ảnh ở đây

    Sau đó, kết quả được chuyển đến regex thứ hai \(\(start.*\)*.\)*giống như phiên bản POSIX BRE ở trên. Nó khớp với một ký tự nếu dấu phân cách bắt đầu startkhông khớp với nếu không nó khớp và bắt dấu phân cách bắt đầu và khớp với các ký tự còn lại.

    nhập mô tả hình ảnh ở đây


Trả lời trực tiếp câu hỏi của bạn

Sử dụng phương pháp số 2 (biểu thức được phân tách), bạn nên chọn hai biểu thức thích hợp:

  • EDE: [^:/]\/

  • SDE: http:

Sử dụng:

$ sed 's/\([^:/]\/\).*/\1/g; s/\(\(http:.*\)*.\)*/\1/' <<< 'http://www.suepearson.co.uk/product/174/71/3816/'

Đầu ra:

http://www.suepearson.co.uk/

Lưu ý: điều này sẽ không hoạt động với các dấu phân cách giống hệt nhau.


3) trong khi đề xuất các trang web như regex101 cho bản demo, vui lòng thêm một lưu ý rằng nó không phải lúc nào cũng phù hợp với các công cụ cli vì sự khác biệt về cú pháp và tính năng
Sundeep

1
@Sundeep Cảm ơn bạn. Tôi đã chuyển tất cả các trích dẫn sang dấu ngoặc đơn. Ngoài ra tôi coi quy tắc trận đấu dài nhất bên trái sẽ được đề cập. Tuy nhiên, trong sedvà tất cả các động cơ khác theo cùng một thứ tự tiêu chuẩn không thành vấn đề khi nói đến sự bình đẳng. Vì vậy, echo 'foo 1' | sed -r 's/.|([0-9]+).*/\1/g'không có một trận đấu nhưng echo 'foo 1' | sed -r 's/([0-9]+).*|./\1/g'có.
revo

@Sundeep cũng là cách giải quyết cho các biểu thức được phân tách không hoạt động đối với các dấu phân cách bắt đầu và kết thúc giống hệt nhau mà tôi đã thêm một ghi chú cho.
revo

điểm tuyệt vời về những gì xảy ra khi các thay thế khác nhau bắt đầu từ cùng một vị trí và có cùng độ dài, đoán rằng sẽ theo thứ tự trái phải như các động cơ khác .. cần phải tìm kiếm nếu điều đó được mô tả trong sách hướng dẫn
Sundeep

Có một trường hợp kỳ lạ ở đây: stackoverflow.com/questions/59683820/ trộm
mẹo

20

Giải pháp không tham lam cho nhiều hơn một nhân vật

Chủ đề này thực sự cũ nhưng tôi cho rằng mọi người vẫn cần nó. Hãy nói rằng bạn muốn giết tất cả mọi thứ cho đến khi xảy ra lần đầu tiên HELLO. Bạn không thể nói [^HELLO]...

Vì vậy, một giải pháp tốt đẹp bao gồm hai bước, giả sử rằng bạn có thể dành một từ duy nhất mà bạn không mong đợi trong đầu vào, nói top_sekrit.

Trong trường hợp này, chúng ta có thể:

s/HELLO/top_sekrit/     #will only replace the very first occurrence
s/.*top_sekrit//        #kill everything till end of the first HELLO

Tất nhiên, với một đầu vào đơn giản hơn, bạn có thể sử dụng một từ nhỏ hơn, hoặc thậm chí có thể là một ký tự.

HTH!


4
Để làm cho nó thậm chí tốt hơn, hữu ích trong tình huống khi bạn không thể mong đợi ký tự không được sử dụng: 1. thay thế ký tự đặc biệt đó bằng WORD thực sự không sử dụng, 2. thay thế chuỗi kết thúc bằng ký tự đặc biệt, 3. thực hiện tìm kiếm kết thúc bằng ký tự đặc biệt, 4 . thay thế ký tự đặc biệt trở lại, 5. thay thế đặc biệt trở lại WORD. Ví dụ: bạn muốn một nhà điều hành tham lam giữa <hello> và </ hello>:
Jakub

3
Ví dụ ở đây: echo "Tìm: <hello> linh hoạt ~ st <br> có </ hello> <hello> sec ~ ond </ hello>" | sed -e "s, ~, RẤT HẤP DẪN, g" -e "s, </ hello>, ~, g" -e "s ,. * Tìm: <hello> ([^ ~] *). *, \ 1 , "-e" s, \ ~, </ hello>, "-e" s, RẤT TUYỆT VỜI, ~, "
Jakub

2
Tôi đồng ý. giải pháp tốt đẹp. Tôi sẽ viết lại nhận xét thành nói: nếu bạn không thể dựa vào ~ không được sử dụng, trước tiên hãy thay thế các lần xuất hiện của nó bằng s / ~ / VERYspeciaL / g, sau đó thực hiện thủ thuật trên, sau đó trả lại ~ ban đầu bằng cách sử dụng s / VERYspeciaL / ~ / g
ishahak

1
Tôi có xu hướng thích sử dụng các "biến" hiếm hơn cho loại điều này, vì vậy thay vì `, tôi sẽ sử dụng <$$>(vì $$mở rộng ID tiến trình của bạn trong trình bao, mặc dù bạn phải sử dụng dấu ngoặc kép thay vì dấu ngoặc đơn và điều đó có thể phá vỡ các phần khác của regex của bạn) hoặc, nếu unicode có sẵn, đại loại như thế <∈∋>.
Adam Katz

Tại một số điểm bạn phải tự hỏi mình tại sao bạn không chỉ sử dụng perlhoặc pythonhoặc một số ngôn ngữ khác để thay thế. perlthực hiện điều này theo cách ít mong manh trong một dòng duy nhất ...
ArtOfWarfare

18

sed - kết hợp không tham lam của Christoph Sieghart

Mẹo để có được kết hợp không tham lam trong sed là khớp tất cả các ký tự loại trừ ký tự kết thúc trận đấu. Tôi biết, một người không có trí tuệ, nhưng tôi đã lãng phí những phút quý giá cho nó và các kịch bản shell nên, sau tất cả, nhanh chóng và dễ dàng. Vì vậy, trong trường hợp người khác có thể cần nó:

Phù hợp tham lam

% echo "<b>foo</b>bar" | sed 's/<.*>//g'
bar

Không tham lam phù hợp

% echo "<b>foo</b>bar" | sed 's/<[^>]*>//g'
foobar

17

Điều này có thể được thực hiện bằng cách sử dụng cắt:

echo "http://www.suepearson.co.uk/product/174/71/3816/" | cut -d'/' -f1-3

9

một cách khác, không sử dụng regex, là sử dụng phương thức trường / dấu phân cách, vd

string="http://www.suepearson.co.uk/product/174/71/3816/"
echo $string | awk -F"/" '{print $1,$2,$3}' OFS="/"

5

sed chắc chắn có vị trí của nó nhưng điều này không phải là một trong số họ!

Như Dee đã chỉ ra: Chỉ cần sử dụng cut. Nó là đơn giản hơn nhiều và an toàn hơn nhiều trong trường hợp này. Dưới đây là một ví dụ nơi chúng tôi trích xuất các thành phần khác nhau từ URL bằng cú pháp Bash:

url="http://www.suepearson.co.uk/product/174/71/3816/"

protocol=$(echo "$url" | cut -d':' -f1)
host=$(echo "$url" | cut -d'/' -f3)
urlhost=$(echo "$url" | cut -d'/' -f1-3)
urlpath=$(echo "$url" | cut -d'/' -f4-)

mang đến cho bạn:

protocol = "http"
host = "www.suepearson.co.uk"
urlhost = "http://www.suepearson.co.uk"
urlpath = "product/174/71/3816/"

Như bạn có thể thấy đây là một cách tiếp cận linh hoạt hơn rất nhiều.

(tất cả tín dụng cho Dee)



3

sed -E diễn giải các biểu thức chính quy như các biểu thức chính quy mở rộng (hiện đại)

Cập nhật: -E trên MacOS X, -r trong GNU sed.


4
Không, nó không ... Ít nhất không phải là GNU sed.
Michel de Ruiter

7
Rộng hơn, -Elà duy nhất cho BSD sedvà do đó OS X. Liên kết đến các trang man. -rkhông mang lại các biểu thức chính quy mở rộng cho GNUsed như đã lưu ý trong phần chỉnh sửa của @ stephancheg. Cẩn thận khi sử dụng một lệnh biến đổi đã biết trên các bản phân phối 'nix. Tôi đã học một bài học đắt giá.
fny

1
Đây là câu trả lời chính xác nếu bạn muốn sử dụng sed, và là câu hỏi phù hợp nhất cho câu hỏi ban đầu.
Sẽ Tice

8
-rTùy chọn của GNU sed chỉ thay đổi các quy tắc thoát, theo Appendix A Extended regular expressionstệp thông tin và một số thử nghiệm nhanh; nó không thực sự thêm một vòng loại không tham lam ( GNU sed version 4.2.1ít nhất là.)
eichin

1
GNU sed được công nhận -Elà một tùy chọn không có giấy tờ trong một thời gian, nhưng trong phiên bản 4.2.2.177 , tài liệu đã được cập nhật để phản ánh điều đó, vì vậy -Ehiện tại vẫn ổn.
Benjamin W.

3

Vẫn còn hy vọng để giải quyết vấn đề này bằng cách sử dụng sed (GNU) thuần túy. Mặc dù đây không phải là một giải pháp chung trong một số trường hợp, bạn có thể sử dụng "vòng lặp" để loại bỏ tất cả các phần không cần thiết của chuỗi như thế này:

sed -r -e ":loop" -e 's|(http://.+)/.*|\1|' -e "t loop"
  • -r: Sử dụng regex mở rộng (cho dấu ngoặc đơn + và không thoát)
  • ": loop": Xác định nhãn mới có tên "loop"
  • -e: thêm lệnh vào sed
  • "T loop": Quay trở lại nhãn "loop" nếu có sự thay thế thành công

Vấn đề duy nhất ở đây là nó cũng sẽ cắt ký tự phân tách cuối cùng ('/'), nhưng nếu bạn thực sự cần nó, bạn vẫn có thể đơn giản đặt lại sau khi "vòng lặp" kết thúc, chỉ cần nối thêm lệnh này vào cuối phần trước dòng lệnh:

-e "s,$,/,"

2

Bởi vì bạn đặc biệt tuyên bố bạn đang cố gắng sử dụng sed (thay vì perl, cut, v.v.), hãy thử nhóm. Điều này phá vỡ định danh không tham lam có khả năng không được công nhận. Nhóm đầu tiên là giao thức (tức là 'http: //', 'https: //', 'tcp: //', v.v.). Nhóm thứ hai là miền:

tiếng vang "http://www.suon.co.uk/product/1/7/3/" | sed "s | ^ \ (. * // \) \ ([^ /] * \). * $ | \ 1 \ 2 |"

Nếu bạn không quen với việc nhóm, hãy bắt đầu ở đây .


1

Tôi nhận ra đây là một mục cũ, nhưng ai đó có thể thấy nó hữu ích. Vì tên miền đầy đủ không được vượt quá tổng chiều dài thay thế là 255 ký tự. * Bằng. \ {1, 255 \}


1

Đây là cách mạnh mẽ thực hiện kết hợp không tham lam của các chuỗi nhiều ký tự bằng cách sử dụng sed. Cho phép nói rằng bạn muốn thay đổi tất cả foo...barđể <foo...bar>ví dụ như vậy đầu vào này:

$ cat file
ABC foo DEF bar GHI foo KLM bar NOP foo QRS bar TUV

nên trở thành đầu ra này:

ABC <foo DEF bar> GHI <foo KLM bar> NOP <foo QRS bar> TUV

Để làm điều đó, bạn chuyển đổi foo và bar thành các ký tự riêng lẻ và sau đó sử dụng phủ định các ký tự đó giữa chúng:

$ sed 's/@/@A/g; s/{/@B/g; s/}/@C/g; s/foo/{/g; s/bar/}/g; s/{[^{}]*}/<&>/g; s/}/bar/g; s/{/foo/g; s/@C/}/g; s/@B/{/g; s/@A/@/g' file
ABC <foo DEF bar> GHI <foo KLM bar> NOP <foo QRS bar> TUV

Ở trên:

  1. s/@/@A/g; s/{/@B/g; s/}/@C/gđang chuyển đổi {}thành chuỗi giữ chỗ không thể tồn tại trong đầu vào để những ký tự đó có sẵn để chuyển đổi foobarsang.
  2. s/foo/{/g; s/bar/}/gđược chuyển đổi foobarđến {}lần lượt
  3. s/{[^{}]*}/<&>/gđang thực hiện op mà chúng ta muốn - chuyển đổi foo...barthành<foo...bar>
  4. s/}/bar/g; s/{/foo/gđang chuyển đổi {}trở lại foobar.
  5. s/@C/}/g; s/@B/{/g; s/@A/@/g đang chuyển đổi các chuỗi giữ chỗ trở lại các ký tự gốc của chúng.

Lưu ý rằng ở trên không phụ thuộc vào bất kỳ chuỗi cụ thể nào không có trong đầu vào vì nó tạo ra các chuỗi đó trong bước đầu tiên, cũng không quan tâm đến sự xuất hiện của bất kỳ biểu thức chính quy nào bạn muốn khớp vì bạn có thể sử dụng {[^{}]*}nhiều lần nếu cần trong biểu thức để cô lập kết quả khớp thực tế mà bạn muốn và / hoặc với toán tử khớp số seds, ví dụ: chỉ thay thế lần xuất hiện thứ 2:

$ sed 's/@/@A/g; s/{/@B/g; s/}/@C/g; s/foo/{/g; s/bar/}/g; s/{[^{}]*}/<&>/2; s/}/bar/g; s/{/foo/g; s/@C/}/g; s/@B/{/g; s/@A/@/g' file
ABC foo DEF bar GHI <foo KLM bar> NOP foo QRS bar TUV

1

Chưa thấy câu trả lời này, vì vậy đây là cách bạn có thể làm điều này với vihoặc vim:

vi -c '%s/\(http:\/\/.\{-}\/\).*/\1/ge | wq' file &>/dev/null

Điều này chạy vi :%sthay thế trên toàn cầu (theo dõi g), kiềm chế không phát sinh lỗi nếu không tìm thấy mẫu ( e), sau đó lưu các thay đổi kết quả vào đĩa và thoát. Các&>/dev/null ngăn chặn GUI nhấp nháy nhanh trên màn hình, điều này có thể gây khó chịu.

viĐôi khi tôi thích sử dụng cho các regex siêu phức tạp, bởi vì (1) perl đã chết dần chết mòn, (2) vim có một công cụ regex rất tiên tiến và (3) Tôi đã quen thuộc với các regex vitrong chỉnh sửa sử dụng hàng ngày của tôi các tài liệu.


0
echo "/home/one/two/three/myfile.txt" | sed 's|\(.*\)/.*|\1|'

đừng làm phiền, tôi đã nhận nó trên một diễn đàn khác :)


4
Vì vậy, bạn nhận được kết hợp tham lam : /home/one/two/three/, nếu bạn thêm một người khác /giống như /home/one/two/three/four/myfile.txtbạn cũng sẽ tham lam phù hợp four: /home/one/two/three/four, câu hỏi là về người không tham lam
stefanB

0

sed 's|\(http:\/\/www\.[a-z.0-9]*\/\).*|\1| làm việc quá


0

Đây là điều bạn có thể làm với cách tiếp cận hai bước và awk:

A=http://www.suepearson.co.uk/product/174/71/3816/  
echo $A|awk '  
{  
  var=gensub(///,"||",3,$0) ;  
  sub(/\|\|.*/,"",var);  
  print var  
}'  

Đầu ra: http://www.suepearson.co.uk

Mong rằng sẽ giúp!


0

Một phiên bản sed khác:

sed 's|/[:alnum:].*||' file.txt

Nó phù hợp /theo sau bởi một ký tự chữ và số (vì vậy không phải là một dấu gạch chéo về phía trước) cũng như các ký tự còn lại cho đến cuối dòng. Sau đó, nó thay thế nó bằng không có gì (ví dụ: xóa nó.)


1
Tôi đoán nó nên "[[:alnum:]]", không phải "[:alphanum:]".
oli_arborum
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.