Menjalankan `exec` dengan Bash built-in

9

Saya mendefinisikan fungsi shell (sebut saja clock), yang ingin saya gunakan sebagai pembungkus untuk perintah lain, mirip dengan timefungsi, misalnya clock ls -R.

Fungsi shell saya melakukan beberapa tugas dan kemudian diakhiri dengan exec "$@".

Saya ingin fungsi ini berfungsi bahkan dengan built-in shell, mis. clock time ls -RHarus menampilkan hasil dari timebuilt-in, dan tidak /usr/bin/timedapat dieksekusi. Tapi execselalu berakhir menjalankan perintah saja.

Bagaimana saya bisa membuat fungsi Bash saya berfungsi sebagai pembungkus yang juga menerima shell built-in sebagai argumen?

Sunting : Saya baru tahu bahwa timeitu bukan Bash built-in, tetapi kata khusus yang dicadangkan terkait dengan saluran pipa. Saya masih tertarik dengan solusi untuk built-in walaupun tidak bekerja time, tetapi solusi yang lebih umum akan lebih baik.

anol
sumber
Anda perlu memohon shell secara eksplisit, menggunakan exec bash -c \' "$@" \'. Kecuali jika perintah Anda pada parameter pertama dikenali sebagai skrip shell, maka itu akan ditafsirkan sebagai biner untuk dijalankan secara langsung. Atau, dan lebih sederhana, lewatkan saja execdan panggil "@"dari shell asli Anda.
AFH

Jawaban:

9

Anda mendefinisikan fungsi bash. Jadi, Anda sudah menggunakan bash shell saat menjalankan fungsi itu. Jadi fungsi itu bisa terlihat seperti:

clock(){
  echo "do something"
  $@
}

Fungsi itu dapat dipanggil dengan bash builtin, kata-kata khusus, perintah, fungsi-fungsi tertentu lainnya:

Alias:

$ clock type ls
do something
ls is aliased to `ls --color=auto'

Bash bawaan:

$ clock type type
do something
type is a shell builtin

Fungsi lain:

$ clock clock
do something
do something

Dapat dieksekusi:

$ clock date
do something
Tue Apr 21 14:11:59 CEST 2015
kekacauan
sumber
Dalam hal ini, apakah ada perbedaan antara menjalankan $@dan exec $@, jika saya tahu saya menjalankan perintah yang sebenarnya?
anol
3
Saat Anda menggunakan exec, perintah itu menggantikan shell. Oleh karena itu tidak ada builtin, alias, kata-kata khusus yang dicadangkan, kata-kata yang didefinisikan lagi, karena dieksekusi dieksekusi melalui system call execve(). Syscall itu mengharapkan file yang dapat dieksekusi.
kekacauan
Tetapi dari sudut pandang pengamat eksternal, apakah masih mungkin untuk membedakan mereka, misalnya dengan exec $0ada satu proses, sementara $@masih memiliki dua.
anol
4
Ya, $@memiliki shell yang berjalan sebagai orangtua dan perintah sebagai proses anak. Tetapi ketika Anda ingin menggunakan builtin, alias dan sebagainya, Anda harus mempertahankan shell. Atau mulai yang baru.
kekacauan
4

Satu-satunya cara untuk meluncurkan shell builtin atau kata kunci shell adalah untuk meluncurkan shell baru karena exec "mengganti shell dengan perintah yang diberikan". Anda harus mengganti baris terakhir Anda dengan:

IFS=' '; exec bash -c "$*"

Ini bekerja dengan baik bawaan dan kata-kata yang dipesan; prinsipnya sama.

Anthony Geoghegan
sumber
3

Jika pembungkus perlu memasukkan kode sebelum perintah yang diberikan, alias akan berfungsi karena mereka diperluas pada tahap yang sangat awal:

alias clock="do this; do that;"

Alias ​​hampir secara harfiah dimasukkan sebagai pengganti kata alias, jadi jejaknya ;penting - itu membuatnya clock time fooberkembang menjadi do this; do that; time foo.

Anda dapat menyalahgunakan ini untuk membuat alias ajaib yang bahkan memotong kutipan.


Untuk memasukkan kode setelah perintah, Anda bisa menggunakan kait "DEBUG".

shopt -s extdebug
trap "<code>" DEBUG

Secara khusus:

shopt -s extdebug
trap 'if [[ $BASH_COMMAND == "clock "* ]]; then
          eval "${BASH_COMMAND#clock }"; echo "Whee!"; false
      fi' DEBUG

Hook masih berjalan sebelum perintah, tetapi ketika ia kembali falseitu memberitahu bash untuk membatalkan eksekusi (karena hook sudah menjalankannya melalui eval).

Sebagai contoh lain, Anda dapat menggunakan ini untuk alias command pleaseke sudo command:

trap 'case $BASH_COMMAND in *\ please) eval sudo ${BASH_COMMAND% *}; false; esac' DEBUG
pengguna1686
sumber
1

Satu-satunya solusi yang saya dapat sampai sejauh ini adalah melakukan analisis kasus untuk membedakan apakah argumen pertama adalah perintah, bawaan, atau kata kunci, dan gagal dalam kasus terakhir:

#!/bin/bash

case $(type -t "$1") in
  file)
    # do stuff
    exec "$@"
    ;;
  builtin)
    # do stuff
    builtin "$@"
    ;;
  keyword)
    >&2 echo "error: cannot handle keywords: $1"
    exit 1
    ;;
  *)
    >&2 echo "error: unknown type: $1"
    exit 1
    ;;
esac

Namun, itu tidak menangani time, jadi mungkin ada solusi yang lebih baik (dan lebih ringkas).

anol
sumber