(OK, xin lỗi, tôi đã đọc câu hỏi của bạn quá nhanh, vì vậy một số câu trả lời của tôi có một chút bên cạnh vấn đề, vẫn để nó vì nó có thể hữu ích cho bạn hoặc một số người)
Có một số điều cần xem xét ở đây.
trích dẫn các biến shell
Để lại một biến không được trích dẫn trong các vỏ POSIX (trong ngữ cảnh danh sách, như trong các đối số cho một lệnh), không awk
, là toán tử split + global.
Nếu bạn làm:
cmd foo=$var
Trong trường hợp $var
là * *
.
Không yêu cầu shell phân chia nội dung $var
dựa trên giá trị của $IFS
biến shell đặc biệt, theo mặc định trên các khoảng trống. Vì vậy, ở trên, điều đó mang lại cho chúng ta foo=*
và *
thực hiện toàn cầu trên mỗi cái, được mở rộng foo=*
ra tất cả các tên tệp trong thư mục hiện tại bắt đầu foo=
và *
cho tất cả các tên tệp không bị ẩn.
Vì vậy, thực sự, bạn hầu như luôn luôn trích dẫn các biến shell của mình , cho dù chúng có phải là đối số awk
hay không. Điều đó cũng áp dụng cho thay thế lệnh shell ( `...`
và $(...)
) và mở rộng số học shell ( $((...))
).
truyền dữ liệu nguyên trạng awk
Vấn đề khác là awk
(không phải shell) mở rộng các chuỗi thoát dấu gạch chéo ngược trong các phép gán của các biến như -v var=value
(và với GNU awk
4.2 trở lên, nếu giá trị bắt đầu bằng @/
và kết thúc /
, nó được coi là một loại biến regrec ).
Chẳng hạn, -v var='\n/\n/'
đặt nội dung của awk
var
biến thành <newline>/<newline>/
, không \n/\n/
. Điều đó cũng áp dụng cho awk
các biến được định nghĩa là:
awk '...' var=value
Để truyền dữ liệu đến awk
mà không trải qua quá trình mở rộng đó, bạn có thể sử dụng các mảng ENVIRON
hoặc ARGV
awk:
var=$value awk 'BEGIN {var=ENVIRON["var"]} ...'
.
hoặc là:
awk 'BEGIN {var=ARGV[1]; delete ARGV[1]} ...' "$value"
trích dẫn và awk
các biến
Sự phân chia + toàn cầu đó chỉ là một tính năng shell (mis-). Các awk
ngôn ngữ là một ngôn ngữ hoàn toàn khác nhau.
Trong awk
, các biến được tham chiếu đến a varname
, không $varname
và dấu ngoặc kép được sử dụng để giới thiệu chuỗi. Vậy "varname"
là varname
chuỗi, trong khi varname
đề cập đến biến.
vệ sinh các biến để tránh tiêm mã
Nói một cách chính xác, trích dẫn các biến shell không vệ sinh, nó không trích dẫn các biến đang sử dụng toán tử split + global. Mặc dù trong hầu hết các ngôn ngữ bạn đặt dấu ngoặc kép quanh các chuỗi cố định, trong shell, nó lại theo cách khác: mọi thứ đều là chuỗi và dấu ngoặc kép được sử dụng để ngăn chặn một số hành vi đặc biệt và đặc biệt là các biến nên luôn luôn được trích dẫn (một quyết định thiết kế kém loại đó có ý nghĩa trong lớp vỏ Bourne vào những năm 70, nhưng là một trở ngại trong lớp vỏ hiện đại, zsh
là lớp vỏ duy nhất cố định một phần điều đó).
Shell hoặc awk sẽ không đánh giá / giải thích mã được lưu trữ trong biến của riêng họ trừ khi bạn bảo họ.
var='foo; rm -f var'
echo $var
# or
echo "$var"
Sẽ không làm cho nội dung của biến được đánh giá là mã shell (mặc dù mã đầu tiên sẽ trải qua quá trình phân tách và tạo khối có thể gây hậu quả nghiêm trọng (ví dụ với var='/*/*/*/*/../../../../*/*/*/*/../../../../*/*/*/*'
). Bạn cần:
eval "echo $var"
# or
sh -c "echo $var"
cho nó được đánh giá / giải thích như mã shell.
awk
không có eval
tính năng như vậy . perl
/ python
làm.
Nhưng hãy cẩn thận của ô nhiễm chéo. Bạn có thể có shell pass dữ liệu biến (trong biến shell ) dưới dạng mã để thực thi bởi awk
:
awk '{print "'"$var"': " $0}'
sẽ nguy hiểm trong trường hợp biến $var
shell chứa ví dụ:
var='test"; print "foo" > /etc/passwd; print "blah'
bởi vì shell sẽ thực thi:
["awk", "{print \"test\"; print \"foo\" > /etc/passwd; print \"blah: \" $0}"]
Hoặc đường vòng khác:
awk '{system("echo foo: " $0)}' < file
nơi awk
sẽ chạy một vỏ như:
["sh", "-c", "echo foo: content-of-the-line"]
cho mỗi dòng file
(và nghĩ về những gì một dòng ; rm -rf /
sẽ làm).
Nó không chỉ giữa awk
và sh
. Bạn phải cẩn thận bất cứ khi nào dữ liệu biến / không kiểm soát có thể được đánh giá là mã bởi một thông dịch viên khác. Ví dụ là:
sed "s/$regexp/blah/g"
sed
Ngôn ngữ của nó bị hạn chế nhưng nó vẫn có thể gây hại, như với regexp='//;w /etc/passwd; s/
'.
Hoặc là:
find . -exec sh -c "echo {}" \;
Bây giờ, để tránh những vấn đề đó, có hai cách tiếp cận chung:
chuyển đổi biến từ một thông dịch viên khác. Nó hoạt động cho shell -> awk hoặc find -> sh case ở trên. Thích thay đổi:
awk '{print "'"$var"': " $0}'
đến:
awk -v awk_var="$var" '{print awk_var ": " $0}'
Và:
find . -exec sh -c "echo {}" \;
đến:
find . -exec sh -c 'echo "$1"' sh {} \;
nhưng điều đó sẽ không làm việc cho vỏ -> sed, hoặc awk -> vỏ.
khi 1 không thể, bạn cần vệ sinh các biến để loại bỏ hoặc thoát khỏi các ký tự có thể là một vấn đề. Trong,
awk '{system("echo foo: " $0)}'
bạn cần chuyển đổi $0
thành một chuỗi rõ ràng khi có liên quan đến trình bao. Một tùy chọn là tiền tố mỗi ký tự có dấu gạch chéo ngược nhưng sẽ không hoạt động cho dòng mới (không phải là vấn đề ở đây). Một cách khác là gửi chuỗi trong dấu ngoặc đơn và thoát từng trích dẫn.
awk 'function escape(s) {
gsub(/'\''/,"&\\\\&&",s)
return "'\''" s "'\''"
}
{system("echo foo: " escape($0))}'