2014-10-26 15 views
103

Giả sử tôi có 'abbc' chuỗi và tôi muốn thay thế:Làm thế nào để thay thế nhiều mẫu cùng một lúc bằng sed?

  • ab -> bc
  • bc -> ab

Nếu tôi thử hai Thay thế kết quả là không phải những gì tôi muốn :

echo 'abbc' | sed 's/ab/bc/g;s/bc/ab/g' 
abab 

Vậy tôi có thể sử dụng lệnh sed nào để thay thế như dưới đây?

echo abbc | sed SED_COMMAND 
bcab 

EDIT: Trên thực tế các văn bản có thể có nhiều hơn 2 mẫu và tôi không biết có bao nhiêu Thay thế tôi sẽ cần. Vì có một câu trả lời nói rằng sed là một trình soạn thảo luồng và các thay thế của nó là tham lam, tôi nghĩ rằng tôi sẽ cần phải sử dụng một số ngôn ngữ kịch bản cho điều đó.

+0

Bạn có cần thực hiện nhiều thay thế trên cùng một dòng không? Nếu không chỉ cần thả cờ 'g' từ cả hai lệnh' s /// 'này và nó sẽ hoạt động. –

+0

Có, cùng một dòng. – DaniloNC

+0

Bạn đã bỏ lỡ câu hỏi của mình. Tôi có nghĩa là bạn cần phải thực hiện * mỗi * thay thế nhiều hơn một lần trên cùng một dòng. Có nhiều hơn một kết quả phù hợp cho 'ab' * hoặc *' bc' trong đầu vào gốc hay không. –

Trả lời

163

Có lẽ một cái gì đó như thế này:

sed 's/ab/~~/g; s/bc/ab/g; s/~~/bc/g' 

Thay ~ với một nhân vật mà bạn biết sẽ không có trong chuỗi.

+3

GNU sed xử lý nuls, vì vậy bạn có thể sử dụng '\ x0' cho' ~~ '. – jthill

+1

Là 'g' cần thiết và nó làm gì? – Lee

+5

@Lee 'g' là dành cho toàn cầu - nó thay thế tất cả các trường hợp của mẫu trong mỗi dòng, thay vì chỉ lần đầu tiên (đó là hành vi mặc định). – naught101

3

sed là trình chỉnh sửa luồng. Nó tìm kiếm và thay thế tham lam. Cách duy nhất để làm những gì bạn yêu cầu là sử dụng một mẫu thay thế trung gian và thay đổi nó trở lại vào cuối.

echo 'abcd' | sed -e 's/ab/xy/;s/cd/ab/;s/xy/cd/'

5

Điều này có thể làm việc cho bạn (GNU sed):

sed -r '1{x;s/^/:abbc:bcab/;x};G;s/^/\n/;:a;/\n\n/{P;d};s/\n(ab|bc)(.*\n.*:(\1)([^:]*))/\4\n\2/;ta;s/\n(.)/\1\n/;ta' file 

này sử dụng một bảng tra cứu mà được chuẩn bị và tổ chức trong không gian giữ (HS) và sau đó gắn vào mỗi dòng. Một điểm đánh dấu duy nhất (trong trường hợp này là \n) được thêm vào đầu dòng và được sử dụng như một phương thức để làm nổi bật tìm kiếm trong suốt chiều dài của dòng. Khi điểm đánh dấu đến cuối dòng, quá trình đã hoàn thành và được in ra bảng tra cứu và các điểm đánh dấu bị loại bỏ.

N.B. Bảng tra cứu được bắt đầu ngay từ đầu và một điểm đánh dấu duy nhất thứ hai (trong trường hợp này là :) được chọn để không đụng độ với các chuỗi thay thế.

Với một số ý kiến:

sed -r ' 
    # initialize hold with :abbc:bcab 
    1 { 
    x 
    s/^/:abbc:bcab/ 
    x 
    } 

    G  # append hold to patt (after a \n) 

    s/^/\n/ # prepend a \n 

    :a 

    /\n\n/ { 
    P  # print patt up to first \n 
    d  # delete patt & start next cycle 
    } 

    s/\n(ab|bc)(.*\n.*:(\1)([^:]*))/\4\n\2/ 
    ta  # goto a if sub occurred 

    s/\n(.)/\1\n/ # move one char past the first \n 
    ta  # goto a if sub occurred 
' 

bảng này hoạt động như thế này:

** ** replacement 
:abbc:bcab 
** **  pattern 
0

Dưới đây là một awk dựa trên oogas sed

echo 'abbc' | awk '{gsub(/ab/,"xy");gsub(/bc/,"ab");gsub(/xy/,"bc")}1' 
bcab 
1

Tcl có builtin cho điều này

$ tclsh 
% string map {ab bc bc ab} abbc 
bcab 

Điều này hoạt động bằng cách đi bộ chuỗi ký tự tại một thời điểm thực hiện so sánh chuỗi bắt đầu từ vị trí hiện tại.

Trong perl:

perl -E ' 
    sub string_map { 
     my ($str, %map) = @_; 
     my $i = 0; 
     while ($i < length $str) { 
      KEYS: 
      for my $key (keys %map) { 
       if (substr($str, $i, length $key) eq $key) { 
        substr($str, $i, length $key) = $map{$key}; 
        $i += length($map{$key}) - 1; 
        last KEYS; 
       } 
      } 
      $i++; 
     } 
     return $str; 
    } 
    say string_map("abbc", "ab"=>"bc", "bc"=>"ab"); 
' 
bcab 
4

Đây là một biến thể của ooga's answer mà làm việc cho nhiều tìm kiếm và thay thế cặp mà không cần phải kiểm tra như thế nào giá trị có thể được tái sử dụng:

sed -i ' 
s/\bAB\b/________BC________/g 
s/\bBC\b/________CD________/g 
s/________//g 
' path_to_your_files/*.txt 

Dưới đây là ví dụ:

trước:

some text AB some more text "BC" and more text. 

sau:

some text BC some more text "CD" and more text. 

Lưu ý rằng \b biểu thị ranh giới từ, đó là những gì ngăn cản ________ can thiệp vào tìm kiếm (Tôi đang sử dụng GNU sed 4.2.2 trên Ubuntu) . Nếu bạn không sử dụng tìm kiếm ranh giới từ, thì kỹ thuật này có thể không hoạt động.

Cũng lưu ý rằng điều này cho kết quả tương tự như xóa s/________//g và thêm && sed -i 's/________//g' path_to_your_files/*.txt vào cuối lệnh, nhưng không yêu cầu chỉ định đường dẫn hai lần.

Một biến thể chung chung về vấn đề này sẽ được sử dụng \x0 hoặc _\x0_ ở vị trí của ________ nếu bạn biết rằng không có null xuất hiện trong các tập tin của bạn, as jthill suggested.

Các vấn đề liên quan