명령 앞에 "일회성 변수 할당"을 붙일 때 변수를 bash 확장하지 않는 이유는 무엇입니까?

명령 앞에 "일회성 변수 할당"을 붙일 때 변수를 bash 확장하지 않는 이유는 무엇입니까?

이 bash 명령을 실행하고 명령문 앞에 접두사를 붙인 경우 변수는 fruit이 명령이 실행되는 동안에만 존재해야 합니다.

$ fruit=apple echo $fruit

$

결과는 빈 줄입니다. 왜?

와일드카드를 인용한 댓글이 문제:

매개변수 확장은 쉘에 의해 수행됩니다. "fruit" 변수는 쉘 변수가 아니며 "echo" 명령 환경의 환경 변수일 뿐입니다.

환경 변수는 여전히 변수이므로 echo 명령과 함께 사용할 수 있습니까?

답변1

문제는 현재 쉘이 변수를 조기에 확장한다는 것입니다. 해당 컨텍스트에 설정되어 있지 않으므로 명령에 echo인수가 없습니다. 즉, 명령은 다음과 같습니다.

$ fruit=apple echo

작은따옴표로 인해 변수가 조기에 확장되지 않도록 하는 해결 방법은 다음과 같습니다.

$ fruit=apple sh -c 'echo $fruit'

fruit또는 변수가 실행된 명령에 올바르게 전달되었음을 보여주는 한 줄짜리 셸 스크립트를 사용할 수도 있습니다 .

$ cat /tmp/echof
echo $fruit
$ /tmp/echof

$ fruit=apple /tmp/echof
apple
$ echo $fruit

$

이 문제는 예상치 못한 논란과 논의를 불러일으켰기 때문에, 일부 의견은 다음과 같습니다.

  • 변수 fruit를 내보냈는지 여부는 해당 동작에 영향을 주지 않습니다. 중요한 것은 쉘이 변수를 확장하는 정확한 순간의 변수 값이 무엇인지입니다.
$ 수출 과일 = 바나나
$fruit=사과 에코 $fruit
바나나
  • 이 명령이 내장되어 있다는 사실은 echoOP의 질문에 영향을 미치지 않습니다. 그러나 경우에 따라 이 구문과 함께 내장 함수나 셸 함수를 사용하면 다음과 같은 예기치 않은 부작용이 발생할 수 있습니다.
$ 수출 과일 = 바나나
$fruit=apple 평가 'echo $fruit'
사과
$echo$과일
사과
  • 비록 둘 사이에는 유사점이 있지만여기에 묻는 질문그리고 그것은 정확히 같은 질문이 아닙니다. 또 다른 질문으로, IFS쉘이분사 다른$var여기서 변수, fruit임시변수 값은 쉘 실행시 아직 사용할 수 없습니다.확장하다이것동일한바꾸다.

  • 게다가또 다른 문제OP는 사용된 구문의 중요성에 대해 묻고 있으며, 보다 정확하게는 "이 작업이 수행되는 이유는 무엇입니까?"입니다. 여기서 OP는 중요성을 인식하지만 예상치 못한 동작을 보고하고 그 이유를 묻습니다. 즉, "이것이 왜 작동하지 않습니까?" 글쎄, 다른 질문에 게시된 형편없는 스크린샷을 좀 더 주의 깊게 읽어본 결과, 거기에도 실제로 동일한 상황이 설명되어 있으므로( BAZ=jake echo $BAZ) 네, 결국 중복입니다.

답변2

이것을 올바르게 이해하려면 먼저 구별해야합니다.쉘 변수~에서환경 변수.

환경 변수는 내부적으로 사용하는지 여부에 관계없이 모든 프로세스가 소유하는 속성입니다. 실행 중에도 sleep 10환경변수가 있습니다 . 모든 프로세스에는 PID(프로세스 식별자), 현재 작업 디렉터리(cwd), PPID(상위 PID), 매개변수 목록(비어 있는 경우에도) 등이 있습니다. 모든 프로세스에는 포크할 때 상위 프로세스에서 상속되는 "환경"도 있습니다.

유틸리티 작성자(C로 코드를 작성하는 사람)의 관점에서 프로세스는 환경 변수를 설정, 설정 해제 또는 변경할 수 있습니다. 그러나 스크립트 작성자의 관점에서 볼 때 대부분의 도구는 사용자에게 이 기능을 제공하지 않습니다. 대신, 당신은 당신의껍데기프로세스 환경을 변경한 다음 호출하는 명령(외부 바이너리)을 실행할 때 해당 프로세스 환경을 상속합니다. (쉘 자체의 환경은 수정될 수 있으며 수정 사항은 상속되거나 포크 이후 호출한 명령을 실행하기 전에 쉘에 이를 수정하도록 지시할 수 있습니다. 어느 쪽이든 환경이 상속됩니다. 또한 친밀감에 대해서는 두 가지 경우를 모두 확인하십시오.)

