毎回インポートすることなく、このスクリプトファイルの機能をどのようにロードできますか?
foo
実行したいスクリプト機能を含むファイルを作成しました。 PATHにあります/usr/bin
。
文書金持ち:
#!/bin/bash
echo "Running foo"
function x {
echo "x"
}
x
ただし、端末に関数名を入力すると、次のようになります。
x: command not found
入力すると、foo
次のよう
Running foo
に表示されます(したがってファイルはPATHにあり、実行可能です)。
一度入力すると、実行機能をsource foo
入力できますxx
私も知っている、これは非常に基本的な質問です。私はスクリプトを抽象化して管理しやすくしたいと思います(すべてを.profileまたは.bashrcにダンプするのと比較して)。
ベストアンサー1
問題は、.../bin
ディレクトリ内のスクリプトがexec
別のシェル環境で編集されたことです。その環境は実行後も持続しないため、完了時に定義はx() { ... ; }
現在のシェル環境では持続しません。
時. ./somescript.sh
(またはsource
などの一部のシェルで)bash
zsh
現在のシェルはスクリプトを現在の環境に読み込み、プロンプトで発行されたかのように内容を実行します。これはx() { ... ; }
現在、シェル環境である対話型シェルで定義されています。
デフォルトでは、問題は次のように証明できます。
sh <<\HEREDOC_CMD_FILE #runs sh shell with heredoc input file
{ #begins current shell compound expression
( #begins subshell compound expression
x() { echo 'I am function x.' ; } #define function x
x #x invoked in subshell
) #ends subshell compound expression
x #x invoked in current shell
} #ends current shell compound expression
#v_end_v
HEREDOC_CMD_FILE
###OUTPUT###
I am function x.
sh: line 6: x: command not found
同様に( : subshell )
、上記の例で定義された環境は完了後も維持されず、実行するスクリプトで定義された環境も維持されません。同様にsh
、読み込み時にHEREDOC_CMD_FILE
ユーザーと同じ機能を実行し、source ../file
現在のシェル実行環境でコンテンツを実行します。ただし、その環境はそれを実行したプロンプトを発行したシェルのサブシェルサブであるため、その環境では何も実行されません。そこでも。しかし、次のようにすることができます。
. /dev/fd/0 <<\HEREDOC_CMD_FILE && x
x() { echo 'I am function x.' ; }
HEREDOC_CMD_FILE
###OUTPUT###
I am function x.
...これはあなたがやっていることとほぼ同じです。source ${script}
ただし、ここでは.dot
ディスクにあるファイルの内容をインポートするのに対しstdin
、あなたはディスクにあるファイルの内容をインポートします。
ただし、実行スクリプトと実行スクリプトの主な違いは、( : subshell )
実行スクリプトの実行環境がどのように見えるかです。スクリプトを実行すると新しいオペレーティング環境 - したがって、コマンドラインから明示的にエクスポートまたは宣言しない限り、現在シェルで宣言されているすべての変数はその環境に転送されません。この動作は次のように説明できます。
{ x() { printf "$FMT" "$0" "$var2" x ; } #describe env
export FMT='argv0: %s\t\tvar2: %s\t\tI am function %s.\n' \
var1=val1 #var1 and FMT are explicitly exported
var2=shell_val2 #var2 is not
cat >./script && #read out stdin to >./script
chmod +x ./script && #then make ./script executable
var3=val3 ./script #then define var3 for ./script's env and execute
} <<\SCRIPT ; x ; y #send heredoc to block's stdin then call {x,y}()
#!/usr/bin/sh
#read out by cat to >./script then executed in a separate environment
y() { printf "$FMT" "$0" "$var2" y ; } #describe env
echo "${var1:-#var1 is unset or null}" #$var1 if not unset or null else :-this}
echo "${var2:-#var2 is unset or null}"
echo "${var3:-#var3 is unset or null}"
export var2=script_val2
x ; y #run x() ; y() in script
#v_end_v
SCRIPT
###OUTPUT###
val1
#var2 is unset or null
val3
./script: line 8: x: command not found
argv0: ./script var2: script_val2 I am function y.
argv0: sh var2: shell_val2 I am function x.
sh: line 18: y: command not found
この動作は、プロンプトで実行するか、現在のシェルの子として実行するのとは異なります。つまり( : subshells )
、上記のように実行中の子は明示的に編集する必要がありますが、親の環境を自動的に継承するということですexport
。
var1=val1 ; export var2=val2
x() { echo 'I am function x.' ; }
(
printf '%s\n' "$var1" "$var2"
x
)
###OUTPUT###
val1
val2
I am function x.
.dot
最後に注目したいのは、現在のシェルで実行されているように、実行中のスクリプトに関数定義を含むファイルをインポートせず、親シェルで実行されたスクリプトに関数をエクスポートする移植可能な方法がないことです。デフォルトでは、変数を使用しているかのように移植することsource
はできません。しかし、実行中のスクリプトにexport function
あなたがいる可能性があると思います。もちろん、サブ環境(実行しているかどうかにかかわらず)はサブ環境と共に消えます。export fn_def='fn() { : fn body ; }'
eval "$fn_def"
したがって、スクリプトで定義された関数が欲しいがスクリプト自体をインポートしたくない場合は、関数をいくつかのコマンドとその出力に読み込む必要がありますeval
。
eval "$(cat ./script)"
しかし、本質的に同じです. ./script
。それはあまり効率的ではありません。ただ受け取れます。
これを行う最善の方法は、スクリプト自体から関数定義を削除し、独自のファイルに入れてから、必要に応じてスクリプトと現在のシェルの両方からインポートすることです。このように:
{
echo 'x() { echo "I am function x and my argv0 is ${0}." ; }' >./fn_x
echo '. ./fn_x ; x' >./script
chmod +x ./script && ./script
. ./fn_x ; x
}
###OUTPUT###
I am function x and my argv0 is ./script.
I am function x and my argv0 is sh.
対話型シェルで常に使用できるようにするには、. /path/to/fn_x
シェルENV
ファイルに追加します。たとえば、にある関数を含むスクリプトの場合は、次のbash
行を次に追加します。foo
/usr/bin
~/.bashrc
. /usr/bin/foo
起動時に何らかの理由でその場所でスクリプトを使用できない場合、シェルは期待どおりにENV
ファイルの残りの部分を読み取ってインポートしますが、stderr
関数定義ファイルのインポートに問題があることを知らせる診断メッセージを印刷します。