Melewati blok kode sebagai anon. fungsi

9

Apakah mungkin memperlakukan blok perintah sebagai fungsi anonim?

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
}

(Saya sadar Anda membuat fungsi atau file untuk setiap blok, tetapi saya menemukan opsi ini lebih jelas dan lebih mudah dibaca dalam situasi tertentu.)

whilemelakukannya dengan do/donedan functionmelakukannya dengan { multiple lines }. Saya menyadari BASH tidak memiliki fungsi anonim, tetapi apakah mungkin untuk meneruskan beberapa perintah ke fungsi lain, seperti yang dapat Anda lakukan saat mendefinisikan fungsi atau while?

dgo.a
sumber
Maksud Anda, Anda ingin menghias (dalam bahasa Python) - yaitu, mengembalikan fungsi dari suatu fungsi? Contoh Anda, secara sintaksis, bahkan bukan BASH: apakah wrap_this ini seharusnya fungsi atau panggilan fungsi?
Mel Boyce
Tidak jelas bagi saya apa yang ingin Anda lakukan. Seperti yang ditunjukkan Mel, apa yang Anda tulis bahkan sah secara sintaksis, tetapi tidak jelas bagi saya bagaimana apa yang Anda tulis terkait dengan fungsi anonim.
Chris Down

Jawaban:

2

Ini adalah solusi terpendek yang dapat saya pikirkan:

Diberikan fungsi-fungsi ini:

# 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 ]]; }

Contoh:

# 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/; {}}}}}}}}}}}}}}}}}}}}}}

(Contoh-contoh ini tentu saja hanya contoh penggunaan, dan pada kenyataannya, gaya ini hanya masuk akal untuk kasus penggunaan yang lebih rumit.)

Secara umum, gaya berikut selalu dapat digunakan:

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

Ini tidak cukup lambda, tetapi sangat sangat dekat. Hanya beberapa karakter berlebih.

Tetapi singkatan kenyamanan berikut ini tidak berfungsi sejauh yang saya tahu:

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

Sayangnya, bashtidak terlalu cocok untuk gaya ini, meskipun beberapa fitur-fiturnya memiliki gaya yang sangat fungsional.

Evi1M4chine
sumber
Fitur bash memiliki gaya fungsional? Satu-satunya fungsi dari jarak jauh adalah bahwa bash (seperti bahasa shell lainnya) mendukung bentuk komposisi fungsi dengan menyalurkan output dari satu perintah ke input yang lain. Itu lebih merupakan fitur dari desain umum Unix, bukan bash per se.
kyrill
4

Saya sudah berhasil melakukan apa yang Anda inginkan dengan evalperetasan. Dengan ini saya berbaring memperingatkan bahwa eval tidak aman dan Anda harus menghindari di semua biaya . Karena itu, jika Anda percaya kode Anda tidak akan disalahgunakan, Anda dapat menggunakan ini:

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

Ini memungkinkan Anda untuk menjalankan kode sebagai berikut:

wrap_this << EOF
    my function
EOF

Tidak persis ideal, karena blok bagian dalam adalah string, tetapi tidak membuat penggunaan kembali.

hkupty
sumber
Ini luar biasa! Saya hanya akan menambahkan tanda kutip di sekitar dulu !!untuk mencegah penggantian di heredoc. stackoverflow.com/questions/27920806/…
Saintali
3

Anda dapat menempatkan kode dalam string dan menyebarkannya ke evalatau shatau hanya interpolasi itu.

perform () {
  "$@"
}

perform echo "moo"

Anda dapat dengan cepat berakhir dalam kesulitan mengutip yang dalam.

tripleee
sumber
1
Sebenarnya, saya sedang mencari perform { echo "moo" \n echo "moo moo" \n echo "moo moo moo" }. Saya sudah tahu Anda bisa melewati satu perintah. Tapi, saya mencari beberapa baris, bukan hanya satu perintah atau satu baris. Terima kasih sudah mencoba.
dgo.a
3

Tidak, bash tidak memiliki fungsi anonim. Namun dimungkinkan untuk memberikan nama fungsi dan argumen sebagai string dan bash menyebutnya.

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

wrap do_something with_arguments

Namun ini agak terbatas. Berurusan dengan mengutip dapat menjadi masalah. Melewati lebih dari satu perintah juga merupakan komplikasi.

David Baggerman
sumber
1
Bukan nama malam, yang harus Anda lakukan adalah mengubah $*ke"$@"
glenn jackman
Badan fungsi multi-garis harus bekerja dengan baik dengan metode ini.
Glenn, saya lupa tentang formulir itu, terima kasih. Itu tidak sepenuhnya memecahkan masalah mengutip. Evan, satu perintah yang dibungkus beberapa baris akan bekerja dengan baik, maksud saya memiliki lebih dari satu perintah di tubuh sesuai dengan contoh dalam pertanyaan. Saya akan memperbarui jawaban saya untuk membahas kedua komentar.
David Baggerman
Formulir ini berfungsi dengan sangat baik jika Anda dengan hati-hati mendefinisikan fungsi yang menggunakan argumen sederhana, dan kemudian meneruskan panggilan ke fungsi tersebut sebagai argumen. Yaitu wrap my_func "$arg1" "$arg2". Satu masalah saya dengan contoh ini adalah bahwa nilai kembalinya "$ @" hilang, tapi itu mudah diatasi. Mungkin diinginkan untuk membungkus "$ @" dengan ()untuk memastikan perubahan lingkungan tidak bocor, dengan beberapa biaya kinerja forking.
Michael Mol