506

実行時に、呼び出し元のシェルに設定されたままになるいくつかの環境変数を設定するシェルスクリプトを作成しようとしています。

setenv FOO foo

csh/tcsh、または

export FOO=foo

sh/bash では、スクリプトの実行中にのみ設定します。

私はすでにそれを知っています

source myscript

新しいシェルを起動するのではなく、スクリプトのコマンドを実行するため、「呼び出し元」の環境が設定される可能性があります。

しかし、ここにこすりがあります:

このスクリプトを bash または csh から呼び出せるようにします。つまり、いずれかのシェルのユーザーがスクリプトを実行して、シェルの環境を変更できるようにしたいと考えています。したがって、csh を実行しているユーザーは bash スクリプトを入手できず、bash を実行しているユーザーは csh スクリプトを入手できないため、'source' は機能しません。

スクリプトに 2 つのバージョンを記述して維持する必要のない合理的な解決策はありますか?

4

21 に答える 21

508

「ドット スペース スクリプト」呼び出し構文を使用します。たとえば、スクリプトへのフルパスを使用してそれを行う方法は次のとおりです。

. /path/to/set_env_vars.sh

スクリプトと同じディレクトリにいる場合の方法は次のとおりです。

. set_env_vars.sh

これらは、別のシェルをロードする代わりに、現在のシェルでスクリプトを実行します (これは、実行した場合に起こることです./set_env_vars.sh)。同じシェルで実行されるため、設定した環境変数は終了時に使用できます。

これは を呼び出すのと同じことですが、入力するのが短く、機能しないsource set_env_vars.sh場所でも機能する可能性があります。source

于 2015-02-12T23:04:57.163 に答える
312

シェル プロセスには親の環境のコピーがあり、親プロセスの環境にはまったくアクセスできません。シェル プロセスが終了すると、その環境に加えた変更はすべて失われます。スクリプト ファイルのソースは、シェル環境を構成するために最も一般的に使用される方法です。弾丸を噛んで、シェルの 2 つのフレーバーのそれぞれに 1 つを維持したいだけかもしれません。

于 2009-01-30T19:06:58.643 に答える
56

別のプロセス コンテキストにあるため、呼び出し元のシェルを変更することはできません。子プロセスがシェルの変数を継承するとき、それらは自分自身のコピーを継承しています。

できることの 1 つは、呼び出し方法に基づいて tcsh または sh の正しいコマンドを発行するスクリプトを作成することです。スクリプトが「setit」の場合は、次のようにします。

ln -s setit setit-sh

ln -s setit setit-csh

直接またはエイリアスで、shからこれを行います

eval `setit-sh`

またはこれはcshから

eval `setit-csh`

setit は $0 を使用して出力スタイルを決定します。

これは、人々が TERM 環境変数セットを取得するために使用する方法を思い起こさせます。

ここでの利点は、 setit が次のように好きなシェルで書かれていることです。

