Vượt qua một khối mã như một anon. chức năng


9

Có thể coi một khối lệnh là một hàm ẩn danh?

function wrap_this {
   run_something
   # Decide to run block or maybe not.
   run_something else
}

wrap_this {
   do_something
   do_somthing else
}

# Do something else

wrap_this {
   do_something_else_else
   do_something_else_else_else
}

(Tôi nhận thấy bạn tạo một chức năng hoặc tệp cho mỗi khối, nhưng tôi thấy tùy chọn này rõ ràng hơn và dễ đọc hơn trong các tình huống nhất định.)

whilelàm nó với do/donefunctionlàm nó với { multiple lines }. Tôi nhận ra BASH không có chức năng ẩn danh, nhưng có thể chuyển nhiều lệnh sang chức năng khác, giống như bạn có thể làm khi xác định chức năng hay whilekhông?


Bạn có nghĩa là bạn muốn trang trí (theo cách nói của Python) - tức là trả về một hàm từ một hàm? Ví dụ của bạn, về mặt cú pháp, thậm chí không phải là BASH: đây có phải là hàm hay là hàm gọi không?
Mel Boyce

Nó không rõ ràng với tôi những gì bạn muốn làm. Như Mel đã chỉ ra, những gì bạn đã viết thậm chí có giá trị về mặt cú pháp, nhưng tôi không rõ những gì bạn đã viết liên quan đến các chức năng ẩn danh.
Chris Xuống

Câu trả lời:


2

Đây là giải pháp ngắn nhất mà tôi có thể nghĩ ra:

Cho các chức năng sau:

# List processing
map() { while IFS='' read -r x; do "$@" "$x"; done; }
filter() { while IFS='' read -r x; do "$@" "$x" >&2 && echo "$x"; done; }
foldr() { local f="$1"; local result="$2"; shift 2;  while IFS='' read -r x; do result="$( "$f" "$@" "$x" "$result" )"; done; echo "$result"; }
foldl() { local f="$1"; local result="$2"; shift 2;  while IFS='' read -r x; do result="$( "$f" "$@" "$result" "$x" )"; done; echo "$result"; }

# Helpers
re() { [[ "$2" =~ $1 ]]; }

Ví dụ:

# Example helpers
toLower() { tr '[:upper:]' '[:lower:]'; }
showStructure() { [[ "$1" == "--curly" ]] && echo "{$2; $3}" || echo "($1, $2)"; }

# All lib* directories, ignoring case, using regex
ls /usr | map toLower | filter re 'lib.*'

# All block devices. (Using test, for lack of a full bash [[ … ]].)
cd /dev; ls | filter test -b

# Show difference between foldr and foldl
$ ls / | foldr showStructure '()'
(var/, (usr/, (tmp/, (sys/, (sbin/, (run/, (root/, (proc/, (opt/, (mnt/, (media/, (lost+found/, (lib64/, (lib32/, (lib@, (home/, (etc/, (dev/, (daten/, (boot/, (bin/, ())))))))))))))))))))))
$ ls / | foldr showStructure '{}' --curly
{var/; {usr/; {tmp/; {sys/; {sbin/; {run/; {root/; {proc/; {opt/; {mnt/; {media/; {lost+found/; {lib64/; {lib32/; {lib@; {home/; {etc/; {dev/; {daten/; {boot/; {bin/; {}}}}}}}}}}}}}}}}}}}}}}

(Những ví dụ này tất nhiên chỉ là ví dụ sử dụng và trong thực tế, phong cách này sẽ chỉ có ý nghĩa đối với các trường hợp sử dụng phức tạp hơn.)

Nói chung, kiểu sau luôn có thể được sử dụng:

f() { something "$@"       ; }; someList    | map    f
g() { something "$1" "$2" …; }; someCommand | filter g
                                               

Nó không hoàn toàn lambda, nhưng nó rất gần. Chỉ có một vài ký tự thừa.

Nhưng bất kỳ chữ viết tắt tiện lợi nào sau đây không hoạt động như tôi có thể nói:

