2015-11-24 42 views
5

我有一個Bash腳本來源。當這個腳本來源時,它在Bash腳本中運行一個函數。如果某個條件匹配,該函數應該終止腳本。如何在不終止腳本所在的shell的情況下完成此任務?如何擺脫源Bash腳本的功能

要清楚:我希望終止操作由源腳本中的函數完成,而不是在源腳本的主體中完成。我可以看到的問題是return只是從函數返回到腳本的主體,而exit 1終止了調用shell。

下面的小例子說明了這個問題:

main(){ 
    echo "starting test of environment..." 
    ensure_environment 
    echo "environment safe -- starting other procedures..." 
} 

ensure_environment(){ 
    if [ 1 == 1 ]; then 
     echo "environment problemm -- terminating..." 
     # exit 1 # <-- terminates calling shell 
     return # <-- returns only from function, not from sourced script 
    fi 
} 

main 
+1

改爲執行腳本。這樣,'exit'將退出它正在運行的子shell。 – fedorqui

+1

有趣。你可以舉一個小例子來開始,那可以改進。例如。一個終止shell。 –

+0

你問一些不可能的事情。你必須重構你的代碼。 –

回答

2

這是一個配方,你如何用你的方法實現你的目標。我不會爲你寫代碼,只是描述它是如何完成的。

您的目標是通過有效地獲取可能複雜的shell腳本來設置/更改當前bash shell中的環境變量。此腳本的某些組件可能會決定停止執行此源腳本。使這變得複雜的是這個決定不一定是頂級的,但可能位於嵌套函數調用中。 return,然後,沒有幫助,並且exit將終止源shell,這是不希望的。

你的任務是通過你的這種說法變得更加容易:

額外的複雜性,我真的不能包括一個小例子 使得它非常希望集中在 功能的終止程序。

這是你如何做到這一點:

而不是你的採購是決定設置什麼(「realscript.bash‘),則源的另一劇本’ipcscript.bash」的環境中的實際腳本。

ipcscript.bash將設置一些進程間通信。這可能是一些你用exec打開的額外文件描述符的管道,它可能是一個臨時文件,它可能是別的。

ipcscript.bash將作爲子進程啓動realscript.bash。這意味着,realscript.bash首先會影響該子進程實例的環境變化。作爲一個子進程啓動realscript.bash,您可以在任何嵌套級別退出而不終止源代碼的情況下終止執行。

當您決定終止執行時,您的退出調用將在您編寫時集中存儲在從任何級別調用的集中式函數中。在退出之前,現在終止函數需要以適當的格式將當前環境寫入IPC機制。

ipcscript.bash將從IPC機制中讀取環境設置並重現sourcing shell過程中的所有設置。

7

您可以return從採購的shell腳本。 POSIX spec

所以,雖然你可以從函數不return直接得到你想要的,你什麼可以從腳本的主體回報,如果你的函數返回非零(或其他一些價值時約定)。

例如:

$ cat foo.sh 
f() { 
    echo in f "[email protected]" 
} 

e() { 
    return 2 
} 

f 1 
e 
f 2 
if ! e; then 
    return 
fi 
f 3 
$ . foo.sh 
in f 1 
in f 2 
+1

感謝您的解決方案。我知道這種方法。我的目的是找到一種終止源函數的方法,因爲在一個最小的例子中,我不能真正包含的額外複雜性使得將終止過程集中在一個函數中是非常理想的。 – d3pd

+0

@ d3pd你不行。沒有沒有子外殼。這是不可能的。你可以在這個函數中保留你想要的任何和所有的邏輯。你只需要在'if'或'ensure_environment‖中調用函數返回「或類似的東西。 ....您可能*能夠通過檢查'$?'或其他任何變量的'RETURN'陷阱來做些事情。 –

2

如何:通過一個簡單的包裝,這裏的 「ocall」,維護全局狀態,這裏的 「STILL_OK」 叫一切

STILL_OK=true 

ocall() { 
    if $STILL_OK 
    then 
     echo -- "[email protected]" # this is for debugging, you can delete this line 
     if "[email protected]" 
     then 
      true 
     else 
      STILL_OK=false 
     fi 
    fi 
} 

main(){ 
    ocall echo "starting test of environment..." 
    ocall ensure_environment 
    ocall echo "environment safe -- starting other procedures..." 
} 

ensure_environment(){ 
    if [ 1 == 1 ]; then 
     ocall echo "environment problemm -- terminating..." 
     # exit 1 # <-- terminates calling shell 
     return 1 # <-- returns from sourced script but leaves sourcing shell running 
    fi 
} 

ocall main 
+0

爲了讓'$ @'保留它的屬性,它應該基本上總是用雙引號。這裏並不重要,但對我來說,它立即引發了一個新手報警,所以你可能想要解決它,只是出於這個原因。 – tripleee

+0

沒錯。我可以使用紅寶石時避免使用bash。糾正。 –

+0

Nitpicking:'如果$ STILL_OK'是安全弱點。如果[[$ STILL_OK「=」true「]'更好地使用if',即使它不夠優雅。否則,如果您在不設置「STILL_OK」的情況下實現通向此源代碼行的任何路徑,那麼您將執行環境中當前爲「STILL_OK」設置的任何路徑。也許只有一個人不知道。 – Alfe

2

這是不可能。

如果你輸入一個腳本,它就是(對於這裏涉及的方面),就像在調用(sourcing)shell中逐一輸入每一行一樣。你想留下一個不存在的範圍(源腳本),所以它不能被留下。

我能想到的唯一方法是通過將出口希望回調用函數,並檢查它:

main() { 
    echo "starting test of environment..." 
    [ "$(ensure_environment)" = "bailout" ] && return 
    echo "environment safe -- starting other procedures..." 
} 

ensure_environment() { 
    if [ 1 == 1 ]; then 
     echo "bailout" 
     return 
    fi 
} 

main 

什麼你問的也通常是不可能的其他語言。通常情況下,每個函數只能通過返回來終止自己,而不是自身之外更廣泛的定義範圍(如它駐留的腳本)。 An exception to this rule is exception handling使用try/catch或類似的。

另外考慮這個問題:如果你輸入這個腳本,shell函數就會在sourcing shell中知道。所以你可以再打電話給他們。然後再次(沒有)周圍的作用域可能會終止。

0

有時我寫一個腳本,它具有我想在腳本之外使用的方便功能。在這種情況下,如果腳本運行,則它執行它的操作。但是如果腳本來源,它只是將一些函數加載到源代碼shell中。 我使用這種形式:

#!/bin/bash 

# This function will be sourcable 
foo() { 
    echo hello world 
} 

# end if being sourced 
if [[ $0 == bash ]]; then 
    return 
fi 

# the rest of the script goes here