4

unsetつまり、シェル関数自体ではないものを使用したいということです。それができれば、command実行してそれが純粋であることを確認できます

#!/bin/sh
{ \unset -f unalias command [; \unalias unset command [ } 2>/dev/null;
# make zsh find *builtins* with `command` too:
[ -n "$ZSH_VERSION" ] && options[POSIX_BUILTINS]=on

\unsetDebian Almquist シェル (dash) を使用している場合、純粋であると信頼できると思います。少なくともunset、 inという名前のシェル関数を定義できませんでしたdash。inbashまたは in でzshを定義できunset() { echo fake unset; }ましたが、その後関数を設定解除できません\unset -f unset。「偽の設定解除」を出力します。

これに関連して、スクリプトでは、スクリプトによって呼び出されるスクリプトで使用できるようにbash、関数をエクスポートできます。ただし、同じことはスクリプトでは機能しません。私が書いているスクリプトファイルの外でコマンドがシェル関数として定義されていることを心配する必要があるのだろうか?他の POSIX 互換シェルはどうですか?export -f <function name>bashdashdash

4

3 に答える 3

12

注: 以下は、特に明記されている場合を除き、すべての主要な POSIX 互換シェルに適用されます: bashdashksh、およびzsh。( dash、Debian Almquist シェルは、shUbuntu などの Debian ベースの Linux ディストリビューションのデフォルト シェル ( ) です)。

  • unset本来の意味を持つこと(オプションでシェル関数-f定義解除できるビルトイン) は、他のシェル キーワード、コマンド、またはビルトインが本来の意味を持つことを保証するための鍵です。

    • 変更されていない から始めて、変更されていないおよび/またはunsetを確保できます。これらを一緒に使用して、シェル キーワード、ビルトイン、および外部ユーティリティを隠す可能性のあるエイリアスまたはシェル関数をバイパスまたは未定義にすることができます。shoptcommand
    • 関数を未定義にする代わりに、環境を通じて、コードの外部で定義されている可能性のあるものを含め、関数をバイパスcommandするために使用できます。関数のエクスポートは、サポートのみであり、これらのメカニズムの1 つにすぎません。異なるシェルには異なるシェルがあり、複数をサポートする場合があります - 以下を参照してください。
      bash
  • 、 、およびPOSIX 互換モードの場合のみdash、再定義されていないことが保証されます。kshbash unset

    • dashあなたが発見したように、という名前の関数を定義することはできず、 as を呼び出すことでエイリアスフォームをバイパスできるため、ksh安全です。unset\unset

    • bashPOSIX互換モードの場合、という名前の関数を定義できますがunset、を呼び出すとそれを無視unsetし、後で発見したように常にビルトインを実行します。

      • POSIX 互換モードが Bash の機能セットを制限し、その動作を変更することを考えると、通常、Bash コードをそこで実行することは望ましくありません。この投稿の最後に、提案された回避策の実装があります。これは、関数が定義されていないことを確認するために一時的に POSIX 互換モードをアクティブにします。unset
  • 悲しいことに、私の知る限りではzsh、またbashのデフォルト モードでも、それ自体が再定義されていないことを保証する方法はなくunset、同様に動作する POSIX ライクなシェルが他にもある可能性があります。

    • \unsetそれを(名前の一部を引用して)呼び出すと、エイリアスの再定義はバイパスされますが、関数の再定義はバイパスされません。元に戻すには、元の自体が必要になりunsetます:catch 22.
  • したがって、実行環境を制御できないため、改ざんの影響を完全に受けないシェル スクリプトは作成できませdashkshbash

    • unset改ざんされていないと仮定する場合、最も堅牢な方法は次のとおりです。

      • とが変更されていない\unset -fことを確認するために使用します (シェル関数によってシャドウされていません: )unaliascommand\unset -f unalias command

        • 関数は、エイリアスとは異なり、名前で明示的に未定義にする必要がありますが、残念ながら、すべてのシェルがすべての定義済み関数を列挙するメカニズムを提供しているわけではありません ( 、、およびでtypeset -f機能しますが、メカニズムがまったくないように見えます )。したがって、すべての関数を未定義にすることは常に可能であるとは限りません。bashkshzshdash
      • \unalias -aすべてのエイリアスを削除するために使用します。

      • 次に、定義した関数を除いて、すべてをで呼び出します。外部ユーティリティを呼び出すときは、可能な場合は明示的なパスを使用し、標準のユーティリティの場合は、標準の場所に限定された最小限の定義を使用する を使用します(実行してその定義を確認します)。command [-p]command -p$PATHcommand -p getconf PATH


追加情報:

  • POSIX に従って、コマンド名の任意の部分 (例: ) を引用すると、エイリアス形式またはキーワード形式 ( POSIX および用語の予約語\unset) はその名前でバイパスされますが、シェル関数はバイパスされませんzsh

  • POSIX ごとに、すべてのエイリアスunalias -aを定義解除します。すべての関数の定義を解除するための同等の POSIX 準拠のコマンドはありません。

    • 警告: 古いzshバージョンはサポートしていません-av5.0.8ただし、少なくとも現在はそうです。
  • Builtinは、 、、および- のcommandキーワード、エイリアス、関数をバイパスするために使用できます。つまり、組み込み外部ユーティリティのみを実行します。対照的に、デフォルトではbuiltinsバイパスします。ビルトインも実行するには、を使用します。 bashdashkshcommandzshzshoptions[POSIX_BUILTINS]=on

  • 以下は、すべてのシェルで名前付きのみの外部ユーティリティを実行するために使用でき ます。<name>
    "$(command which <name>)" ...
    which

  • コマンド形式の優先順位:

    • bash, zsh: エイリアス > シェル キーワード > シェル関数 > 組み込み > 外部ユーティリティ
    • ksh, dash: シェル キーワード > エイリアス > シェル関数 > 組み込み > 外部ユーティリティ
    • つまり、inbashzshエイリアスはシェル キーワードをオーバーライドできますが、inkshとエイリアスはdashできません。
  • bashksh、およびzsh- すべてではありませんが、POSIX 準拠の形式の代わりにdash、非標準の関数シグネチャ を使用できます。function <name> { ...<name>() { ...

    • 構文は、次のfunction前提条件です。
      • 関数が定義される前に<name>、それ自体がエイリアス展開の対象にならないようにします。
      • シェルキーワード<name>でもある aを選択できること。 このような関数は、引用符で囲まれた形式でのみ呼び出すことができることに注意してください。例: 。
        \while
      • ( の場合ksh、構文を使用すると、さらにステートメントがローカル変数を作成functionすることを意味します。)typeset
    • dashksh、およびbash POSIX モードの場合は、特別なビルトイン (例: 、unsetbreakset)の命名関数を追加で防止しshiftます。POSIX 定義の特別なビルトインのリストはここにあります。両方ともdashksh再定義できないものをさらにいくつか追加します (例: localin dash;typesetおよびunaliasin ksh) が、両方のシェルには、再定義できる特別ではない追加のビルトイン(例: ) があります。 上記の場合、構文が使用されているかどうかに関係なく、ルールが適用されることに注意してください。type
      kshfunction
  • コードの範囲内の環境シェル関数の潜在的なソース:

    • 注: これらを防ぐ最も簡単な方法は、ビルトインまたは外部ユーティリティを呼び出したいときはいつでも(変更されていない)commandビルトインを使用することです (ビルトインzshのバイパスも防止するために)。options[POSIX_BUILTINS]=on

    • POSIX では、環境変数の絶対パスで指定されたスクリプトを対話型シェル用ENVソースすることが義務付けられています(いくつかの制限があります -仕様を参照してください)。常にそれを尊重しますが、v4.2+ では;として or として呼び出された場合にのみそうします。対照的に、この変数を尊重することはありません。kshdashbashsh--posixzsh

      • 注:スクリプトとして実行されるコードは、通常、非対話型シェルで実行されますが、それは保証されていません。たとえば、インタラクティブスクリプトからコードを取得したり、誰かがスクリプトを呼び出してsh -iインタラクティブ インスタンスを強制したりすることができます。
    • bash2 つのメカニズムがあります。

      • orを使用して個々の関数をエクスポートする(他のシェルは変数のエクスポートのみをサポートします)export -fdeclare -fx
      • 非対話型シェルがオプションの環境変数で開始されるたびに、source へのスクリプトのフル パスを指定しますBASH_ENV
    • kshオプションの環境変数を介した関数の自動ロードをサポートします。FPATH指定されたディレクトリにある関数定義を含むファイルFPATHは、暗黙的かつ自動的にロードされます。

      • (もzshサポートしていFPATHますが、自動ロード関数には明示的な autoload <name>ステートメントが必要です。そのため、指定された名前で関数を自動ロードするように特に要求しない限り、関数はシェルに追加されません。)
    • zshは、その初期化ファイルを介して、任意の zshインスタンス (インタラクティブかどうかに関係なく)のソース スクリプトをサポートします。/etc/zshenv~/.zhsenv

    • (環境を介して関数を定義するためのメカニズムdashをサポートしていないようです。)


の回避策:が元の意味を持っているbashことを確認します。unset

この回避策は、 がスクリプトを実行することがわかっている場合にのみ安全ですがbash、残念ながらそれ自体は保証できません。

また、シェル環境を変更する (エイリアスと関数の削除) ため、ソース化するように設計されたスクリプトには適していません。

前述のように、通常、コードを Bash の POSIX 互換モードで実行することは望ましくありませんが、関数によってシャドウされないようにするために一時的に有効にすることができます。unset

#!/bin/bash

# *Temporarily* force Bash into POSIX compatibility mode, where `unset` cannot 
# be shadowed, which allows us to undefine any `unset` *function* as well
# as other functions that may shadow crucial commands.
# Note: Fortunately, POSIXLY_CORRECT= works even without `export`, because
#       use of `export` is not safe at this point.
#       By contrast, a simple assignment cannot be tampered with.
POSIXLY_CORRECT=

# If defined, unset unset() and other functions that may shadow crucial commands.
# Note the \ prefix to ensure that aliases are bypassed.
\unset -f unset unalias read declare

# Remove all aliases.
# (Note that while alias expansion is off by default in scripts, it may
#  have been turned on explicitly in a tampered-with environment.)
\unalias -a  # Note: After this, \ to bypass aliases is no longer needed.

# Now it is safe to turn POSIX mode back off, so as to reenable all Bash
# features.
unset POSIXLY_CORRECT

# Now UNDEFINE ALL REMAINING FUNCTIONS:
# Note that we do this AFTER switching back from POSIX mode, because
# Bash in its default mode allows defining functions with nonstandard names
# such as `[` or `z?`, and such functions can also only be *unset* while
# in default mode.
# Also note that we needn't worry about keywords `while`, `do` and `done`
# being shadowed by functions, because the only way to invoke such functions
# (which you can only define with the nonstandard `function` keyword) would
# be with `\` (e.g., `\while`).
while read _ _ n; do unset -f "$n"; done < <(declare -F)

# IN THE REST OF THE SCRIPT:
#  - It is now safe to call *builtins* as-is.
#  - *External utilities* should be invoked:
#      - by full path, if feasible
#      - and/or, in the case of *standard utilities*, with
#        command -p, which uses a minimal $PATH definition that only
#        comprises the locations of standard utilities.
#      - alternatively, as @jarno suggests, you can redefine your $PATH
#        to contain standard locations only, after which you can invoke
#        standard utilities by name only, as usual:
#          PATH=$(command -p getconf PATH)

# Example command:
# Verify that `unset` now refers to the *builtin*:
type unset

テスト コマンド:

上記のコードがscript現在のディレクトリのファイルに保存されたとします。

unset次のコマンドは、 がエイリアスと関数の両方によってシャドウされ、 filescriptsourcedである改ざんされた環境をシミュレートします。これにより、関数が認識され、対話的にソースされると、エイリアスも展開されます。

$ (unset() { echo hi; }; alias unset='echo here'; . ./script)
unset is a shell builtin

type unset出力unset is a shell builtinは、関数とビルトインをシャドウするエイリアスの両方unsetが非アクティブ化されたことの証拠です。

于 2016-03-11T03:52:34.227 に答える
1

これは私ができることを知っていることです...

#!/bin/bash --posix

# たとえば BASH_FUNC_unset() 環境変数が設定されている場合、スクリプトの実行はできません
# ここまで (`bash script ...` としてではなく、そのまま実行する場合)

unset -f 組み込みコマンドの宣言 ...

saved_IFS=$IFS; 読み取り専用のsaved_IFS
# すべての関数を削除 (サブシェルで実行されるシェル組み込み宣言)
IFS=$'\n'; for f in `declare -Fx`; unset -f ${f##* }; を実行します。終わり; IFS=$saved_IFS
于 2016-11-01T10:32:49.980 に答える