2009-04-27 39 views
5

Tôi hoàn toàn bị mất trong lập trình vỏ, chủ yếu là vì mỗi trang web tôi sử dụng cung cấp công cụ khác nhau để làm khớp mẫu. Vì vậy, câu hỏi của tôi là công cụ để sử dụng để làm khớp mẫu đơn giản trong luồng đường ống.phù hợp với văn bản trong dấu ngoặc kép (newbie)

ngữ cảnh: Tôi có tệp named.conf và tôi cần tất cả tên vùng trong một tệp đơn giản để xử lý tiếp. Vì vậy, tôi làm ~ $ cat named.local | grep khu vực và nhận được hoàn toàn bị mất ở đây. Đầu ra của tôi là ~ trăm hoặc hơn dòng mới trong mẫu 'zone' domain.tld "{'và tôi cần văn bản trong dấu ngoặc kép.

Cảm ơn bạn đã hiển thị một cách để thực hiện việc này.

J

Trả lời

23

Tôi nghĩ rằng những gì bạn đang tìm kiếm là sed ... đó là một s tream ed itor mà sẽ cho phép bạn làm thay thế trên cơ sở line-by-line.

Khi bạn giải thích, lệnh `cat named.local | Khu grep' mang đến cho bạn một sản lượng một chút như thế này:

zone "domain1.tld" { 
zone "domain2.tld" { 
zone "domain3.tld" { 
zone "domain4.tld" { 

Tôi đoán bạn muốn đầu ra được một cái gì đó như thế này, kể từ khi bạn nói rằng bạn cần văn bản trong dấu ngoặc kép:

"domain1.tld" 
"domain2.tld" 
"domain3.tld" 
"domain4.tld" 

Vì vậy, trên thực tế, từ mỗi dòng, chúng tôi chỉ muốn văn bản giữa các dấu ngoặc kép (bao gồm cả hai dấu ngoặc kép.)

Tôi không chắc bạn đã quen thuộc với Regular Expressions, nhưng chúng là một công cụ vô giá cho bất kỳ ai viết kịch bản shell. Ví dụ: biểu thức chính quy /.o.e/ sẽ khớp với bất kỳ dòng nào có từ thứ 2 có chữ cái viết thường là o và số thứ 4 là e. Điều này sẽ phù hợp với chuỗi có chứa những từ như "zone", "tone", hay thậm chí là "I am tone-deaf."

Bí quyết đã ở đó để sử dụng ký tự . (dot) có nghĩa là "bất kỳ chữ". Có một vài ký tự đặc biệt khác, chẳng hạn như * có nghĩa là "lặp lại ký tự trước đó 0 hoặc nhiều lần". Vì vậy, một biểu hiện thường xuyên như a* sẽ phù hợp với "a", "aaaaaaa", hoặc một chuỗi rỗng: ""

Vì vậy, bạn có thể phù hợp với chuỗi bên trong dấu ngoặc kép sử dụng: /".*"/

Có một điều bạn sẽ biết về sed (và theo nhận xét, bạn đã làm!) - nó cho phép backtracking. Một khi bạn đã nói với nó làm thế nào để nhận ra một từ, bạn có thể có nó sử dụng từ đó như là một phần của sự thay thế. Ví dụ, chúng ta hãy nói rằng bạn muốn chuyển danh sách này:

Billy "The Kid" Smith 
Jimmy "The Fish" Stuart 
Chuck "The Man" Norris 

Into danh sách này:

The Kid 
The Fish 
The Man 

Trước tiên, bạn muốn tìm kiếm chuỗi trong dấu ngoặc kép. Chúng tôi đã thấy rằng, đó là /".*"/.

Tiếp theo, chúng tôi muốn sử dụng nội dung bên trong dấu ngoặc kép.Chúng ta có thể nhóm nó sử dụng dấu ngoặc: /"(.*)"/

Nếu chúng ta muốn thay thế các văn bản với các dấu ngoặc kép với một dấu gạch dưới, chúng tôi muốn làm một thay thế: s/"(.*)"/_/, và điều đó sẽ để lại cho chúng ta:

Billy _ Smith 
Jimmy _ Stuart 
Chuck _ Norris 

Nhưng chúng tôi có backtracking! Điều đó sẽ cho phép chúng ta nhớ lại những gì nằm trong các parens, sử dụng ký hiệu \1. Vì vậy, nếu chúng ta làm gì bây giờ: s/"(.*)"/\1/ chúng tôi sẽ nhận được:

Billy The Kid Smith 
Jimmy The Fish Stuart 
Chuck The Man Norris 

Bởi vì có dấu ngoặc kép là không nằm trong dấu ngoặc, họ không phải là một phần của nội dung của \1!

Để chỉ để nội dung bên trong dấu ngoặc kép, chúng tôi cần khớp toàn bộ dòng. (. Có nghĩa là "cuối dòng") để làm điều đó chúng tôi có ^ (có nghĩa là "sự khởi đầu của dòng"), và $

Vì vậy, bây giờ nếu chúng ta sử dụng s/^.*"(.*)".*$/\1/, chúng tôi sẽ nhận được:

The Kid 
The Fish 
The Man 

Tại sao? Hãy đọc các biểu thức chính quy s/^.*"(.*)".*$/\1/ từ trái sang phải:

  • s/ - Bắt đầu một thay biểu thức chính quy
  • ^ - Nhìn cho sự khởi đầu của dòng. Bắt đầu từ đó.
  • .* - Tiếp tục, đọc từng ký tự, cho đến khi ...
  • " - ... cho đến khi bạn đạt được báo giá kép.
  • ( - bắt đầu nhóm một ký tự mà chúng tôi có thể muốn gọi lại sau khi quay lại.
  • .* - Tiếp tục đi, đọc tất cả các nhân vật, cho đến khi ...
  • ) - (! Pssst đóng nhóm)
  • " - ... cho đến khi bạn đạt được một đôi báo giá.
  • .* - Tiếp tục, đọc từng ký tự, cho đến khi ...
  • $ - Sự kết thúc của dòng!

  • / - sử dụng những gì sau này để thay thế những gì bạn phù hợp

  • \1 - dán nội dung của nhóm đầu tiên (những gì đang ở trong dấu ngoặc) phù hợp.
  • / - cuối biểu thức chính quy

Trong tiếng Anh đơn giản:. "Đọc toàn bộ dòng, sao chép sang một bên các văn bản giữa hai dấu ngoặc kép Sau đó, thay thế toàn bộ phù hợp với nội dung giữa các qoutes đôi."

Bạn thậm chí có thể thêm đúp quote xung quanh văn bản thay thế s/^.*"(.*)".*$/"\1"/, vì vậy chúng tôi sẽ nhận được:

"The Kid" 
"The Fish" 
"The Man" 

Và đó có thể được sử dụng bởi sed để thay thế cho phù hợp với nội dung từ bên trong dấu ngoặc kép:

sed -e "s/^.*\"\(.*\)\".*$/\"\1\"/" 

(Đây chỉ là vỏ thoát để đối phó với hai dấu ngoặc kép và dấu gạch chéo và các công cụ.)

Vì vậy, toàn bộ lệnh wo uld có dạng như sau:

cat named.local | grep zone | sed -e "s/^.*\"\(.*\)\".*$/\"\1\"/" 
+0

Yup, tôi đang sử dụng nó ngay bây giờ, nhưng tôi nghĩ rằng nên có cách dễ dàng hơn để làm điều đó, bởi vì bây giờ tôi sử dụng sed-'s/zone "// g' | sed -e 's /" { // g 'để xóa đầu và cuối của một tệp thay vì chỉ khớp với phần giữa. – jpou

+1

Cạo đầu và cuối là hoàn toàn chấp nhận được. Đây không phải là cuộc thi - nếu nó hoạt động, nó ổn. Nếu bạn muốn làm điều đó bằng cách kết hợp văn bản trong dấu ngoặc kép, hãy xem ‘chụp nhóm’. – zoul

+0

ugh. Tôi đã dành quá nhiều thời gian để đánh máy, và nó vẫn chưa được thực hiện ... có vẻ như mọi người đã đánh tôi với nó. Nhưng tôi vui vì bạn đã tìm ra :-) – scraimer

1

1.

[email protected]:etc$ cat named.conf | grep zone 
zone "." IN { 
zone "localhost" IN { 
    file "localhost.zone"; 
zone "0.0.127.in-addr.arpa" IN { 

2.

[email protected]:etc$ cat named.conf | grep ^zone 
zone "." IN { 
zone "localhost" IN { 
zone "0.0.127.in-addr.arpa" IN { 

3.

[email protected]:etc$ cat named.conf | grep ^zone | sed 's/.*"\([^"]*\)".*/\1/' 
. 
localhost 
0.0.127.in-addr.arpa 

Các regexp là .*"\([^"]*\)".*, mà phù hợp:

  1. bất kỳ số lượng bất kỳ ký tự: .*
    • báo giá: "
    • bắt đầu nhớ cho sau này: \(
    • bất kỳ ký tự trừ quote: [^"]*
    • kết thúc nhóm cần nhớ: \)
    • đóng quote : "
    • và bất kỳ số ký tự nào: .*

Khi gọi sed, cú pháp là 's/what_to_match/what_to_replace_it_with/'. Các dấu ngoặc kép duy nhất có để giữ cho regexp của bạn không bị mở rộng thêm bash. Khi bạn "nhớ" một cái gì đó trong regexp bằng cách sử dụng parens, bạn có thể gọi nó là \1, \2 vv Fiddle với nó trong một thời gian.

2

Vâng, không ai đề cập cut, vì vậy, để chứng minh rằng có rất nhiều cách để làm điều gì đó với vỏ:

% grep '^zone' /etc/bind/named.conf | cut -d' ' -f2 
"gennic.net" 
"generic-nic.net" 
"dyn.generic-nic.net" 
"langtag.net" 
0

Chừng nào ai đó đang chỉ ra sed/awk, tôi sẽ chỉ ra rằng grep là dư thừa.

sed -ne '/^zone/{s/.*"\([^"]*\)".*/\1/;p}' /etc/bind/named.conf 

Điều này cung cấp cho bạn những gì bạn đang tìm kiếm mà không có dấu ngoặc kép (di chuyển dấu ngoặc kép trong dấu ngoặc đơn để giữ chúng).Trong awk, nó thậm chí còn đơn giản hơn với các dấu ngoặc kép:

awk '/^zone/{print $2}' /etc/bind/named.conf 

Tôi cố gắng tránh đường ống càng nhiều càng tốt (nhưng không nhiều hơn). Hãy nhớ rằng, Don't pipe cat. Nó không cần thiết. Và, như awk và sed sao chép công việc của grep, không grep ống, một trong hai. Ít nhất, không vào sed hoặc awk.

Cá nhân, tôi có thể đã sử dụng perl. Nhưng đó là bởi vì tôi có thể đã làm phần còn lại của bất kỳ thứ gì bạn đang làm trong perl, làm cho nó trở thành một chi tiết nhỏ (và có thể slurp toàn bộ tập tin và regex chống lại mọi thứ cùng một lúc, bỏ qua \ n's sẽ là tiền thưởng cho các trường hợp Tôi không kiểm soát/etc/bind, chẳng hạn như trên một webhost chia sẻ). Nhưng, nếu tôi làm điều đó trong vỏ, một trong hai điều trên sẽ là cách tôi tiếp cận nó.

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