development

쉘 스크립트가 호출 쉘의 환경 변수를 설정할 수 있습니까?

big-blog 2020. 2. 23. 11:54
반응형

쉘 스크립트가 호출 쉘의 환경 변수를 설정할 수 있습니까? [복제]


실행할 때 호출자의 셸에 설정된 상태로 유지되는 환경 변수를 설정하는 셸 스크립트를 작성하려고합니다.

setenv FOO foo

csh / tcsh에서 또는

export FOO=foo

sh / bash에서는 스크립트 실행 중에 만 설정합니다.

나는 이미 그것을 알고있다

source myscript

새 셸을 시작하지 않고 스크립트 명령을 실행하면 "호출자"환경이 설정 될 수 있습니다.

그러나 여기 문지름이 있습니다.

이 스크립트를 bash 또는 csh에서 호출 할 수 있기를 바랍니다. 다시 말해, 어느 쉘 사용자라도 스크립트를 실행할 수 있고 쉘 환경이 변경되기를 원합니다. csh를 실행하는 사용자는 bash 스크립트를 소싱 할 수없고 bash를 실행하는 사용자는 csh 스크립트를 소싱 할 수 없으므로 'source'는 작동하지 않습니다.

스크립트에서 두 가지 버전을 작성하고 유지 관리 할 필요가없는 합리적인 솔루션이 있습니까?


셸 프로세스에는 부모 환경의 복사본이 있으며 부모 프로세스 환경에 액세스 할 수 없습니다. 쉘 프로세스가 종료되면 환경에 대한 변경 사항이 유실됩니다. 쉘 환경을 구성하기 위해 스크립트 파일을 소싱하는 것이 가장 일반적으로 사용되는 방법입니다. 글 머리 기호를 물고 두 가지 유형의 쉘 각각에 대해 하나씩 유지하는 것이 좋습니다.


"dot space script"호출 구문을 사용하십시오. 예를 들어 다음은 스크립트의 전체 경로를 사용하여 수행하는 방법입니다.

. /path/to/set_env_vars.sh

다음은 스크립트와 동일한 디렉토리에있는 경우 수행 방법입니다.

. set_env_vars.sh

이들은 다른 쉘을로드하는 대신 현재 쉘에서 스크립트를 실행합니다 (이 경우 수행됩니다 ./set_env_vars.sh). 동일한 쉘에서 실행되므로 설정 한 환경 변수는 종료 될 때 사용할 수 있습니다.

이것은 호출하는 것과 동일 source set_env_vars.sh하지만 입력하는 것이 더 짧으며 입력 source하지 않는 곳에서는 작동 할 수 있습니다 .


