Truyền biến shell dưới dạng / mẫu / cho awk


59

Có các chức năng sau trong một trong các hàm shell của tôi:

function _process () {
  awk -v l="$line" '
  BEGIN {p=0}
  /'"$1"'/ {p=1}
  END{ if(p) print l >> "outfile.txt" }
  '
}

, vì vậy khi được gọi là as _process $arg, $argđược truyền vào $1và được sử dụng làm mẫu tìm kiếm. Nó hoạt động theo cách này, vì vỏ mở rộng $1thay cho mẫu awk! Cũng lcó thể được sử dụng trong chương trình awk, được khai báo với -v l="$line". Tất cả đều ổn.

Có thể theo cách tương tự đưa ra mô hình để tìm kiếm như một biến?

Theo dõi sẽ không hoạt động,

awk -v l="$line" -v search="$pattern" '
  BEGIN {p=0}
  /search/ {p=1}
  END{ if(p) print l >> "outfile.txt" }
  '

, vì awk sẽ không diễn giải /search/như một biến, mà thay vào đó theo nghĩa đen.

Câu trả lời:


46

Sử dụng ~toán tử của awk và bạn không cần phải cung cấp biểu thức chính tả ở phía bên phải:

function _process () {
    awk -v l="$line" -v pattern="$1" '
        $0 ~ pattern {p=1} 
        END {if(p) print l >> "outfile.txt"}
    '  
}

Mặc dù điều này sẽ hiệu quả hơn (không phải đọc toàn bộ tệp)

function _process () {
    grep -q "$1" && echo "$line"
}

Tùy thuộc vào mẫu, có thể muốn grep -Eq "$1"


Đây chính xác là những gì giải quyết điều này theo cách tôi muốn (ví dụ đầu tiên), bởi vì nó giữ được ngữ nghĩa, đó là mục tiêu của tôi. Cảm ơn.
Branquito

1
Tôi đã không lưu ý việc loại bỏ khối BEGIN: một biến không được gán được coi là 0 trong ngữ cảnh số hoặc chuỗi trống. Vì vậy, một biến không được gán sẽ là sai trongif (p) ...
glenn jackman

vâng tôi nhận thấy, nó cần được đặt trên khối BEGIN về 0 mỗi lần, vì nó đóng vai trò là một công tắc. Nhưng thú vị là tôi đã thử sử dụng tập lệnh ngay bây giờ $0 ~ patternvà nó không hoạt động, tuy nhiên với /'"$1"'/nó thì nó hoạt động!? : O
branquito 21/03 '

có lẽ nó có liên quan đến cách $linelấy ra, tìm kiếm mẫu được thực hiện trên đầu ra của whois $line, $lineđến từ tệp trong khối WHILE DO.
branquito

Vui lòng hiển thị nội dung của $line- làm điều đó trong câu hỏi của bạn để định dạng thích hợp.
glenn jackman

17
awk  -v pattern="$1" '$0 ~ pattern'

Có một vấn đề trong đó awkmở rộng các chuỗi thoát ANSI C (như \nđối với dòng mới, \fcho nguồn cấp dữ liệu biểu mẫu, \\cho dấu gạch chéo ngược, v.v.) trong $1. Vì vậy, nó trở thành một vấn đề nếu $1chứa các ký tự dấu gạch chéo ngược phổ biến trong các biểu thức thông thường (với GNU awk4.2 trở lên, các giá trị bắt đầu bằng @/và kết thúc bằng /, cũng là một vấn đề ). Một cách tiếp cận khác không gặp phải vấn đề đó là viết nó:

PATTERN=$1 awk '$0 ~ ENVIRON["PATTERN"]'

Làm thế nào xấu nó sẽ được phụ thuộc vào việc awkthực hiện.

$ nawk -v 'a=\.' 'BEGIN {print a}'
.
$ mawk -v 'a=\.' 'BEGIN {print a}'
\.
$ gawk -v 'a=\.' 'BEGIN {print a}'
gawk: warning: escape sequence `\.' treated as plain `.'
.
$ gawk5.0.1 -v 'a=@/foo/' BEGIN {print a}'
foo

Tất cả đều awkhoạt động giống nhau cho các chuỗi thoát hợp lệ mặc dù:

