foo='foo(){ echo "Inside Function"; }'
bash -c "$foo; foo"
Inside Function
Cuối cùng, một hàm chỉ đơn giản là một chuỗi lệnh tĩnh, được phân tích trước, được lưu trữ trong bộ nhớ của shell. Và do đó, cách thực sự duy nhất để làm điều đó là bằng cách đánh giá các chuỗi dưới dạng mã, đó chính xác là những gì shell thực hiện mỗi khi bạn gọi hàm của mình.
Nó chưa bao giờ rất khác trước đây, và nó vẫn không. Các nhà phát triển thiết lập các phương thức thuận tiện để làm như vậy và họ cố gắng bảo vệ các lỗ hổng bảo mật có thể có, nhưng thực tế của vấn đề là một thứ càng trở nên thuận tiện, bạn càng biết ít về nó hơn bạn nên làm.
Có rất nhiều tùy chọn có sẵn cho bạn khi nhập dữ liệu từ shell cha vào subshells. Làm quen với chúng, trở nên đủ tự tin để bạn có thể xác định những công dụng tiềm năng và những điểm yếu tiềm ẩn của chúng, và sử dụng chúng một cách tiết kiệm nhưng hiệu quả.
Tôi cho rằng tôi có thể giải thích một hoặc hai trong số này trong khi tôi đang ở đó. Có lẽ cách tốt nhất để truyền các lệnh tĩnh từ shell này sang shell khác (cho dù đó là lên hay xuống chuỗi lệnh subshelling) là alias
. alias
là hữu ích trong khả năng này vì nó được chỉ định POSIX để báo cáo các lệnh được xác định của nó một cách an toàn :
Định dạng để hiển thị bí danh (khi không có toán hạng hoặc chỉ các toán hạng tên được chỉ định) sẽ là:
Chuỗi giá trị phải được viết với trích dẫn thích hợp để nó phù hợp cho việc cung cấp lại cho trình bao. Xem mô tả trích dẫn shell trong Trích dẫn .
Ví dụ:
alias foo="foo(){ echo 'Inside Function'; }"
alias foo
foo='foo(){ echo '\''Inside Function'\''; }'
Xem những gì nó làm với dấu ngoặc kép, có? Đầu ra chính xác phụ thuộc vào shell, nhưng, nói chung, bạn có thể dựa vào shell báo cáo các alias
định nghĩa của nó theo cách eval
an toàn để cung cấp lại cho cùng một shell. Tuy nhiên, việc trộn và kết hợp các vỏ nguồn / đích có thể là iffy trong một số trường hợp.
Để chứng minh một nguyên nhân để thực hiện thận trọng như vậy:
ksh -c 'alias foo="
foo(){
echo \"Inside Function\"
}"
alias foo'
foo=$'\nfoo(){\n\techo "Inside Function"\n}'
Cũng lưu ý rằng alias
không gian tên đại diện cho một trong ba không gian tên độc lập mà bạn thường có thể mong đợi để tìm thấy hoàn toàn xác thực trong bất kỳ lớp vỏ hiện đại nào. Các shell thường cung cấp cho bạn ba không gian tên này:
Các biến: name=value
ngoài danh sách
- Đây cũng có thể được xác định với một số lệnh nội trú như
export
, set
, readonly
trong một số trường hợp.
Chức năng: name() compound_command <>any_redirects
ở vị trí lệnh
- Shell được chỉ định không mở rộng hoặc giải thích bất kỳ phần nào của định nghĩa hàm mà nó có thể tìm thấy trong lệnh tại thời điểm định nghĩa.
- Lệnh cấm này không bao gồm các bí danh, tuy nhiên, vì chúng được mở rộng trong quá trình phân tích cú pháp đọc lệnh của shell, và do đó
alias
, các giá trị, nếu alias
được tìm thấy trong ngữ cảnh chính xác, sẽ được mở rộng thành lệnh định nghĩa hàm khi tìm thấy.
- Như đã đề cập, shell lưu giá trị được phân tích cú pháp của lệnh định nghĩa dưới dạng một chuỗi tĩnh trong bộ nhớ của nó và nó thực hiện tất cả các mở rộng và chuyển hướng được tìm thấy trong chuỗi chỉ khi tên hàm sau đó được tìm thấy sau khi phân tích cú pháp.
Bí danh: alias name=value
ở vị trí chỉ huy
- Giống như các hàm, các
alias
định nghĩa được diễn giải khi các tên định nghĩa được tìm thấy sau đó ở vị trí lệnh.
- Tuy nhiên, không giống như các hàm, vì định nghĩa của chúng là đối số của
alias
lệnh, các mở rộng shell hợp lệ được tìm thấy bên trong cũng được diễn giải theo thời gian xác định. Và do đó, alias
định nghĩa luôn luôn được giải thích hai lần.
- Cũng không giống như các hàm, các
alias
định nghĩa hoàn toàn không được phân tích cú pháp, nhưng, đúng hơn, đó là trình phân tích cú pháp của shell mở rộng các giá trị của chúng trước khi phân tích thành một chuỗi lệnh khi chúng được tìm thấy.
Bạn có thể nhận thấy những gì tôi coi là sự khác biệt chính giữa một alias
hoặc một chức năng ở trên, đó là điểm mở rộng của trình bao cho mỗi. Sự khác biệt, thực sự, có thể làm cho sự kết hợp của họ khá hữu ích.
alias foo='
foo(){
printf "Inside Function: %s\n" "$@"
unset -f foo
}; foo "$@" '
Đó là một bí danh sẽ xác định một chức năng tự hủy cài đặt khi được gọi và do đó, nó ảnh hưởng đến một không gian tên bằng ứng dụng của một không gian tên khác. Sau đó, nó gọi hàm. Ví dụ là ngây thơ; nó không hữu ích lắm trong ngữ cảnh thông thường, nhưng ví dụ sau có thể chứng minh một số tính hữu dụng hạn chế khác mà nó có thể cung cấp trong các bối cảnh khác:
sh -c "
alias $(alias foo)
foo \'
" -- \; \' \" \\
Inside Function: ;
Inside Function: '
Inside Function: "
Inside Function: \
Inside Function: '
Bạn phải luôn luôn gọi một alias
tên trên một dòng đầu vào khác với tên mà định nghĩa được tìm thấy - một lần nữa, điều này là để thực hiện theo thứ tự phân tích cú pháp của lệnh. Khi được kết hợp, các bí danh và chức năng có thể được sử dụng cùng nhau để di chuyển một cách hiệu quả, an toàn và hiệu quả các mảng thông tin và tham số trong và ngoài các bối cảnh được chia nhỏ.
Hầu như không liên quan, nhưng đây là một niềm vui khác:
alias mlinesh='"${SHELL:-sh}" <<"" '
Chạy nó tại một dấu nhắc tương tác và bất kỳ đối số nào bạn chuyển đến nó trên cùng một dòng thực thi như dòng mà bạn gọi nó sẽ được hiểu là đối số cho một lớp con. Tuy nhiên, tất cả các dòng sau đó cho đến khi dòng trống xuất hiện đầu tiên, được đọc bởi lớp vỏ hiện tại để chuyển dưới dạng stdin sang subshell mà nó chuẩn bị gọi, và không ai trong số chúng được hiểu theo bất kỳ cách nào cả.
$ mlinesh -c '. /dev/fd/0; foo'
> foo(){ echo 'Inside Function'; }
>
Inside Function
...nhưng chỉ...
$ mlinesh
> foo(){ echo 'Inside Function'; }
> foo
>
... dễ dàng hơn ...
foo() { echo "Inside function"; }; foo
vậy?