쉘 변수는 또 다른 이야기입니다. 하지만껍질 내부그들은 같은 방식으로 동작합니다. 차이점은 단순한 "쉘 변수"는 호출하는 명령의 동작을 변경하거나 영향을 미치지 않는다는 것입니다.~에서당신의 껍질. 올바른 용어로 표현하면 이러한 구별은 실제로 약간 다르게 표현됩니다.출구쉘 변수는 호출하는 도구 환경의 일부가 되며 쉘 변수는아니요수출은 되지 않습니다. 그러나 "쉘 변수"로 내보내지지 않는 쉘 변수를 참조하고 "쉘 변수"로 내보내지는 쉘 변수를 참조하는 것이 통신에 더 도움이 된다는 것을 알았습니다."환경 변수"로 내보내졌습니다.셸 생성 프로세스의 관점에서 환경 변수를 봅니다.


텍스트가 너무 많아요. 몇 가지 예를 보고 무슨 일이 일어나고 있는지 설명하겠습니다.

$ somevar=myfile
$ ls -l "$somevar"
-rw-r--r--  1 Myname  staff  0 May 29 19:12 myfile
$ 

이 예에서는 somevar특별한 것이 아닌 쉘 변수일 뿐입니다. 껍데기매개변수 확장(참조 LESS='+/Parameter Expansion' man bash) 일이 일어나다앞으로실행 파일은 ls실제로 로드되지만("exec") ls명령(프로세스)은 로드되지도 않습니다.보다문자열 "달러 기호 somevar". 단지 "myfile"이라는 문자열을 보고 이를 현재 작업 디렉터리에 있는 파일의 경로로 해석하고 이에 대한 정보를 가져오고 인쇄합니다.

export somevar명령 전에 실행 하면 ls사실이 somevar=myfile다음에 나타납니다.환경process 이지만 명령이 이 변수를 사용하여 아무 작업도 수행하지 않으므로 ls아무 영향도 미치지 않습니다 . ls가서 하나 보세요영향환경 변수의 경우 호출하는 프로세스가 실제로 확인하고 이를 사용하여 작업을 수행할 환경 변수를 선택해야 합니다.


bc: 기본 계산기