$ a='\\-\b' awk 'BEGIN {print ENVIRON["a"]}' | od -tc
0000000   \   \   -   \   b  \n
0000006

(nội dung $athông qua nguyên trạng)

$ awk -v a='\\-\b' 'BEGIN {print a}' | od -tc
0000000   \   -  \b  \n
0000004

( \\thay đổi thành \\bthay đổi thành một ký tự backspace).


Vì vậy, bạn đang nói rằng nếu mẫu là ví dụ \d{3}để tìm ba chữ số, điều đó sẽ không hoạt động như mong đợi, nếu tôi hiểu rõ về bạn?
branquito

2
cho \dđó không phải là một chuỗi C thoát hợp lệ, mà phụ thuộc vào bạn awkthực hiện (chạy awk -v 'a=\d{3}' 'BEGIN{print a}'để kiểm tra). Nhưng với \` or \ b , yes definitely. (BTW, I don't know of any awk implementations that understands \ d` có nghĩa là một chữ số).
Stéphane Chazelas

nó nói: cảnh báo awk - chuỗi thoát \d' treated as plain d 'd {3}, vì vậy tôi đoán tôi sẽ gặp vấn đề trong trường hợp này?
branquito

1
Xin lỗi, xấu của tôi, tôi đã có một lỗi đánh máy trong câu trả lời của tôi. Tên của biến môi trường sau đó phải khớp ENVIRON["PATTERN"]với PATTERNbiến môi trường. Nếu bạn muốn sử dụng biến shell, trước tiên bạn cần xuất nó ( export variable) hoặc sử dụng ENV=VALUE awk '...ENVIRON["ENV"]'cú pháp truyền env-var như trong câu trả lời của tôi.
Stéphane Chazelas

1
Bởi vì bạn cần xuất một biến shell để nó được truyền trong môi trường sang một lệnh.
Stéphane Chazelas

5

Hãy thử một cái gì đó như:

awk -v l="$line" -v search="$pattern" 'BEGIN {p=0}; { if ( match( $0, search )) {p=1}}; END{ if(p) print l >> "outfile.txt" }'

Nếu điều này hoạt động giống như /regex/trong việc tìm mẫu, đây có thể là một giải pháp tốt. Tôi sẽ thử.
branquito

1
Các bài kiểm tra nhanh mà tôi đã chạy dường như hoạt động tương tự, nhưng tôi thậm chí sẽ không bắt đầu đảm bảo nó ... :)
Hunter Eidson

0

Không, nhưng bạn có thể chỉ cần nội suy mẫu vào chuỗi trích dẫn kép mà bạn chuyển đến awk:

awk -v l="$line" "BEGIN {p=0}; /$pattern/ {p=1}; END{ if(p) print l >> \"outfile.txt\" }"

Lưu ý rằng bây giờ bạn phải thoát khỏi chữ awk được trích dẫn hai lần, nhưng nó vẫn là cách đơn giản nhất để thực hiện điều này.


Cách này có an toàn không nếu $patternchứa khoảng trắng, ví dụ của tôi ở trên sẽ hoạt động vì $ 1 được bảo vệ với dấu ngoặc kép "$ 1", tuy nhiên không chắc chắn điều gì xảy ra trong trường hợp của bạn.
branquito

2
Ví dụ ban đầu của bạn kết thúc chuỗi trích dẫn đơn ở giây thứ hai ', sau đó bảo vệ $1thông qua dấu ngoặc kép và sau đó xử lý một chuỗi trích dẫn đơn khác cho nửa sau của chương trình awk. Nếu tôi hiểu chính xác, điều này sẽ có tác dụng chính xác như bảo vệ $1thông qua các trích dẫn đơn bên ngoài - awk không bao giờ nhìn thấy các trích dẫn kép mà bạn đặt xung quanh nó.
Kilian Foth

4
Nhưng nếu $pattern^/ {system("rm -rf /")};, thì bạn đang gặp rắc rối lớn.
Stéphane Chazelas

đó chỉ là nhược điểm của phương pháp này, có tất cả được gói gọn trong ""?
Branquito

-3

Bạn có thể sử dụng hàm eval giải quyết trong ví dụ này biến lưới trước khi awk được chạy.

nets="searchtext"
eval "awk '/"${nets}"/'" file.txt
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.