환경 변수를 지정하고 동일한 명령 줄에서 에코 할 수없는 이유는 무엇입니까?


91

이 스 니펫을 고려하십시오.

$ SOMEVAR=AAA
$ echo zzz $SOMEVAR zzz
zzz AAA zzz

여기에 내가 설정 한 $SOMEVARAAA첫 번째 줄에 - 나는 두 번째 줄에 에코 때, 나는 도착 AAA예상대로 내용을.

그러나 다음과 같은 명령 줄에서 변수를 지정하려고하면 echo:

$ SOMEVAR=BBB echo zzz $SOMEVAR zzz
zzz AAA zzz

... BBB예상대로 얻지 못함 -이전 값 ( AAA)을 얻습니다 .

이게 어떻게 돼야 돼? 그렇다면 어떻게 변수를 지정 LD_PRELOAD=/... program args ...하고 작동시킬 수 있습니까? 내가 무엇을 놓치고 있습니까?


2
할당을 별도의 명령문으로 만들거나 자체 환경에서 스크립트를 호출 할 때 작동하지만 현재 환경에서 명령을 앞에 둘 때는 작동하지 않습니다. 흥미 롭군요!
Todd A. Jacobs

1
이유는 LD_PRELOAD작품은 변수가 프로그램에 설정되어 있는지입니다 환경 - 하지 의 명령 줄에서.
추후 공지가있을 때까지 일시 중지되었습니다.

답변:


103

당신이 보는 것은 예상되는 행동입니다. 문제는 부모 쉘이$SOMEVAR 이 수정 된 환경에서 명령을 호출하기 전에 명령 줄에서 것입니다. $SOMEVAR환경이 설정 될 때까지 지연된 평가를 받아야합니다 .

즉각적인 옵션은 다음과 같습니다.

  1. SOMEVAR=BBB eval echo zzz '$SOMEVAR' zzz.
  2. SOMEVAR=BBB sh -c 'echo zzz $SOMEVAR zzz'.

둘 다 작은 따옴표를 사용하여 부모 쉘이 평가하지 못하도록합니다 $SOMEVAR. 환경에 설정된 후에 만 ​​평가됩니다 (일시적으로 단일 명령 기간 동안).

또 다른 옵션은 하위 쉘 표기법을 사용하는 것입니다 ( 마커스 쿤답변 에서 제안한 것처럼 ).

(SOMEVAR=BBB; echo zzz $SOMEVAR zzz)

변수는 하위 쉘에서만 설정됩니다.


훌륭합니다, @JonathanLeffler-설명해 주셔서 감사합니다. 건배!
sdaau

@ markus-kuhn의 추가는 과대 평가하기 어렵습니다.
Alex Che

37

재검토 된 문제

솔직히, 매뉴얼은이 점에서 혼란 스럽습니다. GNU 배쉬 설명서는 말한다 :

간단한 명령 또는 함수 [내장을 제외 함]에 대한 환경은 쉘 매개 변수에 설명 된대로 매개 변수 지정을 접 두부로 추가하여 일시적으로 보강 할 수 있습니다. 이러한 할당 명령문은 해당 명령이 표시하는 환경에만 영향을줍니다.

실제로 문장을 구문 분석하면 명령 / 함수에 대한 환경 은 수정되지만 상위 프로세스의 환경은 수정되지 않습니다. 따라서 이것은 작동합니다.

$ TESTVAR=bbb env | fgrep TESTVAR
TESTVAR=bbb

env 명령이 실행되기 전에 환경이 수정 되었기 때문입니다. 그러나 이것은 작동하지 않습니다.

$ set -x; TESTVAR=bbb echo aaa $TESTVAR ccc
+ TESTVAR=bbb
+ echo aaa ccc
aaa ccc

매개 변수 확장이 쉘에 의해 수행 될 때 때문입니다.

통역사 단계

문제의 또 다른 부분은 Bash 가 인터프리터에 대해 다음 단계정의 한다는 것입니다 .

  1. 파일 (쉘 스크립트 참조), -c 호출 옵션에 인수로 제공된 문자열 (Bash 호출 참조) 또는 사용자 터미널에서 입력을 읽습니다.
  2. Quoting에 설명 된 인용 규칙에 따라 입력을 단어와 연산자로 나눕니다. 이러한 토큰은 메타 문자로 구분됩니다. 별칭 확장은이 단계에서 수행됩니다 (별칭 참조).
  3. 토큰을 단순 및 복합 명령으로 구문 분석합니다 (셸 명령 참조).
  4. 다양한 셸 확장 (셸 확장 참조)을 수행하여 확장 된 토큰을 파일 이름 목록 (파일 이름 확장 참조)과 명령 및 인수로 나눕니다.
  5. 필요한 리디렉션을 수행하고 (리디렉션 참조) 리디렉션 연산자와 해당 피연산자를 인수 목록에서 제거합니다.
  6. 명령을 실행합니다 (명령 실행 참조).
  7. 선택적으로 명령이 완료 될 때까지 대기하고 종료 상태를 수집합니다 (종료 상태 참조).