호출자의 쉘이 다른 프로세스 컨텍스트에 있기 때문에 호출자의 쉘을 수정할 수 없습니다. 자식 프로세스가 셸 변수를 상속하면 복사본 자체를 상속합니다.

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`'

이것에 대한 한 가지 좋은 점은 목록을 한 곳에만 유지하면된다는 것입니다. 이론적으로는 목록에 파일을 넣고 cat nvpairfilename"in"과 "do"사이에 둘 수도 있습니다.

이것은 로그인 쉘 터미널 설정이 수행되는 방식과 거의 비슷합니다. 스크립트는 로그인 쉘에서 실행되는 통계를 출력합니다. 별명은 일반적으로 "tset vt100"과 같이 호출을 단순하게 만드는 데 사용됩니다. 다른 답변에서 언급했듯이 INN UseNet 뉴스 서버에도 비슷한 기능이 있습니다.


내 .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
}

따라서 프록시를 비활성화하려면 함수가 로그인 셸에서 실행되고 변수를 예상대로 설정합니다.


gdb와 setenv (3) 을 사용하면 "종류"가 가능 하지만 실제로이 작업을 수행하는 것은 쉽지 않습니다. (또한 가장 최근의 우분투는 커널이 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

이것은 작동합니다. 제가 사용하는 것이 아니지만 '작동'합니다. 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'}"인수리스트가 적어도 하나 개의 인자를 포함하는 경우, 원래의 인수 목록을 사용하여 비트 수단 '; 그렇지 않으면 -i존재하지 않는 인수를 대체 하십시오.


모듈을 사용해야합니다 ( http://modules.sourceforge.net/ 참조) .

편집 : 모듈 패키지는 2012 년 이후 업데이트되지 않았지만 여전히 기본 사항에 대해서는 정상적으로 작동합니다. 모든 새로운 기능, 종소리 및 휘파람은 오늘 lmod에서 발생합니다 (나는 그것을 더 좋아합니다) : https://www.tacc.utexas.edu/research-development/tacc-projects/lmod


내가 언급하지 않은 또 다른 해결 방법은 변수 값을 파일에 쓰는 것입니다.

나는 모든 테스트 대신 마지막 세트 테스트를 실행하고 싶었던 매우 비슷한 문제에 부딪쳤다. 첫 번째 계획은 env 변수 TESTCASE를 설정하기위한 하나의 명령을 작성한 다음이를 사용하여 테스트를 실행하는 다른 명령을 사용하는 것입니다. 말할 것도없이 당신과 똑같은 문제가있었습니다.

그러나이 간단한 해킹을 생각해 냈습니다.

첫 번째 명령 ( testset) :

#!/bin/bash

if [ $# -eq 1 ]
then
  echo $1 > ~/.TESTCASE
  echo "TESTCASE has been set to: $1"
else
  echo "Come again?"
fi

두 번째 명령 ( testrun) :

#!/bin/bash

TESTCASE=$(cat ~/.TESTCASE)
drush test-run $TESTCASE

bash 스크립트 위에 -l 플래그를 추가하십시오.

#!/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).

하위 프로세스에 환경 변수를 인쇄하도록 지시하고 ( "env"호출), 상위 프로세스에서 인쇄 된 환경 변수를 반복하고 해당 변수에 대해 "내보내기"를 호출 할 수 있습니다.

다음 코드는 find 출력 캡처를 기반으로 합니다. bash 배열로 -print0

부모 쉘이 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

bash_profile이 다른 다른 Bash를 호출 할 수 있습니다. 또한 다중 bashprofile 환경에서 사용하기 위해 특수한 bash_profile을 작성할 수 있습니다.

당신이 사용할 수있는 기억 기능을 bashprofile의 내부 및 기능에 avialable 전 세계적으로 될 것입니다. 예를 들어, "함수 사용자 {export USER_NAME $ 1}"은 (는) 런타임에서 변수를 설정할 수 있습니다. grep olegchir


기술적으로는 맞습니다. 'eval'만 다른 쉘을 포크하지 않습니다. 그러나 수정 된 환경에서 실행하려는 응용 프로그램의 관점에서 차이는 없습니다. 자식이 부모의 환경을 상속하므로 (수정 된) 환경은 모든 내림차순 프로세스로 전달됩니다.

실제로, 부모 프로그램 / 쉘에서 실행되는 한 변경된 환경 변수 '스틱'.

상위 (Perl 또는 쉘)가 종료 된 후에 환경 변수가 남아 있어야하는 경우, 상위 쉘이 무거운 리프팅을 수행해야합니다. 문서에서 본 한 가지 방법은 현재 스크립트가 필요한 '내보내기'언어로 실행 파일을 생성 한 다음 부모 셸이이를 실행하도록 속이는 것입니다. 비 휘발성 버전의 수정 된 환경을 남겨 두려는 경우 'source'명령을 사용하십시오. 최선의 Kluge.

두 번째 방법은 수정 된 매개 변수를 포함하도록 셸 환경 (.bashrc 등)을 시작하는 스크립트를 수정하는 것입니다. 초기화 스크립트를 연결하면 다음에 시작할 때 쉘을 사용하지 못할 수 있습니다. 현재 쉘을 수정하기위한 많은 도구가 있습니다. 필요한 조정을 '런처'에 첨부함으로써 이러한 변경 사항을 효과적으로 추진할 수 있습니다. 일반적으로 좋은 생각은 아닙니다. 특정 응용 프로그램 제품군에 대한 환경 변경 만 필요한 경우에는 나중에 돌아가서 셸 시작 스크립트를 원래 상태 (vi 또는 기타 사용)로 되돌려 야합니다.

간단히 말해서, 좋은 방법은 없습니다. 아마도 이것은 시스템의 보안이 결정적으로 손상되지 않도록하기 위해 어려워졌습니다.


짧은 대답은 아니요, 부모 프로세스의 환경을 변경할 수는 없지만 사용자가 선택한 환경 변수 및 사용자 정의 환경 변수가있는 환경 인 것 같습니다.

그렇다면 왜 단순히

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

그런 다음 환경을 다 마치면 exit.


항상 별칭을 사용할 수 있습니다

alias your_env='source ~/scripts/your_env.sh'

또 다른 옵션은 "환경 모듈"( http://modules.sourceforge.net/ )을 사용하는 것입니다. 불행히도 이것은 혼합에 제 3의 언어를 도입합니다. Tcl 언어로 환경을 정의하지만 일반적인 수정 (접두사 대 추가 대 세트)에 대한 몇 가지 편리한 명령이 있습니다. 또한 환경 모듈이 설치되어 있어야합니다. 그런 다음 module load *XXX*원하는 환경 이름을 지정할 수 있습니다 . 모듈 명령은 기본적으로 evalThomas Kammeyer가 위에서 설명한 메커니즘 의 멋진 별칭입니다 . 여기서 주요 장점은 환경을 한 언어로 유지하고 "환경 모듈"을 사용하여 sh, ksh, bash, csh, tcsh, zsh, python (?!? !!) 등으로 변환 할 수 있다는 것입니다.


나는 몇 년 전에 이것을했습니다. 올바르게 기억한다면, 각 .bashrc 및 .cshrc에 별명을 매개 변수와 함께 포함시켜 환경을 공통 양식으로 설정하는 각 양식의 별명을 지정했습니다.

그런 다음 두 쉘 중 하나에서 소스를 생성하는 스크립트에는 마지막 양식의 명령이 있으며, 각 쉘에서 별명을 지정합니다.

구체적인 별칭을 찾으면 게시합니다.


파이프, 평가 및 신호를 사용하여 솔루션을 만들었습니다.

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

모든 명령에서 작동 할 수 있습니다.


OS X bash에서 다음을 수행 할 수 있습니다
. bash 스크립트 파일을 작성하여 변수를 설정 해제하십시오.

#!/bin/bash
unset http_proxy

파일을 실행 가능하게 만들기

sudo chmod 744 unsetvar

별칭 만들기

alias unsetvar='source /your/path/to/the/script/unsetvar'

스크립트 파일이 포함 된 폴더가 경로에 추가 된 상태에서 사용할 준비가되어 있어야합니다.


협력 프로세스 로이 문제를 해결하는 방법을 문서화 한 답변이 없습니다. 이와 같은 일반적인 패턴 ssh-agent은 자식 프로세스가 부모가 할 수있는 표현식을 인쇄하도록하는 것 eval입니다.

bash$ eval $(shh-agent)

예를 들어 ssh-agentCsh 또는 Bourne 호환 출력 구문을 선택하는 옵션이 있습니다.

bash$ ssh-agent
SSH2_AUTH_SOCK=/tmp/ssh-era/ssh2-10690-agent; export SSH2_AUTH_SOCK;
SSH2_AGENT_PID=10691; export SSH2_AGENT_PID;
echo Agent pid 10691;

(이로 인해 에이전트 실행이 시작되지만이 출력을 셸 프롬프트에 복사하여 붙여 넣지 않으면 실제로 사용할 수 없습니다.) 비교 :

bash$ ssh-agent -c
setenv SSH2_AUTH_SOCK /tmp/ssh-era/ssh2-10751-agent;
setenv SSH2_AGENT_PID 10752;
echo Agent pid 10752;

(당신이 볼 수 있듯이 cshtcsh사용 setenv설정 varibles에 있습니다.)

자신의 프로그램도이 작업을 수행 할 수 있습니다.

bash$ foo=$(makefoo)

귀하의 makefoo스크립트는 단순히 계산하고 값을 출력하고, 호출자가 무엇이든 그들이 그것으로 싶어 할 것 - 일반적인 사용 사례입니다 변수에 할당하지만, 아마 뭔가 당신은 가치를 생산하는 도구로 하드 코드로 원하는 .


그것은 내가 뛰어난 것으로 부르지는 않지만, 어쨌든 쉘에서 스크립트를 호출 해야하는 경우에도 작동합니다. 좋은 해결책은 아니지만 단일 정적 환경 변수의 경우 충분히 잘 작동합니다.

1.) 0 (성공) 또는 1 (성공하지 않음)을 종료하는 조건으로 스크립트를 작성하십시오.

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

2.) 종료 코드에 의존하는 별명을 작성하십시오.

alias='myscript.sh && export MyVariable'

스크립트를 호출하는 별명을 호출하여 조건을 평가합니다.이 조건은 상위 쉘에서 환경 변수를 설정하기 위해 '&&'를 통해 0을 종료해야합니다.

이것은 flotsam이지만 핀치에 유용 할 수 있습니다.


$ SHELL / $ TERM 설정에 따라 조건문을 작성하는 것 외에는 없습니다. 펄 사용에있어 무엇이 문제입니까? 꽤 편 재적이며 (유닉스가없는 단일 UNIX 변형을 생각할 수 없음) 문제를 해결할 것입니다.

참고 URL : https://stackoverflow.com/questions/496702/can-a-shell-script-set-environment-variables-of-the-calling-shell



반응형