Bạn có thể làm gì với eval
lệnh? Tại sao nó hữu ích? Đây có phải là một loại chức năng tích hợp trong bash không? Không có man
trang nào cho nó ..
help eval
để lấy trang "người đàn ông" từ vỏ của bạn
Bạn có thể làm gì với eval
lệnh? Tại sao nó hữu ích? Đây có phải là một loại chức năng tích hợp trong bash không? Không có man
trang nào cho nó ..
help eval
để lấy trang "người đàn ông" từ vỏ của bạn
Câu trả lời:
eval
là một phần của POSIX. Đây là một giao diện có thể được tích hợp sẵn.
Nó được mô tả trong "Hướng dẫn lập trình POSIX": http://www.unix.com/man-page/poseix/1poseix/eval/
eval - construct command by concatenating arguments
Nó sẽ lấy một đối số và xây dựng một lệnh của nó, sẽ được thực thi bởi shell. Đây là ví dụ của trang web:
1) foo=10 x=foo
2) y='$'$x
3) echo $y
4) $foo
5) eval y='$'$x
6) echo $y
7) 10
$foo
với giá trị '10'
và $x
với giá trị 'foo'
.$y
, bao gồm các chuỗi '$foo'
. Ký hiệu đô la phải được thoát với '$'
.echo $y
.'$foo'
eval
. Đầu tiên nó sẽ đánh giá $x
chuỗi 'foo'
. Bây giờ chúng tôi có tuyên bố y=$foo
sẽ được đánh giá y=10
.echo $y
bây giờ là giá trị '10'
.Đây là một chức năng phổ biến trong nhiều ngôn ngữ, ví dụ Perl và JavaScript. Hãy xem perldoc eval để biết thêm ví dụ: http://perldoc.perl.org/fifts/eval.html
eval
là một tích hợp, không phải là một chức năng. Trong thực tế, các hàm dựng sẵn hoạt động rất giống các hàm không có định nghĩa bằng ngôn ngữ, nhưng không hoàn toàn (sẽ trở nên rõ ràng nếu bạn bị xoắn đủ để xác định hàm được gọi eval
).
Có, eval
là một lệnh nội bộ bash vì vậy nó được mô tả trong bash
trang man.
eval [arg ...]
The args are read and concatenated together into a single com-
mand. This command is then read and executed by the shell, and
its exit status is returned as the value of eval. If there are
no args, or only null arguments, eval returns 0.
Thông thường nó được sử dụng kết hợp với Thay thế lệnh . Không có một tường minh eval
, shell cố gắng thực hiện kết quả của một sự thay thế lệnh, không đánh giá nó.
Nói rằng bạn muốn mã tương đương VAR=value; echo $VAR
. Lưu ý sự khác biệt trong cách vỏ xử lý các bài viết của echo VAR=value
:
andcoz@...:~> $( echo VAR=value )
bash: VAR=value: command not found
andcoz@...:~> echo $VAR
<empty line>
Shell cố gắng thực thi echo
và VAR=value
như hai lệnh riêng biệt. Nó ném một lỗi về chuỗi thứ hai. Việc chuyển nhượng vẫn không hiệu quả.
andcoz@...:~> eval $( echo VAR=value )
andcoz@...:~> echo $VAR
value
Shell hợp nhất (nối) hai chuỗi echo
và VAR=value
, phân tích đơn vị đơn lẻ này theo các quy tắc phù hợp và thực hiện nó.Cuối cùng nhưng không kém phần quan trọng, eval
có thể là một mệnh lệnh rất nguy hiểm. Bất kỳ đầu vào cho một eval
lệnh phải được kiểm tra cẩn thận để tránh các vấn đề bảo mật.
eval
không có trang man vì nó không phải là một lệnh bên ngoài riêng biệt, mà là một shell được tích hợp sẵn, nghĩa là một lệnh bên trong và chỉ được biết bởi shell ( bash
). Phần có liên quan của bash
trang người đàn ông nói:
eval [arg ...]
The args are read and concatenated together into a single command.
This command is then read and executed by the shell, and its exit
status is returned as the value of eval. If there are no args, or only
null arguments, eval returns 0
Ngoài ra, đầu ra nếu help eval
là:
eval: eval [arg ...]
Execute arguments as a shell command.
Combine ARGs into a single string, use the result as input to the shell,
and execute the resulting commands.
Exit Status:
Returns exit status of command or success if command is null.
eval
là một lệnh mạnh mẽ và nếu bạn có ý định sử dụng nó, bạn nên hết sức cẩn thận để tránh các rủi ro bảo mật có thể xảy ra với nó.
Câu lệnh eval nói với shell để lấy các đối số của eval làm lệnh và chạy chúng thông qua dòng lệnh. Nó rất hữu ích trong một tình huống như dưới đây:
Trong tập lệnh của bạn nếu bạn đang xác định một lệnh thành một biến và sau này bạn muốn sử dụng lệnh đó thì bạn nên sử dụng eval:
/home/user1 > a="ls | more"
/home/user1 > $a
bash: command not found: ls | more
/home/user1 > # Above command didn't work as ls tried to list file with name pipe (|) and more. But these files are not there
/home/user1 > eval $a
file.txt
mailids
remote_cmd.sh
sample.txt
tmp
/home/user1 >
ls | more
. Nói cách khác: Tên lệnh duy nhất bao gồm chín ký tự, bao gồm khoảng trắng và ký hiệu ống dẫn .
eval là một lệnh shell thường được thực hiện dưới dạng dựng sẵn.
Trong POSIX, nó được liệt kê như một phần của "2.14. Tiện ích tích hợp đặc biệt" tại mục "eval" .
Nội dung có nghĩa là:
Thuật ngữ "tích hợp" ngụ ý rằng trình bao có thể thực thi trực tiếp tiện ích và không cần tìm kiếm nó.
Nói một cách đơn giản: làm cho một dòng đầu vào được phân tích cú pháp hai lần .
Shell có một chuỗi các bước mà nó tuân theo để "xử lý" một dòng. Bạn có thể nhìn vào hình ảnh này và nhận ra rằng eval là dòng duy nhất đi lên, quay lại bước 1, bên trái. Từ mô tả POSIX :
2.1 Giới thiệu Shell
- Shell đọc đầu vào của nó ....
- Shell phá vỡ đầu vào thành mã thông báo: từ và toán tử
- Shell phân tích cú pháp đầu vào thành các lệnh đơn giản và ghép.
- Shell thực hiện các mở rộng khác nhau (riêng biệt) ...
- Shell thực hiện chuyển hướng và loại bỏ các toán tử chuyển hướng và toán hạng của chúng khỏi danh sách tham số.
- Shell thực thi một chức năng, tập tin tích hợp, tập tin thực thi hoặc tập lệnh ...
- Shell tùy chọn chờ lệnh hoàn thành và thu thập trạng thái thoát.
Ở bước 6, một tích hợp sẽ được thực thi.
Ở bước 6 eval làm cho dòng được xử lý được gửi trở lại bước 1.
Đây là điều kiện duy nhất theo đó trình tự thực hiện quay trở lại.
Đó là lý do tại sao tôi nói: Với eval, một dòng đầu vào được phân tích cú pháp hai lần .
Và hiệu quả quan trọng nhất để hiểu. Có phải đó là một hệ quả của lần đầu tiên một dòng chịu bảy bước vỏ được trình bày ở trên, được trích dẫn . Bên trong bước 4 (bản mở rộng), cũng có một chuỗi các bước để thực hiện tất cả các bản mở rộng , phần cuối cùng là Trích dẫn loại bỏ :
Trích dẫn loại bỏ sẽ luôn luôn được thực hiện cuối cùng.
Vì vậy, luôn luôn, có một mức trích dẫn được loại bỏ.
Do hậu quả của hiệu ứng đầu tiên đó, các phần bổ sung / khác nhau của dòng trở nên tiếp xúc với phân tích cú pháp shell và tất cả các bước khác.
Điều đó cho phép thực hiện mở rộng gián tiếp:
a=b b=c ; eval echo \$$a ### shall produce "c"
Tại sao? Bởi vì trên vòng lặp đầu tiên, đầu tiên $
được trích dẫn.
Như vậy, nó được bỏ qua cho việc mở rộng bởi vỏ.
Cái tiếp theo $
với tên a được mở rộng để tạo ra "b".
Sau đó, một mức trích dẫn được loại bỏ, làm cho lần đầu tiên không $
được trích dẫn.
Kết thúc vòng lặp đầu tiên.
Sau đó, trên vòng lặp thứ hai, chuỗi $b
được đọc bởi trình bao.
Sau đó mở rộng thành "c"
và đưa ra làm đối số cho echo
.
Để "xem" những gì eval sẽ tạo ra trên vòng lặp đầu tiên (sẽ được đánh giá lại), hãy sử dụng echo. Hoặc bất kỳ lệnh / script / chương trình nào hiển thị rõ ràng các đối số:
$ a=b b=c
$ eval echo \$$a;
c
Thay thế eval bằng echo để "xem" những gì đang xảy ra:
$ echo echo \$$a
echo $b
Cũng có thể hiển thị tất cả "phần" của một dòng với:
$ printf '<%s> ' echo \$$a
<echo> <$b>
Trong ví dụ này, chỉ có một tiếng vang và một biến, nhưng hãy nhớ nó để giúp đánh giá các trường hợp phức tạp hơn.
Phải nói rằng: có một lỗi trong đoạn mã trên, bạn có thể thấy nó không?.
Dễ dàng: có một số trích dẫn bị thiếu.
Làm sao? bạn có thể yêu cầu. Đơn giản, hãy thay đổi các biến (không phải mã):
$ a=b b="hi jk"
$ eval echo \$$a
hi jk
Thấy chỗ trống?
Đó là bởi vì giá trị bên trong $b
đã bị chia tách bởi vỏ.
Nếu điều đó không thuyết phục bạn, hãy thử điều này:
$ a=b b="hi * jk"
$ eval echo \$$a ### warning this will expand to the list
### of all files in the present directory.
Thiếu dấu ngoặc kép. Để làm cho nó hoạt động chính xác (thêm dấu ngoặc kép "$a"
bên trong và bên ngoài \"
).
Hãy thử điều này (hoàn toàn an toàn):
$ a=b b="hi * jk"
$ eval echo \" \$"$a" \"
hi * jk
Không có trang nào cho nó ..
Không, không có một trang người đàn ông độc lập cho việc này. Việc tìm kiếm hướng dẫn sử dụng có man -f eval
hoặc thậm chí apropos eval
không hiển thị mục nhập.
Nó được bao gồm bên trong man bash
. Như là bất kỳ tích hợp.
Tìm kiếm "SHELL BUILTIN THÔNG TIN" và sau đó cho "eval".
Một cách dễ dàng hơn để nhận trợ giúp là: Trong bash, bạn có thể làm help eval
để xem trợ giúp cho tích hợp.
Bởi vì nó là văn bản ràng buộc để mã động.
Nói cách khác: nó chuyển đổi danh sách các đối số của nó (và / hoặc mở rộng các đối số đó) thành một dòng thực thi. Nếu vì bất kỳ lý do nào, một đối số đã được đặt bởi kẻ tấn công, bạn sẽ thực thi mã kẻ tấn công.
Hoặc thậm chí đơn giản hơn, với eval bạn đang nói với bất kỳ ai xác định giá trị của một hoặc một số đối số:
Thôi nào, ngồi đây và gõ bất kỳ dòng lệnh nào, tôi sẽ thực thi nó với quyền hạn của mình.
Điều đó có nguy hiểm không? Nên rõ ràng cho mọi người rằng nó là.
Quy tắc an toàn cho eval nên là:
Chỉ thực hiện eval trên các biến mà bạn đã đưa ra giá trị của nó.
Đọc thêm chi tiết tại đây .
eval
là một tính năng của ngôn ngữ giải thích nhất ( TCL
, python
, ruby
...), không chỉ vỏ. Nó được sử dụng để đánh giá mã động.
Trong shell, nó được triển khai như một lệnh dựng sẵn shell.
Về cơ bản, eval
lấy một chuỗi làm đối số và đánh giá / giải thích mã trong đó. Trong shell, eval
có thể lấy nhiều hơn một đối số, nhưng eval
chỉ cần ghép các đối số đó để tạo thành chuỗi để đánh giá.
Nó rất mạnh bởi vì bạn có thể xây dựng mã một cách linh hoạt và chạy nó, điều mà bạn không thể làm trong các ngôn ngữ được biên dịch như C.
Như:
varname=$1 varvalue=$2
eval "$varname=\$varvalue" # evaluate a string like "foo=$varvalue"
# which in Bourne-like shell language
# is a variable assignment.
Nhưng nó cũng nguy hiểm vì điều quan trọng là phải vệ sinh các phần động (được cung cấp bên ngoài) của những gì được truyền cho eval
vì lý do chính nó được hiểu là mã vỏ.
Ví dụ, ở trên nếu $1
là evil-command; var
, eval
cuối cùng sẽ đánh giá evil-command; var=$varvalue
mã shell và sau đó chạy nó evil-command
.
Sự xấu xa của eval
thường được phóng đại quá mức.
OK, nó nguy hiểm, nhưng ít nhất chúng ta biết nó nguy hiểm.
Rất nhiều lệnh khác sẽ đánh giá mã shell trong đối số của nó nếu không vệ sinh, giống như (tùy thuộc vào vỏ), [
aka test
, export
, printf
, GNU sed
, awk
và dĩ nhiên sh
/ bash
/ perl
và tất cả các thông dịch viên ...
Ví dụ (ở đây sử dụng uname
như evil-command
và $a
như bên ngoài cung cấp dữ liệu unsanitized):
$ a='$(uname>&2)' sh -c 'eval "echo $a"'
Linux
$ a='x[0$(uname>&2)]' mksh -c 'export "$a=$b"'
Linux
$ a='x[0$(uname>&2)]' ksh93 -c 'printf "%d\n" "$a"'
Linux
0
$ a='x[0$(uname>&2)]' ksh93 -c '[ "$a" -gt 0 ]'
Linux
$ a=$'bar/g;e uname>&2\n;s//'; echo foo | sed "s/foo/$a/g"
Linux
bar
$ a='";system("uname");"'; awk "BEGIN{print \"$a\"}"
Linux
$ a=';uname'; sh -c "echo $a"
Linux
Những sed
, export
... lệnh có thể được coi là nguy hiểm hơn vì trong khi nó rõ ràng eval "$var"
sẽ làm cho nội dung của $var
được đánh giá là mã shell, nó không phải là quá rõ ràng với sed "s/foo/$var/"
hoặc export "$var=value"
hoặc [ "$var" -gt 0 ]
. Sự nguy hiểm là như nhau, nhưng nó được che giấu trong các lệnh khác.
sed
giống như eval
đang được truyền một chuỗi chứa trong một biến và đối với cả hai, nội dung của chuỗi đó cuối cùng được đánh giá là mã shell, vì vậy sed
cũng nguy hiểm như eval
những gì tôi đang nói. sed
đang được thông qua một chuỗi chứa uname
(uname chưa được thực thi cho đến nay) và thông qua việc gọi sed
, lệnh uname kết thúc được thực thi. Thích cho eval. Trong sed 's/foo/$a/g'
, bạn không chuyển dữ liệu không được xác nhận đến sed
, đó không phải là những gì chúng ta đang nói ở đây.
Ví dụ này có thể làm sáng tỏ:
#!/bin/bash
VAR1=25
VAR2='$VAR1'
VAR3='$VAR2'
echo "$VAR3"
eval echo "$VAR3"
eval eval echo "$VAR3"
Đầu ra của đoạn script trên:
$VAR2
$VAR1
25
eval
. Bạn có tin rằng có một số khía cạnh cơ bản, quan trọng của chức năng eval
không được đề cập trong các câu trả lời hiện có không? Nếu vậy, sau đó giải thích nó, và sử dụng ví dụ để minh họa giải thích của bạn. Xin vui lòng không trả lời trong các ý kiến; chỉnh sửa câu trả lời của bạn để làm cho nó rõ ràng và đầy đủ hơn.
type command
để tìm hiểu loại lệnh là gì. (type eval
trong trường hợp này)