여기서 일어나는 일은 내장 기능이 자체 실행 환경을 얻지 못하므로 수정 된 환경을 볼 수 없다는 것입니다. 또한 간단한 명령 (예 : / bin / echo) 수정 된 환경 (env 예제가 작동 한 이유)을 얻지 만 쉘 확장은 4 단계 의 현재 환경에서 발생합니다.

즉, 'aaa $ TESTVAR ccc'를 / bin / echo에 전달하지 않습니다. 보간 된 문자열 (현재 환경에서 확장 됨)을 / bin / echo에 전달합니다. 이 경우 현재 환경에 TESTVAR 이 없으므로 단순히 'aaa ccc'를 명령에 전달합니다.

요약

문서는 훨씬 더 명확 할 수 있습니다. 스택 오버플로가 있습니다!

또한보십시오

http://www.gnu.org/software/bash/manual/bashref.html#Command-Execution-Environment


나는 이미 이것을 찬성했다-그러나 나는 방금이 질문으로 돌아 왔고,이 포스트는 내가 필요한 포인터를 정확히 포함하고있다; 감사합니다, @CodeGnome!
sdaau

이 답변이 게시 된 이후로 Bash 가이 영역에서 변경되었는지는 모르겠지만 접두사 변수 할당 이제 내장 기능에서 작동합니다. 예를 들어, 예상대로 FOO=foo eval 'echo $FOO'인쇄합니다 foo. 이것은 당신이 IFS="..." read ....
Will Vousden

나는 Bash가 실제로 자신의 환경을 일시적으로 수정하고 명령이 완료되면이를 복원하여 이상한 부작용을 일으킬 수 있다고 생각합니다.
Will Vousden

22

원하는 것을 얻으려면

( SOMEVAR=BBB; echo zzz $SOMEVAR zzz )

이유:

  • 다음 명령에서 세미콜론 또는 새 줄로 할당을 분리해야합니다. 그렇지 않으면 다음 명령 (echo)에 대한 매개 변수 확장이 발생 하기 전에 실행되지 않습니다 .

  • 현재 행을 넘어 지속되지 않도록하려면 서브 쉘 환경 내에서 할당 해야합니다.

이 솔루션은 다른 솔루션이 제안한 것보다 더 짧고 깔끔하며 효율적이며 특히 새로운 프로세스를 생성하지 않습니다.


3
여기에 올 미래의 Google 사용자를 위해 :이 질문에 대한 최고의 답변 일 것입니다. 더 복잡하게하려면 명령 환경에서 할당을 사용할 수 있어야하는 경우이를 내 보내야합니다. 서브 쉘은 여전히 ​​할당이 지속되는 것을 방지합니다. (export SOMEVAR=BBB; python -c "from os import getenv; print getenv('SOMEVAR')")
eaj

@eaj 만 사용하십시오 예에서와 같이, 하나의 외부 프로그램 호출에 쉘 변수를 내보내려면SOMEVAR=BBB python -c "from os import getenv; print getenv('SOMEVAR')"
마르쿠스 쿤

10

그 이유는 한 줄에 환경 변수를 설정하기 때문입니다. 그러나 echo확장을 bash하지 않습니다. 따라서, 귀하의 변수는 실제로 명령이 실행되기 전에, 비록 확장 SOME_VAR입니다 BBB에코 명령의 맥락에서.

효과를 확인하려면 다음과 같이 할 수 있습니다.

$ SOME_VAR=BBB bash -c 'echo $SOME_VAR'
BBB

여기서 변수는 하위 프로세스가 실행될 때까지 확장되지 않으므로 업데이트 된 값이 표시됩니다. SOME_VARIABLE부모 셸에서 다시 확인하면 AAA예상대로 여전히 입니다.


1
서면으로 작동하지 않는 이유에 대한 올바른 설명과 실행 가능한 해결 방법을 보려면 +1하십시오.
Jonathan Leffler

1
SOMEVAR=BBB; echo zzz $SOMEVAR zzz

사용; 같은 줄에있는 문을 구분합니다.


1
작동하지만 요점은 아닙니다. 아이디어는 솔루션처럼 영구적이 아닌 하나의 명령에 대한 환경을 설정하는 것입니다.
Jonathan Leffler

@Kyros에 감사드립니다. 지금까지 내가 어떻게 그것을 놓쳤는 지 모르겠습니다 :) LD_PRELOAD세미콜론이없는 실행 파일 앞에서 어떻게 작동 하는지 여전히 방황 하고 있습니다. 다시 한번 감사드립니다. 건배!
sdaau

@JonathanLeffler-실제로 그 아이디어였습니다. 세미콜론이 변경 사항을 영구적으로 만드는지 몰랐습니다. 주목 해 주셔서 감사합니다!
sdaau

1

다음은 한 가지 대안입니다.

SOMEVAR=BBB && echo zzz $SOMEVAR zzz

사용 여부 &&또는 ;하지 OP의 원하는 동작입니다 명령, 할당이 지속를 분리 할 수 있습니다. Markus Kuhn 은이 답변의 올바른 버전을 가지고 있습니다.
eaj
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.