아마도 더 나은 예가 있을 수 있지만 이것은 너무 복잡하지 않은 제가 생각해낸 예입니다. 가장 먼저 알아야 할 것은 이것이 bc수학적 표현을 처리하고 계산할 수 있는 기본 계산기라는 것입니다. (입력 파일의 내용을 처리한 후 표준 입력을 처리합니다. 예제에서는 표준 입력을 사용하지 않습니다. Ctrl-D만 누르면 아래 텍스트 조각에는 표시되지 않습니다. 추가로 다음을 사용합니다. 통화할 때마다 소개 메시지를 표시 -q하지 않습니다.

제가 설명할 환경 변수는 아래에 설명되어 있습니다 man bc.

   BC_ENV_ARGS
      This is another mechanism to get arguments to bc.  The format is
      the  same  as  the  command line arguments.  These arguments are
      processed first, so any files listed in  the  environment  argu-
      ments  are  processed  before  any  command line argument files.
      This allows the user to set up "standard" options and  files  to
      be  processed at every invocation of bc.  The files in the envi-
      ronment variables would typically contain  function  definitions
      for functions the user wants defined every time bc is run.

시작:

$ cat file1
5*5
$ bc -q file1
25
$ cat file2
6*7
8+9+10
$ bc -q file2
42
27
$ bc -q file1 file2
25
42
27
$

이것은 단지 bc그것이 어떻게 작동하는지 보여주기 위한 것입니다. 각각의 경우에 "입력 끝" 신호를 보내려면 Ctrl-D를 눌러야 합니다 bc.

이제 환경 변수를 전달해 보겠습니다.곧장도착하다 bc:

$ BC_ENV_ARGS=file1 bc -q file2
25
42
27
$ echo "$BC_ENV_ARGS"

$ bc -q file2
42
27
$

이 변수에 넣은 내용은 다음과 같습니다.아니요echo이는 나중에 명령을 통해 확인할 수 있습니다 . 동일한 명령의 일부로(세미콜론 없이) 할당을 수행함으로써 변수를 다음에 할당합니다.부분환경 bc- 우리가 실행 중인 쉘 자체는 영향을 받지 않습니다.

BC_ENV_ARGS이제 설정해 보겠습니다 .껍데기바꾸다:

$ BC_ENV_ARGS=file1
$ echo "$BC_ENV_ARGS"
file1
$ bc -q file2
42
27
$

echo여기에서 우리의 명령이 콘텐츠를 볼 수 있지만 환경의 일부가 아니므 bcbc특별한 작업을 수행할 수 없음을 알 수 있습니다 .

물론 변수 자체를 bc인수 목록에 넣으면 다음과 같은 내용이 표시됩니다.

$ bc -q "$BC_ENV_ARGS"
25
$ 

그런데 여기는,껍데기변수를 확장한 다음 실제로 s file1에 나타나는 내용을 확장합니다.bc매개변수 목록. 따라서 이것은 여전히 ​​환경 변수가 아닌 쉘 변수로 사용됩니다.

이제 이 변수를 쉘 변수이자 환경 변수가 되도록 "내보냅니다".

$ export BC_ENV_ARGS
$ echo "$BC_ENV_ARGS"
file1
$ bc -q file2
25
42
27
$

여기 명령줄에서는 언급되지 않았지만 여기서는 file1이전에 처리된 것을 볼 수 있습니다 . file2이는 쉘 환경의 일부이며 bc프로세스를 실행할 때 환경의 일부가 되므로 이 환경 변수의 값은 다음과 같습니다.유전학bc작업 수행 방식에 영향을 미칩니다 .

명령별로 이를 재정의하거나 null 값으로 다시 쓸 수도 있습니다.

$ BC_ENV_ARGS= bc -q file2
42
27
$ echo "$BC_ENV_ARGS"
file1
$ bc -q file2
25 
42
27
$ 

그러나 보시다시피 변수는 셸에서 설정 및 내보내진 상태로 유지되며 bc셸 자체와 후속 명령에서 볼 수 있습니다.아니요이 값을 재정의합니다. "내보내기 취소" 또는 "설정 해제"하지 않는 이상 그대로 유지됩니다. 나는 후자를 택하겠다:

$ unset BC_ENV_ARGS
$ echo "$BC_ENV_ARGS"

$ bc -q file2
42
27
$ 

다른 쉘을 생성하는 것과 관련된 또 다른 예:

쉘에 다음 명령을 차례로 입력하고 결과를 고려하십시오. 실행하기 전에 결과를 예측할 수 있는지 확인하세요.

# fruit is not set
echo "$fruit"
sh -c 'echo "$fruit"'
# fruit is set as a shell variable in the current shell only
fruit=apple
echo "$fruit"
sh -c 'echo "$fruit"'
sh -c "echo $fruit" ### NOT advised for use in scripts, for illustration only
# fruit is exported, so it's accessible in current AND new processes
export fruit
echo "$fruit"
sh -c 'echo "$fruit"'
echo '$fruit' ### I threw this in to make sure you're not confused on quoting
# fruit is unset again
unset fruit
echo "$fruit"
sh -c 'echo "$fruit"'
# setting fruit directly in environment of single command but NOT in current shell
fruit=apple sh -c 'echo "$fruit"'
echo "$fruit"
fruit=apple echo "$fruit"
# showing current shell is unaffected by directly setting env of single command
fruit=cherry
echo "$fruit"
fruit=apricot sh -c 'echo "$fruit"'
echo "$fruit"
sh -c 'echo "$fruit"'

마지막은 보너스 트릭입니다. 순서대로 실행되는 다음 명령의 출력을 예측할 수 있습니까? :)

fruit=banana
fruit=orange sh -c 'fruit=lemon echo "$fruit"; echo "$fruit"; export fruit=peach'
echo "$fruit"

의견에 필요한 설명을 언급해 주세요. 이것이 도움이 될 것이라고 확신합니다. 하지만 그렇더라도 도움이 될 것입니다.

답변3

변수 할당 전에 명령줄 확장이 발생하기 때문입니다.표준에는 그렇게 나와 있습니다.:

주어진 간단한 명령을 실행해야 하는 경우 다음을 수행해야 합니다.

  1. 변수 할당으로 식별된 단어는 3단계와 4단계에서 처리하기 위해 저장됩니다.

  2. 변수 할당이나 리디렉션이 아닌 단어는 확장되어야 합니다. [...]

  3. 리디렉션은 리디렉션에 설명된 대로 수행되어야 합니다.

  4. 각 변수 할당은 할당 전에 확장 [...]되어야 합니다.

순서에 유의하십시오. 첫 번째 단계에서는 할당만 수행됩니다.저장됨, 그런 다음 다른 단어를 확장하고 마지막에만 변수 할당을 수행합니다.

물론, 표준은 항상 그랬기 때문에 단지 그렇게 말할 수도 있고, 기존 동작을 성문화하는 것일 뿐이기 때문입니다. 역사나 그 이유에 대해서는 아무 것도 언급하지 않습니다. 쉘은 특정 시점에서 할당처럼 보이는 단어를 인식해야 합니다(명령의 일부로 포함하지 않는 경우에만). 따라서 변수를 먼저 할당한 다음 명령줄에서 무엇이든 확장하는 등 다른 순서로 작동할 수 있다고 생각합니다. (아니면 왼쪽에서 오른쪽으로 진행하세요...)

관련 정보