題 用內置的Bash運行`exec`


我定義了一個shell函數(讓我們調用它 clock),我想用作另一個命令的包裝器,類似於 time 功能,例如 clock ls -R

我的shell函數執行一些任務,然後結束 exec "$@"

我希望這個功能即使使用shell內置函數也可以工作,例如: G。 clock time ls -R 應輸出結果 time 內置,而不是 /usr/bin/time 可執行文件。但 exec 總是最終運行命令。

如何讓我的Bash函數作為一個包裝器來工作,它也接受shell內置函數作為參數?

編輯:我剛學會了 time 不是Bash內置的,而是一個 特別保留字 與管道有關。我仍然對內置插件的解決方案感興趣,即使它不起作用 time,但更通用的解決方案會更好。


8
2018-04-21 11:17


起源


您需要使用顯式調用shell exec bash -c \' "$@" \'。除非您在第一個參數中的命令被識別為shell腳本,否則它將被解釋為直接運行的二進製文件。或者,更簡單地說,就是錯過了 exec並打電話 "@" 從你的原始shell。 - AFH


答案:


你定義了一個bash函數。因此,在調用該函數時,您已經處於bash shell中。那麼這個功能可以簡單地看起來像:

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

可以使用bash內置函數,特殊保留字,命令和其他已定義函數調用該函數:

別名:

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

一個bash內置:

$ clock type type
do something
type is a shell builtin

另一個功能:

$ clock clock
do something
do something

可執行文件:

$ clock date
do something
Tue Apr 21 14:11:59 CEST 2015

8
2018-04-21 12:12



在這種情況下,運行之間是否有任何區別 $@ 和 exec $@,如果我知道我正在運行一個實際的命令? - anol
當你使用 exec,該命令替換shell。因此,不再有內置,別名,特殊保留字,定義的單詞,因為可執行文件是通過系統調用執行的 execve()。該系統調用期望一個可執行文件。 - chaos
但是從外部觀察者的角度來看,是否仍然可以區分它們,例如同 exec $0 有一個過程,而 $@ 還有兩個。 - anol
是, $@ 將正在運行的shell作為父級,將命令作為子進程。但是當你想使用內置函數,別名等時,你必須保留shell。或者開始一個新的。 - chaos


啟動shell builtin或shell關鍵字的唯一方法是啟動一個新的shell,因為exec“用給定的命令替換shell”。我認為這是一個有趣的問題,但不應該太難解決。然而,獲得正確的引用接管了將近一個小時的反複試驗(Bash很棒,但我不是引用的粉絲)。

你應該用你的最後一行替換:

exec bash -c ""$@""

這適用於內置和保留字;原則是一樣的。


3
2018-04-21 12:41



出於某種原因,當我刷新此頁面時,所有其他答案都沒有顯示出來。該死的!我想要我的午餐時間回來。 - Anthony Geoghegan


如果包裝器需要插入代碼 之前 給定的命令,別名將在很早的階段擴展時起作用:

alias clock="do this; do that;"

別名幾乎是字面插入代替別名字,所以尾隨 ; 很重要 - 這樣做 clock time foo 擴展到 do this; do that; time foo

你可以濫用它來創建 魔術別名 甚至繞過引用。


用於插入代碼  一個命令,你可以使用“DEBUG”鉤子。

shopt -s extdebug
trap "<code>" DEBUG

特別:

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

鉤子仍在運行 之前 命令,但它返回 false 它告訴bash取消執行(因為鉤子已經通過eval運行它)。

作為另一個示例,您可以使用它來別名 command please 至 sudo command

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

2
2018-04-21 12:15





到目前為止,我能想出的唯一解決方案是執行案例分析,以區分第一個參數是命令,內置函數還是關鍵字,並在最後一種情況下失敗:

#!/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

它無法處理 time但是,可能會有更好(更簡潔)的解決方案。


0
2018-04-21 12:11