#!/bin/bash
arg0=$0
arg0=${arg0##*/}
for nv in \
   NAME1=VALUE1 \
   NAME2=VALUE2
do
   if [ x$arg0 = xsetit-sh ]; then
      echo 'export '$nv' ;'
   elif [ x$arg0 = xsetit-csh ]; then
      echo 'setenv '${nv%%=*}' '${nv##*=}' ;'
   fi
done

上記のシンボリック リンクと逆引用符で囲まれた式の評価を使用すると、目的の結果が得られます。

csh、tcsh、または同様のシェルの呼び出しを簡素化するには、次のようにします。

alias dosetit 'eval `setit-csh`'

またはsh、bashなどの場合:

alias dosetit='eval `setit-sh`'

これについての 1 つの良い点は、リストを 1 か所で管理するだけでよいことです。cat nvpairfilename理論的には、リストをファイルに貼り付けて、「in」と「do」の間に入れることもできます。

これは、ログイン シェルの端末設定が行われていた方法とほとんど同じです。スクリプトは、ログイン シェルで実行されるステートメントを出力します。「tset vt100」のように、エイリアスは通常、呼び出しを簡単にするために使用されます。別の回答で述べたように、INN UseNet ニュース サーバーにも同様の機能があります。

于 2009-01-30T19:06:58.550 に答える
50

私の .bash_profile には次のものがあります。

# No Proxy
function noproxy
{
    /usr/local/sbin/noproxy  #turn off proxy server
    unset http_proxy HTTP_PROXY https_proxy HTTPs_PROXY
}


# Proxy
function setproxy
{
    sh /usr/local/sbin/proxyon  #turn on proxy server 
    http_proxy=http://127.0.0.1:8118/
    HTTP_PROXY=$http_proxy
    https_proxy=$http_proxy
    HTTPS_PROXY=$https_proxy
    export http_proxy https_proxy HTTP_PROXY HTTPS_PROXY
}

したがって、プロキシを無効にしたい場合、関数はログインシェルで実行され、期待どおりに変数を設定します。

于 2011-11-19T23:46:30.900 に答える
35

gdb とsetenv(3)を使用することで「ある程度」可能ですが、実際にこれを行うことをお勧めするのは難しいです。(さらに、つまり、最新の ubuntu では、カーネルに ptrace についてより寛容になるように指示しない限り、実際にはこれを行うことはできません。他のディストリビューションでも同じことが言えます)。

$ cat setfoo
#! /bin/bash

gdb /proc/${PPID}/exe ${PPID} <<END >/dev/null
call setenv("foo", "bar", 0)
END
$ echo $foo

$ ./setfoo
$ echo $foo
bar
于 2011-07-08T22:03:34.663 に答える
13

これは機能します — 私が使用するものではありませんが、「機能します」。teredo環境変数を設定するスクリプトを作成しましょうTEREDO_WORMS:

#!/bin/ksh
export TEREDO_WORMS=ukelele
exec $SHELL -i

これは Korn シェルによって解釈され、環境変数をエクスポートしてから、それ自体を新しいインタラクティブ シェルに置き換えます。

このスクリプトを実行する前SHELLに、環境を C シェルに設定しましたが、環境変数TEREDO_WORMSは設定されていません。

% env | grep SHELL
SHELL=/bin/csh
% env | grep TEREDO
%

スクリプトを実行すると、別の対話型 C シェルである新しいシェルになりますが、環境変数は設定されています。

% teredo
% env | grep TEREDO
TEREDO_WORMS=ukelele
%

このシェルを終了すると、元のシェルが引き継ぎます:

% exit
% env | grep TEREDO
%

元のシェルの環境では環境変数が設定されていません。を使用exec teredoしてコマンドを実行すると、元の対話型シェルが環境を設定する Korn シェルに置き換えられ、次にそれが新しい対話型 C シェルに置き換えられます。

% exec teredo
% env | grep TEREDO
TEREDO_WORMS=ukelele
%

exit(または) と入力Control-Dすると、シェルが終了し、おそらくそのウィンドウからログアウトするか、実験が開始されたシェルの前のレベルに戻ります。

Bash または Korn シェルでも同じメカニズムが機能します。終了コマンドの後のプロンプトがおかしな場所に表示されることがあります。


コメントの議論に注意してください。-iこれは私が推奨するソリューションではありませんが、すべてのシェル (対話型シェルを作成するオプションを受け入れる) で動作する環境を設定する単一のスクリプトの目的を達成します。"$@"オプションの後に追加して、他の引数を中継することもできます。これにより、シェルを一般的な「環境の設定とコマンドの実行」ツールとして使用できるようになります。-i他の引数がある場合は、次のように省略したい場合があります。

#!/bin/ksh
export TEREDO_WORMS=ukelele
exec $SHELL "${@-'-i'}"

この"${@-'-i'}"ビットは、「引数リストに少なくとも 1 つの引数が含まれている場合は、元の引数リストを使用する」ことを意味します。-iそれ以外の場合は、存在しない引数を代用します。

于 2009-01-30T22:17:05.827 に答える
12

モジュールを使用する必要があります。http://modules.sourceforge.net/を参照してください。

編集: モジュール パッケージは 2012 年以降更新されていませんが、基本的には問題なく動作します。今日、すべての新機能、追加機能は lmod で行われます (私はこちらの方が気に入っています): https://www.tacc.utexas.edu/research-development/tacc-projects/lmod

于 2009-02-04T18:52:14.927 に答える
3

子プロセスに環境変数を出力するように指示し ("env" を呼び出す)、親プロセスで出力された環境変数をループして、それらの変数に対して "export" を呼び出すことができます。

次のコードは、 find の出力のキャプチャに基づいています。-print0 を bash 配列に

親シェルがbashの場合、使用できます

while IFS= read -r -d $'\0' line; do
    export "$line"
done < <(bash -s <<< 'export VARNAME=something; env -0')
echo $VARNAME

親シェルがダッシュの場合read、-d フラグが提供されず、コードがより複雑になります

TMPDIR=$(mktemp -d)
mkfifo $TMPDIR/fifo
(bash -s << "EOF"
    export VARNAME=something
    while IFS= read -r -d $'\0' line; do
        echo $(printf '%q' "$line")
    done < <(env -0)
EOF
) > $TMPDIR/fifo &
while read -r line; do export "$(eval echo $line)"; done < $TMPDIR/fifo
rm -r $TMPDIR
echo $VARNAME
于 2014-09-25T14:23:48.723 に答える
3

-l フラグを bash スクリプトの先頭に追加します。

#!/usr/bin/env bash -l

...

export NAME1="VALUE1"
export NAME2="VALUE2"

NAME1との値がNAME2現在の環境にエクスポートされますが、これらの変更は永続的ではありません。それらを永続的にしたい場合は、それらを.bashrcファイルまたは他のinitファイルに追加する必要があります。

マニュアルページから:

-l Make bash act as if it had been invoked as a login shell (see INVOCATION below).
于 2014-06-16T12:43:43.550 に答える
3

それは私が優れていると呼ぶものではありませんが、とにかくシェルからスクリプトを呼び出す必要がある場合にも機能します。これは良い解決策ではありませんが、単一の静的環境変数の場合は十分に機能します。

1.) 0 (成功) または 1 (失敗) のいずれかで終了する条件でスクリプトを作成します。

if [[ $foo == "True" ]]; then
    exit 0
else
    exit 1

2.) 終了コードに依存するエイリアスを作成します。