λ() { [[ $@ ]]; } # fails on spaces
λ() { [[ "${@:-1}" ${@:1:-1} ]]; } # syntax error
alias λ=test # somehow ignored

Thật không may, bashkhông phù hợp lắm cho phong cách này, mặc dù một số tính năng của nó có phong cách rất chức năng.


Tính năng của bash có phong cách chức năng? Điều duy nhất có chức năng từ xa là bash (gần như bất kỳ ngôn ngữ shell nào) hỗ trợ một dạng thành phần chức năng bằng cách đưa đầu ra của một lệnh vào đầu vào của lệnh khác. Đó là một tính năng của thiết kế chung của Unix, không phải là bash per se.
kyrill

4

Tôi đã quản lý để làm những gì bạn muốn với một evalhack. Với điều này tôi đã cảnh báo rằng eval là không an toàn và bạn nên tránh bằng mọi giá . Như đã nói, nếu bạn tin rằng mã của bạn sẽ không bị lạm dụng, bạn có thể sử dụng điều này:

wrap_this(){
    run_something
    eval "$(cat /dev/stdin)"
    run_something_else
}

Điều này cho phép bạn chạy mã như sau:

wrap_this << EOF
    my function
EOF

Không chính xác lý tưởng, vì khối bên trong là một chuỗi, nhưng không tạo ra tái sử dụng.


Điều này thật tuyệt! Tôi sẽ chỉ thêm các trích dẫn xung quanh đầu tiên !!để ngăn chặn sự thay thế trong heredoc. stackoverflow.com/questions/27920806/ từ
Saintali

3

Bạn có thể đặt mã trong một chuỗi và vượt qua nó để evalhoặc shhoặc đơn giản là suy nó.

perform () {
  "$@"
}

perform echo "moo"

Bạn có thể nhanh chóng kết thúc trong rắc rối trích dẫn sâu, mặc dù.


1
Thật ra, tôi đang tìm kiếm perform { echo "moo" \n echo "moo moo" \n echo "moo moo moo" }. Tôi đã biết bạn có thể vượt qua một lệnh. Nhưng, tôi đang tìm kiếm nhiều dòng, không chỉ một lệnh hay một dòng. Cảm ơn dù đã cố gắng.
dgo.a

3

Không, bash không có chức năng ẩn danh. Tuy nhiên, có thể truyền tên hàm và đối số dưới dạng chuỗi và có bash gọi nó.

function wrap() {
    do_before
    "$@"
    do_after
}

wrap do_something with_arguments

Điều này là tuy nhiên, hơi hạn chế. Đối phó với trích dẫn có thể trở thành một vấn đề. Truyền nhiều hơn một lệnh cũng là một biến chứng.


1
Không phải là một nightname, tất cả các bạn phải làm là thay đổi $*để"$@"
glenn Jackman

Các cơ quan chức năng đa dòng nên hoạt động tốt với phương pháp này.

Glenn, tôi quên mất hình thức đó, cảm ơn. Nó không hoàn toàn giải quyết vấn đề trích dẫn mặc dù. Evan, một lệnh duy nhất được bọc trên nhiều dòng sẽ hoạt động tốt, tôi có nghĩa là có nhiều hơn một lệnh trong cơ thể theo các ví dụ trong câu hỏi. Tôi sẽ cập nhật câu trả lời của tôi để giải quyết cả hai ý kiến.
David Baggerman

Biểu mẫu này hoạt động rất tốt nếu bạn chú ý xác định các hàm lấy các đối số đơn giản và sau đó chuyển các cuộc gọi đến các hàm đó làm đối số. Tức wrap my_func "$arg1" "$arg2". Một điểm yếu của tôi với ví dụ này là giá trị trả về của "$ @" bị mất, nhưng điều đó dễ dàng được khắc phục. Có thể nên bọc "$ @" ()để đảm bảo các thay đổi môi trường không bị rò rỉ, với một số chi phí hiệu suất.
Michael Mol
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.