명령 앞에 "일회성 변수 할당"을 붙일 때 왜 이 변수를 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이미 내보내졌는지 여부는 동작에 영향을 주지 않습니다. 중요한 것은 쉘이 변수를 확장하는 정확한 순간에 변수 값이 무엇인지입니다.
$ 수출 과일=바나나
$ 과일 = 사과 에코 $ 과일
바나나
  • echo명령이 내장되어 있다는 사실은 OP 문제에 영향을 미치지 않습니다. 그러나 이 구문과 함께 내장 기능이나 셸 함수를 사용하면 예상치 못한 부작용이 발생하는 경우가 있습니다. 예:
$ 수출 과일=바나나
$ 과일=사과 평가 'echo $fruit'
사과
$ 에코 $과일
사과
  • 사이에 유사점이 있는 반면여기서 묻는 질문그리고 그것은 정확히 같은 문제가 아닙니다. 다른 질문으로 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실제로 로드되고("실행"됨) ls명령(프로세스)은 결코 로드되지 않습니다.본다문자열 "달러 기호 somevar". 이는 "myfile"이라는 문자열만 보고 이를 현재 작업 디렉터리에 있는 파일의 경로로 해석하고 이에 대한 정보를 가져오고 인쇄합니다.

명령 export somevar보다 먼저 실행 하면lssomevar=myfile환경하지만 명령이 이 변수를 사용하여 아무 작업 도 수행하지 않기 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
$ 

하지만 여기서는껍데기변수를 확장하면 실제로 의 결과 file1가 나타납니다.bc인수 목록. 따라서 이것은 여전히 ​​환경 변수가 아닌 쉘 변수로 사용됩니다.

이제 이 변수를 "내보내서" 쉘 변수이자 환경 변수가 되도록 하겠습니다.

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

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

명령별로 이를 재정의할 수 있으며 심지어 빈 값으로 재정의할 수도 있습니다.

$ 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. 각 변수 할당은 값을 할당하기 전에 [...] 확장되어야 합니다.

순서에 유의하십시오. 첫 번째 단계에서는 할당만 수행됩니다.저장됨, 그러면 다른 단어가 확장되고 마지막에만 변수 할당이 수행됩니다.

물론 표준은 항상 그랬고 기존 동작을 성문화했기 때문에 그렇게 말할 가능성이 높습니다. 역사나 그 배경에 대한 추론에 대해서는 아무 것도 말하지 않습니다. 쉘은 어떤 시점에서 할당처럼 보이는 단어를 식별해야 하므로(명령의 일부로 사용하지 않는 경우에만) 다른 순서로 작동할 수 있다고 가정합니다. 변수를 먼저 할당한 다음 명령줄에서 무엇이든 확장할 수 있습니다. . (아니면 그냥 왼쪽에서 오른쪽으로 하세요...)

관련 정보