alias='myscript.sh && export MyVariable'

親シェルで環境変数を設定するために、「&&」を介してゼロを終了する必要がある条件を評価するスクリプトを呼び出すエイリアスを呼び出します。

これは漂流物ですが、いざという時に役立ちます。

于 2018-08-31T15:50:33.523 に答える
2

別のbash_profileを使用して別のBashを呼び出すことができます。また、マルチbashprofile環境で使用するための特別なbash_profileを作成できます。

bashprofile内で関数を使用でき、関数はグローバルに使用できることを忘れないでください。たとえば、「function user {export USER_NAME $ 1}」は、実行時に変数を設定できます。たとえば、次のようになります。user olegchir && env | grep olegchir

于 2010-10-29T06:15:02.647 に答える
2

別のオプションは、「環境モジュール」 ( http://modules.sourceforge.net/ ) を使用することです。残念ながら、これにより、第三言語がミックスに導入されます。Tcl の言語を使用して環境を定義しますが、一般的な変更 (prepend、append、set) のための便利なコマンドがいくつかあります。環境モジュールもインストールする必要があります。その後、 を使用module load *XXX*して、必要な環境に名前を付けることができます。evalmodule コマンドは基本的に、上記の Thomas Kammeyer によるメカニズムの凝ったエイリアスです。ここでの主な利点は、環境を 1 つの言語で維持し、「環境モジュール」に依存してそれを sh、ksh、bash、csh、tcsh、zsh、python (?!?!!) などに変換できることです。

于 2014-10-23T20:02:45.473 に答える
2

パイプ、eval、およびシグナルを使用してソリューションを作成しました。

parent() {
    if [ -z "$G_EVAL_FD" ]; then
            die 1 "Rode primeiro parent_setup no processo pai"
    fi
    if [ $(ppid) = "$$" ]; then
            "$@"
    else
            kill -SIGUSR1 $$
            echo "$@">&$G_EVAL_FD
    fi
}
parent_setup() {
    G_EVAL_FD=99
    tempfile=$(mktemp -u)
    mkfifo "$tempfile"
    eval "exec $G_EVAL_FD<>'$tempfile'"
    rm -f "$tempfile"
    trap "read CMD <&$G_EVAL_FD; eval \"\$CMD\"" USR1
}
parent_setup #on parent shell context
( A=1 ); echo $A # prints nothing
( parent A=1 ); echo $A # prints 1

どのコマンドでも動作する可能性があります。

于 2016-09-21T21:43:53.537 に答える
1

私は何年も前にこれをしました。私の記憶が正しければ、.bashrc と .cshrc のそれぞれにエイリアスをパラメーターとともに含め、環境を設定するそれぞれの形式を共通の形式にエイリアスしました。

次に、2 つのシェルのいずれかでソースとするスクリプトには、各シェルで適切にエイリアス化された最後の形式のコマンドがあります。

具体的なエイリアスが見つかったら、投稿します。

于 2015-10-30T15:14:45.430 に答える
1

親プロセスの環境を変更することはできませんが、カスタム環境変数とユーザーが選択したシェルを備えた環境が必要なようです。

では、単に次のようなものではないのはなぜですか

#!/usr/bin/env bash
FOO=foo $SHELL

次に、環境の操作が完了したら、exit.

于 2013-02-28T06:41:39.053 に答える
1

技術的には正しいです。'eval' だけが別のシェルをフォークしません。ただし、変更された環境で実行しようとしているアプリケーションの観点からは、違いはありません。子は親の環境を継承するため、(変更された) 環境はすべての下位プロセスに伝達されます。

当然のことながら、変更された環境変数は「固執」します-親プログラム/シェルで実行している限り。

親 (Perl またはシェル) が終了した後も環境変数を残すことが絶対に必要な場合は、親シェルが面倒な作業を行う必要があります。ドキュメントで見た 1 つの方法は、現在のスクリプトが必要な「エクスポート」言語で実行可能ファイルを生成し、親シェルをだましてそれを実行させることです。変更された環境の不揮発性バージョンを残したい場合は、'source' を指定してコマンドを実行してください。せいぜいクルーゲ。

2 番目の方法は、シェル環境 (.bashrc など) を開始するスクリプトを変更して、変更したパラメーターを含めることです。これは危険な場合があります。初期化スクリプトをホースアップすると、次に起動しようとしたときにシェルが使用できなくなる可能性があります。現在のシェルを変更するためのツールはたくさんあります。必要な調整を「ランチャー」に追加することで、これらの変更も効果的に進められます。一般的には良い考えではありません。特定のアプリケーション スイートの環境変更のみが必要な場合は、後で戻ってシェル起動スクリプトを元の状態に戻す必要があります (vi などを使用)。

要するに、良い (そして簡単な) メソッドはありません。おそらく、システムのセキュリティが取り返しのつかないほど損なわれないようにするために、これが困難になったのでしょう。

于 2011-05-18T13:23:39.377 に答える
-11

$SHELL/$TERM が何に設定されているかに応じて条件を記述する以外は、いいえ。Perl を使用することの何が問題になっていますか? これはかなりどこにでもあり (UNIX でこれを持たないものは 1 つも思い浮かびません)、トラブルから解放されます。

于 2009-01-30T19:08:12.200 に答える