Làm cách nào để sử dụng biến shell trong tập lệnh awk?


289

Tôi đã tìm thấy một số cách để chuyển các biến shell bên ngoài vào một awktập lệnh, nhưng tôi bối rối về '".

Đầu tiên, tôi đã thử với một kịch bản shell:

$ v=123test
$ echo $v
123test
$ echo "$v"
123test

Sau đó thử awk:

$ awk 'BEGIN{print "'$v'"}'
$ 123test
$ awk 'BEGIN{print '"$v"'}'
$ 123

Tại sao có sự khác biệt?

Cuối cùng tôi đã thử điều này:

$ awk 'BEGIN{print " '$v' "}'
$  123test
$ awk 'BEGIN{print ' "$v" '}'
awk: cmd. line:1: BEGIN{print
awk: cmd. line:1:             ^ unexpected newline or end of string 

Tôi bối rối về điều này.


2
Tôi thích -v như hình dưới đây, nhưng đây thực sự là một bài tập tuyệt vời trong suy nghĩ về cách bảo vệ mọi thứ khỏi vỏ. Làm việc này, lần cắt đầu tiên của tôi sử dụng dấu gạch chéo ngược trên dấu cách và dấu đô la. Không cần phải nói các ví dụ ở đây cũng đáng giá thời gian của tôi.
Chris


Nếu tìm kiếm awk của bạn cần biểu thức chính quy , bạn không thể đặt /var/. Thay vào đó, hãy sử dụng dấu ngã:awk -v var="$var" '$0 ~ var'
Noam Manos

Câu trả lời:


496

Bắt các biến shell vào awk

có thể được thực hiện theo nhiều cách. Một số tốt hơn so với những người khác. Điều này sẽ bao gồm hầu hết trong số họ. Nếu bạn có một bình luận, xin vui lòng để lại dưới đây. v1.5


Sử dụng -v (Cách tốt nhất, di động nhất)

Sử dụng -vtùy chọn: (PS sử dụng khoảng trắng sau -vhoặc nó sẽ ít di động hơn. Ví dụ: awk -v var=không awk -vvar=)

variable="line one\nline two"
awk -v var="$variable" 'BEGIN {print var}'
line one
line two

Điều này phải tương thích với hầu hết awkvà biến cũng có sẵn trong BEGINkhối:

Nếu bạn có nhiều biến:

awk -v a="$var1" -v b="$var2" 'BEGIN {print a,b}'

Cảnh báo . Như Ed Morton viết, các chuỗi thoát sẽ được diễn giải để \ttrở thành hiện thực tabvà không phải \tnếu đó là những gì bạn tìm kiếm. Có thể được giải quyết bằng cách sử dụng ENVIRON[]hoặc truy cập thông quaARGV[]

PS Nếu bạn thích ba thanh dọc làm dấu phân cách |||, nó không thể thoát được, vì vậy hãy sử dụng-F"[|][|][|]"

Ví dụ về việc lấy dữ liệu từ một nhà trọ chương trình / chức năng đến awk(ngày này được sử dụng)

awk -v time="$(date +"%F %H:%M" -d '-1 minute')" 'BEGIN {print time}'

Biến sau khối mã

Ở đây chúng tôi nhận được các biến sau awkmã. Điều này sẽ hoạt động tốt miễn là bạn không cần biến trong BEGINkhối:

variable="line one\nline two"
echo "input data" | awk '{print var}' var="${variable}"
or
awk '{print var}' var="${variable}" file
  • Thêm nhiều biến:

awk '{print a,b,$0}' a="$var1" b="$var2" file

  • Theo cách này, chúng tôi cũng có thể đặt Dấu tách trường khác nhau FScho mỗi tệp.

awk 'some code' FS=',' file1.txt FS=';' file2.ext

  • Biến sau khối mã sẽ không hoạt động cho BEGINkhối:

echo "input data" | awk 'BEGIN {print var}' var="${variable}"


Chuỗi đây

Biến cũng có thể được thêm vào awkbằng cách sử dụng chuỗi ở đây từ các shell hỗ trợ chúng (bao gồm Bash):

awk '{print $0}' <<< "$variable"
test

Điều này giống như:

printf '%s' "$variable" | awk '{print $0}'

PS này coi biến là một đầu vào tập tin.


ENVIRON đầu vào

Như TrueY viết, bạn có thể sử dụng ENVIRONđể in Biến môi trường . Đặt một biến trước khi chạy AWK, bạn có thể in nó ra như sau:

X=MyVar
awk 'BEGIN{print ENVIRON["X"],ENVIRON["SHELL"]}'
MyVar /bin/bash

ARGV đầu vào

Như Steven Penny viết, bạn có thể sử dụng ARGVđể đưa dữ liệu vào awk:

v="my data"
awk 'BEGIN {print ARGV[1]}' "$v"
my data

Để lấy dữ liệu vào chính mã, không chỉ BEGIN:

v="my data"
echo "test" | awk 'BEGIN{var=ARGV[1];ARGV[1]=""} {print var, $0}' "$v"
my data test

Biến trong mã: SỬ DỤNG VỚI THẬN TRỌNG

Bạn có thể sử dụng một biến trong awkmã, nhưng nó lộn xộn và khó đọc, và như đã Charles Duffychỉ ra, phiên bản này cũng có thể là nạn nhân của việc tiêm mã. Nếu ai đó thêm nội dung xấu vào biến, nó sẽ được thực thi như một phần của awkmã.

Điều này hoạt động bằng cách trích xuất biến trong mã, vì vậy nó trở thành một phần của nó.

Nếu bạn muốn thực hiện một awkthay đổi linh hoạt khi sử dụng các biến, bạn có thể thực hiện theo cách này, nhưng KHÔNG sử dụng nó cho các biến thông thường.

variable="line one\nline two"
awk 'BEGIN {print "'"$variable"'"}'
line one
line two

Dưới đây là một ví dụ về tiêm mã:

variable='line one\nline two" ; for (i=1;i<=1000;++i) print i"'
awk 'BEGIN {print "'"$variable"'"}'
line one
line two
1
2
3
.
.
1000

Bạn có thể thêm rất nhiều lệnh awktheo cách này. Thậm chí làm cho nó sụp đổ với các lệnh không hợp lệ.


Thông tin thêm:

Sử dụng báo giá kép

Luôn luôn tốt để nhân đôi biến trích dẫn "$variable"
Nếu không, nhiều dòng sẽ được thêm dưới dạng một dòng dài.

Thí dụ:

var="Line one
This is line two"

echo $var
Line one This is line two

echo "$var"
Line one
This is line two

Các lỗi khác bạn có thể nhận được mà không cần trích dẫn kép:

variable="line one\nline two"
awk -v var=$variable 'BEGIN {print var}'
awk: cmd. line:1: one\nline
awk: cmd. line:1:    ^ backslash not last character on line
awk: cmd. line:1: one\nline
awk: cmd. line:1:    ^ syntax error

Và với trích dẫn duy nhất, nó không mở rộng giá trị của biến:

awk -v var='$variable' 'BEGIN {print var}'
$variable

Thông tin thêm về AWK và các biến

Đọc faq này .


2
"Lộn xộn và khó đọc" bỏ qua mối quan tâm bảo mật quan trọng hơn của việc tiêm mã khi trực tiếp thay thế chuỗi thành mã awk.
Charles Duffy

đọc câu trả lời ở trên tôi có thể chạy tập lệnh của mình mà không gặp lỗi nhưng nó không thực hiện được công việc: awk -v repo = "$ 1" -v tag = "$ 2" '{sub (/ image: registryabx.azurecr.io \ / { in repo}: ([a-z0-9] +) $ /, "hình ảnh: registryabc.azurecr. io / {print repo}: {thẻ in}");} 1 './service/appscompose.yaml >> newcompose.yaml. Là do dấu ngoặc đơn lồng nhau {?
Darion Badlydone

@DarionBadlydone Hãy thử điều này awk -v repo="$1" -v tag="$2" 'BEGIN {print "repo="repo,"tag="tag}'. Nó sẽ xem nếu nó in biến. Gửi một câu hỏi riêng nếu bạn không thể tìm ra.
Jotne

@Jotne có, nó in các giá trị nên tôi đã thử theo cách này: awk -v repo = "$ 1" -v tag = "$ 2" '{print "{sub (/ image: registryabc.azurecr.io/"repo" :( [a-z0-9] +) $ /, \ "hình ảnh: registryabc.azurecr.io/"repo":"tag"\"); Bolog1"} './service/appscompose.yaml >> newcompose.yaml nhưng không hoạt động như mong muốn. Nó thay thế từng dòng của tệp nguồn bằng lệnh được in
Darion Badlydone

@Jotne Tôi đã làm điều đó với sed, Dù sao cũng cảm ơn bạn
Darion Badlydone

28

Dường như người tốt ENVIRON hàm băm tích hợp không được đề cập ở tất cả. Một ví dụ về cách sử dụng của nó:

$ X=Solaris awk 'BEGIN{print ENVIRON["X"], ENVIRON["TERM"]}'
Solaris rxvt

4
Đây là một gợi ý tốt vì nó vượt qua nguyên văn dữ liệu. -vkhông hoạt động khi giá trị chứa dấu gạch chéo ngược.
anh chàng kia

2
@thatotherguy Mình không biết điều đó! Tôi nghĩ rằng nếu tôi sử dụng awk -v x='\c\d' ...thì nó sẽ được sử dụng đúng cách. Nhưng khi xđược in, awk sẽ nổi tiếng: awk: warning: escape sequence '\c' treated as plain 'c'thông báo lỗi ... Cảm ơn!
TrueY

Nó hoạt động đúng - đúng trong ngữ cảnh này có nghĩa là mở rộng các chuỗi thoát vì đó là cách nó -vđược thiết kế để hoạt động để bạn có thể sử dụng \tbiến đó và ví dụ như nó phù hợp với một tab bằng chữ trong dữ liệu. Nếu đó không phải là hành vi bạn muốn thì bạn không sử -vdụng ARGV[]hoặc sử dụng ENVIRON[].
Ed Morton

9

Sử dụng một trong hai tùy thuộc vào cách bạn muốn dấu gạch chéo ngược trong các biến shell được xử lý ( avarlà biến awk, svarlà biến shell):

awk -v avar="$svar" '... avar ...' file
awk 'BEGIN{avar=ARGV[1];ARGV[1]=""}... avar ...' "$svar" file

Xem http://cfajohnson.com/shell/cus-faq-2.html#Q24 để biết chi tiết và các tùy chọn khác. Phương pháp đầu tiên ở trên hầu như luôn luôn là lựa chọn tốt nhất của bạn và có ngữ nghĩa rõ ràng nhất.


6

Bạn có thể chuyển trong tùy chọn dòng lệnh -v với tên biến ( v) và giá trị ( =) của biến môi trường ( "${v}"):

% awk -vv="${v}" 'BEGIN { print v }'
123test

Hoặc để làm cho nó rõ ràng hơn (với ít hơn rất nhiều v):

% environment_variable=123test
% awk -vawk_variable="${environment_variable}" 'BEGIN { print awk_variable }'
123test

3

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

v=123test
awk 'BEGIN {print ARGV[1]}' "$v"

Lưu ý rằng nếu bạn sẽ tiếp tục vào cơ thể, bạn sẽ cần điều chỉnh ARGC:

awk 'BEGIN {ARGC--} {print ARGV[2], $0}' file "$v"

1

Tôi vừa thay đổi câu trả lời của @ Jotne cho "vòng lặp".

for i in `seq 11 20`; do host myserver-$i | awk -v i="$i" '{print "myserver-"i" " $4}'; done

1
Đây dường như chỉ là một minh họa khác về cách sử dụng -vtùy chọn của Awk đã được đề cập trong nhiều câu trả lời hiện có. Nếu bạn muốn chỉ ra cách chạy Awk trong một vòng lặp, đó thực sự là một câu hỏi khác.
tripleee

0

Tôi đã phải chèn ngày ở đầu dòng của tệp nhật ký và nó được thực hiện như dưới đây:

DATE=$(date +"%Y-%m-%d")
awk '{ print "'"$DATE"'", $0; }' /path_to_log_file/log_file.log

Nó có thể được chuyển hướng đến một tập tin khác để lưu


Báo giá kép - trích dẫn đơn - trích dẫn kép chính xác là những gì tôi cần để làm cho công việc của tôi.
dùng53029

2
Điều này đã được đề cập trong câu trả lời được chấp nhận như một phương pháp bạn không nên sử dụng do lỗ hổng tiêm mã. Vì vậy, thông tin ở đây là dư thừa (đã được mô tả trong câu trả lời được chấp nhận) và không đầy đủ (không đề cập đến các vấn đề với phương pháp này).
Jason S
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.