Thay thế một ký tự ngoại trừ lần xuất hiện cuối cùng


9

Tôi có một tệp có một loạt tên máy chủ tương quan với IP trông như thế này:

x-cluster-front-1 192.168.1.2
x-cluster-front-2 192.158.1.10
y-cluster-back-1 10.1.11.99
y-cluster-back-2 10.1.157.38
int.test.example.com 59.2.86.3
super.awesome.machine 123.234.15.6

Tôi muốn nó trông như thế này:

x-cluster-front-1 192.168.1.2
x-cluster-front-2 192.158.1.10
y-cluster-back-1 10.1.11.99
y-cluster-back-2 10.1.157.38
int-test-example-com 59.2.86.3
super-awesome-machine 123.234.15.6

Làm thế nào tôi có thể thay thế. (dấu chấm) từ cột đầu tiên với - (dấu gạch nối) để tạo điều kiện sắp xếp theo cột thứ hai? Tôi đã nghĩ đến việc sử dụng sed để thay thế các dấu chấm cho đến khoảng trắng đầu tiên, hoặc thay thế mọi dấu chấm trừ ba dấu chấm cuối cùng, nhưng tôi gặp khó khăn trong việc hiểu regex và sed. Tôi có thể thực hiện thay thế đơn giản nhưng đây là cách trên đầu của tôi!

Đây là một phần của một kịch bản lớn hơn mà tôi đã viết trong bash. Tôi bị mắc kẹt ở phần này.

Câu trả lời:


7

Bạn có thể sử dụng AWK

awk '{gsub(/-/,".",$1);print}' infile

Giải trình

awkchia một dòng trên khoảng trắng theo mặc định. Do đó, cột đầu tiên của dòng ( $1in awk-ese) sẽ là cột bạn muốn thực hiện thay thế trên. Đối với mục đích này, bạn có thể sử dụng:

 gsub(regex,replacement,string)

để thực hiện thay thế cần thiết.

Lưu ý rằng gsubchỉ được hỗ trợ cho gawknawknhưng trên nhiều bản phân phối hiện đại awklà một liên kết mềm gawk.


1
+1 Đánh bại tôi với nó. Tôi nghĩ rằng một lời giải thích sẽ thực sự có lợi cho người hỏi và độc giả tương lai.
Joseph R.

1
@JosephR. Xin lỗi tôi không giỏi giải thích nhưng tôi đã thử và cập nhật ..
Rahul Patil

2
Thông số POSIX cho awkdựa trên nawk, vì vậy tất cả các awktriển khai hiện đại nên có gsub. Trên Solaris, bạn có thể cần /usr/xpg4/bin/awkhoặc nawk.
Stéphane Chazelas

@RahulPatil Nếu bạn không phiền, tôi đã thêm một vài dòng mà tôi nghĩ sẽ giúp được người khác.
Joseph R.

@JosephR cảm ơn .., bây giờ có vẻ hoàn hảo .. :)
Rahul Patil

6

Nếu bạn cần thực hiện các thay thế trên trường đầu tiên, tốt nhất là sử dụng giải pháp awk của Rahul nhưng hãy cẩn thận, nó có thể ảnh hưởng đến khoảng cách (các trường được viết lại với một khoảng trắng ở giữa chúng).

Bạn có thể tránh nó bằng cách viết nó thay vào đó:

perl -pe 's|\S+|$&=~tr/./-/r|e' file

Các -pphương tiện cờ "đọc dòng tập tin đầu vào theo dòng và in mỗi dòng sau khi áp dụng các kịch bản do -e". Sau đó, thay thế ( s|pattern|replacement|) chuỗi ký tự đầu tiên không phải dấu cách ( \S+) bằng mẫu phù hợp ( $&) sau khi thay thế tất cả .bằng -. Mẹo là sử dụng s|||etrong đó etoán tử sẽ đánh giá một biểu thức là sự thay thế. Vì vậy, bạn có thể tr/./-/áp dụng một thay thế ( ) cho khớp ( $&) của trước đó ( s|||e).

Nếu bạn cần thay thế mọi thứ .bằng một -ngoại trừ 3 cái cuối cùng, bằng GNU sedvà giả sử bạn có một revlệnh:

rev file | sed 's/\./-/4g' | rev

1
Lưu ý rằng giải pháp Perl giả định phiên bản 5.14 trở lên ( /rđể hoạt động).
Joseph R.

3

