Bashソース - 2つのソースファイルが同じ関数名を持つときに正しい関数を選択しますか?

Bashソース - 2つのソースファイルが同じ関数名を持つときに正しい関数を選択しますか?

file2.sh私のbashスクリプトは、パラメータに基づいてスクリプトファイル(呼び出し)を取得します。 (ソースの有無にかかわらず、どちらかです。)スクリプトには、file2.sh「foo」関数(fooの修正または改良版と呼ばれる)が含まれています。

スクリプトは、file1.sh元の関数"foo"を含む他のファイル()も取得します。これfile1.shは常にソースです(そして必要な他の機能もあります)。

file2.shfile1.sh(ソースの場合)の「foo」をオーバーライドするには、「foo」関数が必要ですfile2.sh

また、基本スクリプトで呼び出されるすべてのスクリプトでこれを行う必要があります。スクリプトファイルと呼ばれるいくつかのソースコードも同様ですfile1.sh。彼らは元の「foo」機能を期待しています。ただし、変更せずに改善された「foo」を呼び出す必要があります。

つまり、メインスクリプトに「foo」が含まれている場合は、代わりに「foo」を使用するfile2.shソーススクリプト(メインスクリプトによって呼び出されます)が必要です。デフォルトスクリプト以外のファイルは変更できません。 (または変更する場合は、基本スクリプトでソースが提供されていないときに正しく機能するために必要です。)file1.shfile2.shfile2.sh

答え1

file2.shでは、機能を「読み取り専用」として表示できます。

注:これにより、file1.shが後で関数を定義(再定義)しようとすると警告が発生します。

これらの警告はstderrに表示され、問題を引き起こす可能性があります。無効にできるかどうかはわかりません。

追加の注:スクリプトが関数定義の戻り状態を確認すると、スクリプトが失敗する可能性があります。スクリプトの実行を中断するためにどこでもゼロ以外の戻り状態を発生させるbashオプションを設定することも可能であると思います。頑張ってください!

file1.shを修正できますか?関数を定義する前に、条件を使用して関数が定義されているかどうかを確認することがより安定した解決策になります。

これは「読み取り専用」の使用例です。

hobbes@metalbaby:~/scratch$ mkdir bash-source-test
hobbes@metalbaby:~/scratch$ cd bash-source-test
hobbes@metalbaby:~/scratch/bash-source-test$ cat > file2.sh
#!/bin/bash
fname(){
  echo "file2"
}

readonly -f fname
hobbes@metalbaby:~/scratch/bash-source-test$ cat > file1.sh
#!/bin/bash
fname(){
  echo "file1"
}

readonly -f fname
hobbes@metalbaby:~/scratch/bash-source-test$ cat >top.sh
#!/bin/bash 
if [ $1 ]; then
source file2.sh
fi  
source file1.sh

fname
hobbes@metalbaby:~/scratch/bash-source-test$ chmod +x *.sh
hobbes@metalbaby:~/scratch/bash-source-test$ ./top.sh 
file1
hobbes@metalbaby:~/scratch/bash-source-test$ ./top.sh hello
file1.sh: line 4: fname: readonly function
file2
hobbes@metalbaby:~/scratch/bash-source-test$ 

答え2

これらのスクリプトが実際に解釈され(シミュレーションモードでbashはない場合)、定義されていますが、そうでない場合shfile1.sh使用 foosourceたとえば、他の項目にエイリアスを割り当てることで、ソースが上書きされないように上書きしてコマンドできます。.file1.shfile1.shfoo

$ cat file1.sh
foo() { echo original foo; }
$ cat file2.sh
foo() { echo new foo; }
$ cat main.sh
file2_sourced=false
source() {
  case ${1##*/} in
    (file2.sh)
      file2_sourced=true;;
    (file1.sh)
      if "$file2_sourced"; then
        alias foo=original_foo

        builtin source "$@"; local "ret=$?"
        unalias foo
        return "$ret"
      fi;;
  esac
  builtin source "$@"
}
.() { source "$@"; }

source file2.sh
source file1.sh

foo
original_foo

$ bash main.sh
new foo
original foo

それとももっと適切かもしれません:

source() {
  shopt -s expand_aliases
  if [ "${1##*/}" = file1.sh ] && command -v foo > /dev/null 2>&1; then
    # foo already defined, make file1.sh define original_foo this time
    alias foo=original_foo
    builtin source "$@"; local "ret=$?"
    unalias foo
    return "$ret"
  else
    builtin source "$@"
  fi
}

つまり、file1.sh予防だけしてください。~について-定義foo

これで呼び出された他のスクリプトがmain.sh次のようになります。処刑されたなる代わりに源泉source、これらの機能と.機能を次にエクスポートする必要がありますmain.sh

export -f source .

実際、それはできますいいえ編集してmain.sh呼び出してください。

env BASHOPTS=expand_aliases source='() {
  if [ "${1##*/}" = file1.sh ] && command -v foo > /dev/null 2>&1; then
    # foo already defined, make file1.sh define original_foo this time
    alias foo=original_foo
    builtin source "$@"; local "ret=$?"
    unalias foo
    return "$ret"
  else
    builtin source "$@"
  fi
}' .='() { source "$@"; }' bash main.sh

答え3

私は基本的なシステム管理と自動化のための小さなbashスクリプト以外に何も書いたことがないので、他のファイルから機能をインポートする経験はほとんどありません。したがって、これは非常に素朴または移植不可能である可能性がありますが、少なくとも私のシステムと私が知っているほとんどのプログラミング言語では、何かをオーバーライドすると再定義されたままになります。

つまり、ソースを確認する限りfile1.sh 今後 file2.shfile2.sh次に、ロードされた場合は常にエクスポートし、file1.shロードされていない場合は常にエクスポートする必要がある関数をエクスポートします。

$ cat file1.sh 
#!/bin/bash
fname(){
  echo "BBBBB"
}

$ cat file2.sh 
#!/bin/bash
fname(){
  echo "CCCCC"
}

$ cat foobar.sh 
#!/bin/bash
source file1.sh
if [ $1 ]; then 
    source file2.sh;
fi
export -f fname;
./run_function.sh

$ cat run_function.sh 
#!/bin/bash
fname

(例:)というスクリプトにはコンテンツが定義され./run_function.shています。fname最後file2.sh、リソースをインポートした場合、またはfile.shまだインポートしていない場合はリソースをロードします。

$ ./foobar.sh
BBBBB
$ ./foobar.sh hello
CCCCC

答え4

ここで最も簡単な答えは、これらのスクリプトファイルから関数をインポートすることです。

#file[12]
#foo() { : removed; }
. ./foo.fn
#and rest of script

#foo.fn
foo() { : old version is always defined; }
[ -z "${NEWFOO:+1}" ] ||
foo() { : new version defined only if $NEWFOO; }

#your caller script
export NEWFOO=1
case $srcarg in (./file1) unset NEWFOO;; esac
#or however ^that part^ is usually done
. "$srcarg"
#and rest of script

関連情報