変数の値を失わずに、変数をアンエクスポートするにはどうすればよいですか?


10

変数をエクスポートしたとしましょう:

foo=bar
export foo

それでは、エクスポートを解除したいと思います。つまり、もしそうなら、sh -c 'echo "$foo"'私は得るべきではありませんbar。の環境でfooはまったく表示されませんsh -csh -cは単なる例であり、変数の存在を示す簡単な方法です。コマンドは何でもかまいません。環境内の変数の存在によって動作が影響を受けるものである可能性があります。

できます:

  1. unset 変数、そしてそれを失う
  2. envコマンドごとに使用して削除します。env -u foo sh -c 'echo "$foo"'
    • 現在のシェルをしばらく使用し続けたい場合は非現実的です。

理想的には、変数の値を保持したいのですが、空の変数としても、子プロセスでまったく表示しないようにします。

私はできると思います:

otherfoo="$foo"; unset foo; foo="$otherfoo"; unset otherfoo

これはotherfoo、すでに存在する場合、踏みにじるリスクがあります。

それが唯一の方法ですか?標準的な方法はありますか?


1
あなたは使用して、一時ファイルに値をエコー可能性がmktempあればそれがポータブル十分で、変数を割り当てる値、およびソースに一時ファイルの設定を解除します。シェル変数とは対照的に、少なくとも一時ファイルは多かれ少なかれ任意の名前で作成できます。
Thomas Dickey

@Sukminder sh -cコマンドは単なる例です。可能であれば、代わりに変数を設定解除できないコマンドを実行します。
muru

回答:


6

標準的な方法はありません。

関数を使用すると、一時変数の使用を回避できます。次の関数は、未設定の変数を未設定のままにし、空の変数を空のままにするように注意します。ただし、読み取り専用変数や型付き変数など、一部のシェルにある機能はサポートしていません。

unexport () {
  while [ "$#" -ne 0 ]; do
    eval "set -- \"\${$1}\" \"\${$1+set}\" \"\$@\""
    if [ -n "$2" ]; then
      unset "$3"
      eval "$3=\$1"
    fi
    shift; shift; shift
  done
}
unexport foo bar

ksh、bash、およびzshでは、を使用して変数をアンエクスポートできますtypeset +x foo。これにより、タイプなどの特別なプロパティが保持されるため、使用することをお勧めします。typeset組み込みのシェルはすべて持っていると思いますtypeset +x

case $(LC_ALL=C type typeset 2>&1) in
  typeset\ *\ builtin) unexport () { typeset +x -- "$@"; };;
  *) unexport () {  };; # code above
esac

1
に慣れていない場合は、空の場合でも設定${var+foo}されているfoo場合varは評価され、それ以外の場合は何も評価されません。
muru

前者をサポートするシェルのtypeset +xvs export -nについてコメントはありますか?あるexport -n稀、またはそれはいくつかのプロパティを保持しないのですか?
muru

@muru bashスクリプトを作成している場合はexport -ntypeset +x無差別に使用できます。kshまたはzshでは、のみtypeset +xです。
ジル 'SO-邪悪なことをやめなさい'

7

EDIT:の場合bashのみ、コメントで指摘したように:

-nオプションがしますexport削除しexport、各指定された名前からプロパティを。(を参照してくださいhelp export。)

したがって、のbash場合、必要なコマンドは次のとおりです。export -n foo


1
これはシェル固有であり(POSIXを参照)、OPはシェルを指定しませんでしたが、問題を解決する標準的な方法を求めました。
Thomas Dickey

1
@ThomasDickey、それを知らなかった。ありがとう、更新されました。
ワイルドカード

3

私は同様のPOSIX関数を記述しましたが、これは任意のコードが実行される危険を冒していません。

unexport()
    while case ${1##[0-9]*} in                   ### rule out leading numerics
          (*[!_[:alnum:]]*|"")                   ### filter out bad|empty names
          set "" ${1+"bad name: '$1'"}           ### prep bad name error
          return ${2+${1:?"$2"}}                 ### fail w/ above err or return 
          esac
    do    eval  set '"$'"{$1+$1}"'" "$'"$1"'" "$'@\" ###  $1 = (  $1+ ? $1 : "" )
          eval  "${1:+unset $1;$1=\$2;} shift 3"     ### $$1 = ( $1:+ ? $2 : -- )
    done

また、指定したい数の引数を処理します。引数が、まだ設定されていない有効な名前である場合、それは黙って無視されます。引数が不正な名前である場合、それはstderrに書き込み、必要に応じて停止しますが、コマンドラインで無効な前にある有効な名前は引き続き処理されます。

別の方法を考えました。私はそれがずっと好きです。

unexport()
        while   unset OPTARG; OPTIND=1           ### always work w/ $1
                case  ${1##[0-9]*}    in         ### same old same old
                (*[!_[:alnum:]]*|"")             ### goodname && $# > 0 || break
                    ${1+"getopts"} : "$1"        ### $# ? getopts : ":"
                    return                       ### getopts errored or ":" didnt
                esac
        do      eval   getopts :s: '"$1" -"${'"$1+s}-\$$1\""
                eval   unset  "$1;  ${OPTARG+$1=\${OPTARG}#-}"
                shift
        done

まあ、どちらも同じテクニックをたくさん使っています。基本的に、シェル+変数が設定されていない場合、それへの参照はパラメーター展開で展開されません。しかし、それが設定されている場合-その値に関係なく-次のようなパラメーター展開は、変数の値ではなく-に${parameter+word}展開されwordます。そして、シェル変数は成功すると自己テストし、自己置換します。

彼らはまた、自己失敗することができます。最上位の関数では、不正な名前が見つかった場合に移動$1$2$1nullのままにします。これはreturn、すべての引数が処理されてループが終了した場合に成功するか、引数が無効な場合にシェルが拡大$2$1:?どのスクリプトシェルを殺し、書き込み中にインタラクティブなものに割り込みを返しますwordstderrに。

2つ目getoptsは割り当てを行います。そして、それは悪い名前を割り当てません-むしろそれを書くと標準エラーメッセージをstderrに書き出します。さらに、引数が最初にセット変数の名前であった$OPTARG 場合、argの値を保存します。したがって、getopts必要なのは、適切な割り当てへのevalセットOPTARGの拡張です。


2
ある日、私はあなたの答えの1つに頭を巻きつけようとした後、どこかで精神病棟にいます。:D他の答えは、任意のコード実行にどのように苦しんでいますか?例を挙げていただけますか?
muru

3
@muru-引数が無効な名前でない限り。しかしそれは問題ではありません-問題は入力が検証されないことです。はい、任意のコードを実行させるには、奇妙な名前の引数を渡す必要がありますが、これは、歴史上のすべてのCVEの基本です。export奇妙な名前を付けようとしても、コンピュータが殺されることはありません。
mikeserv 2016年

1
@muru-ああ、引数は任意var=something; varname=var; export "$varname"です。完全に有効です。同じことが、unsetこれと他についても"$varname"言えますが、その変数の内容がおかしくなると、残念なことになるかもしれません。bashとにかく、関数全体のエクスポートの大失敗が起こったのはこのためです。
mikeserv 2016年

1
@mikeserv私はあなたが(少なくとも、またはコメント行)自体を説明したコードとその難読化コードに置き換えた場合、あなたは(少なくとも私から)多くのupvotesを取得したいと思います
PSkocik

1
@PSkocik-完了。
mikeserv 2016年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.