Sed không phải là công cụ dễ nhất cho công việc - xem các câu trả lời khác để biết các công cụ tốt hơn - nhưng nó có thể được thực hiện.

Để thay thế chỉ .bằng -không gian đầu tiên, sử dụng strong một vòng lặp.

sed -e '
  : a                     # Label "a" for the branching command
  s/^\([^ .]*\)\./\1-/    # If there is a "." before the first space, replace it by "-"
  t a                     # If the s command matched, branch to a
'

(Lưu ý rằng một số triển khai sed không hỗ trợ nhận xét trên cùng một dòng. GNU sed hiện.)

Thay vào đó, thực hiện thay thế đến không gian cuối cùng:

sed -e '
  : a                     # Label "a" for the branching command
  s/\.\(.* \)/-\1/        # If there is a "." before the last space, replace it by "-"
  t a                     # If the s command matched, branch to a
'

Một kỹ thuật khác sử dụng không gian giữ của sed. Lưu bit bạn không muốn sửa đổi vào không gian giữ, thực hiện công việc của bạn, sau đó gọi lại không gian giữ. Ở đây, tôi chia dòng ở không gian cuối cùng và thay thế dấu chấm bằng dấu gạch ngang trong phần đầu tiên.

sed -e '
  h           # Save the current line to the hold space
  s/.* / /    # Remove everything up to the last space
  x           # Swap the work space with the hold space
  s/[^ ]*$//  # Remove everything after the last space
  y/./-/      # Replace all "." by "-"
  G           # Append the content of the hold to the work space
  s/\n//      # Remove the newline introduced by G
'

2

Vì Rahul đã cho bạn câu trả lời chính tắc cho trường hợp sử dụng của bạn, tôi nghĩ rằng tôi sẽ cố gắng trả lời vấn đề chính: thay thế tất cả trừ lần xuất hiện cuối cùng của biểu thức chính quy:

perl -pe '
    $count = tr{.}{.}; # Count '.' on the current line
    $x = 3;
    next LINE if $count <= $x;
    while(s{\.}{-}){   # Substitute one '.' with a '-'
        last if ++$i == $count - $x # Quit the loop before the last x substitutions
    }
$i = 0
' your_file

Đoạn mã trên (đã kiểm tra) không cho rằng bạn có các trường được phân tách bằng dấu cách. Nó sẽ thay thế tất cả các dấu chấm trên một dòng bằng dấu gạch ngang trừ 3 dấu chấm cuối cùng. Thay thế 3mã trong ý thích của bạn.


2

Bạn có thể sử dụng nhiều công cụ khác nhau cho việc này. Rahul Patil đã cho bạn một gawkcái nên đây là một vài thứ khác:

  • perl

    perl -lane  '$F[0]=~s/\./-/g; print "@F"' file
    

    Công -atắc khiến perl tự động phân chia các dòng đầu vào trên khoảng trắng và lưu các trường kết quả vào mảng @F. Do đó, trường đầu tiên sẽ là $F[0]vì vậy chúng tôi thay thế ( s///) tất cả các lần xuất hiện .với -trong trường đầu tiên và sau đó in toàn bộ mảng.

  • vỏ

     while read -r a b; do printf "%s %s\n" "${a//./-}" "$b"; done < file 
    

    Ở đây, vòng lặp while đọc tệp và tự động phân tách trên khoảng trắng. Điều này tạo ra hai trường $first$rest. Cấu trúc ${first//pattern/replacement}thay thế tất cả các lần xuất hiện patternvới replacement.


+1 Trong khi perlrun(1)sẽ cho bạn biết đó -alà "chế độ tự động", tôi thích nghĩ về nó như " awkchế độ": D
Joseph R.

2

Tôi tin rằng đây là một chút dễ đọc hơn một regex lớn khó chịu. Về cơ bản tôi chỉ chia dòng thành hai trường ở khoảng trắng và sử dụng sed trên phần đầu tiên.

while read -r host ip; do
    echo "$(sed 's/\./-/g' <<< "$host") $ip"
done < input_file

Tùy thuộc vào trình bao của bạn, bạn cũng có thể sử dụng $ {host //./-} thay cho lệnh sed.


0
sed 's/\./-/' <file name>

Không cần sử dụng gở cuối lệnh, bạn có thể thực hiện điều này. Điều này chỉ đơn giản là thay thế lần xuất hiện đầu tiên của mẫu

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.