명령 대체가 왜 이렇게 작동합니까?

명령 대체가 왜 이렇게 작동합니까?
$ echo $(echo x; echo y)
x y
$ a='echo x; echo y'
$ echo $($a)  # expect 'x y'
x; echo y
  1. 명령 대체가 왜 이렇게 작동합니까?
  2. 및를 사용하지 않고 eval변수에 저장된 명령 목록에서 명령 대체를 어떻게 수행 할 수 있습니까 bash -c?

echo $(eval $a)실제로 echo $(bash -c "$a")는 내가 원하는 대로 되는데 eval을 사용하는 것은 문제를 해결하는 데 일반적으로 잘못된 방법이라고 들었기 때문에 이러한 명령 없이 어떻게 관리할 수 있는지 궁금합니다(using은 bash -c실제로 같은 것입니다).

답변1

참여는 명령 평가의 매우 늦게 발생합니다. 당신에게 가장 중요한 것은 그런 일이 일어났다는 것입니다뒤쪽에변수 확장 및 명령 대체.

이는 두 번째 줄을 의미합니다.

s="echo a; echo b"
echo $($s)

먼저 $s확장 echo a; echo b한 다음 명령을 실행합니다.아니요두 개의 s로 구성된 복합 명령으로 분할합니다 echo.

(세부 사항: 은 , , 및 $s 4개의 단어로 나뉩니다 . 패키지는 , , 및 3개의 매개 변수 , 즉 기본적으로 가지고 있는 명령을 사용하여 이를 실행합니다 .)echoa;echob$(...)echoa;echobecho $('echo' 'a;' 'echo' 'b')

따라서 주어진 가장 바깥쪽 echo은 문자열 a; echo b(실제로 $(...)는 따옴표 없이 세 단어)입니다. 왜냐하면 이것이 가장 안쪽 출력이기 때문입니다 echo.

비교하다

s1="echo a"
s2="echo b"
echo $($s1;$s2)

그러면 예상한 결과가 나올 것입니다.

예, " eval악하다"는 것은 대부분의 경우 sh -c자신이 무엇을 하고 있는지 모른다면 아마도 어색하고 취약하다고 느낄 것입니다. 하지만 신뢰할 수 있는 쉘 코드가 있다고 가정하면문자열로쉘 스크립트에서. 이 경우 이러한 도구는 이 코드를 올바르게 실행하는 유일한(?) 방법입니다. 일반적으로 문자열의 텍스트를 쉘 코드(처음부터 끝까지 모든 평가 단계)로 명시적으로 평가해야 하기 때문입니다. 복합 명령.


나는 이것이 Unix 쉘과 Unix 쉘 때문이라고 생각합니다.텍스트echo $($s)당신이 뭔가를 할 수 있다는 것은 행운이다별말씀을요.

문자열로 주어진 C 코드 조각을 실행하기 위해 C 프로그램을 얻기 위해 취해야 할 단계에 대해 생각해 보십시오.

답변2

좀 읽은 후 나는 스스로 대답하려고 노력했습니다.

명령 대체가 왜 이렇게 작동합니까?

$ a='echo x; echo y'
$ echo $($a)  # expect 'x y'

명령 대체

$a변수 대체가 수행된다는 점에 유의하세요.서브쉘에서현재 쉘이 아닌 명령 대체 중에 생성됩니다(1,2,). 따라서 서브쉘은 $a대신 명령을 실행합니다 echo x; echo y(변수의 값은 a상속됨).

그럼 이제 알아내기만 하면 된다쉘이 왜 이렇게 동작합니까?:

$ a='echo x; echo y'
$ $a
x; echo y

분사

~에 따르면배쉬 참조 매뉴얼:

확장에는 7가지 유형이 있습니다... 확장 순서는 물결표 확장, 매개변수 및 변수 확장, 산술 확장 및 명령 대체(왼쪽에서 오른쪽으로 완료)입니다.분사; 및 파일 이름 확장자

——본 항목에서 발췌3.5 쉘 확장


쉘 스캔매개변수 확장, 명령 대체 및 산술 확장의 결과단어 분리에 사용되는 큰따옴표 내에서는 이런 일이 발생하지 않습니다.

쉘은 각각을 처리합니다.$IFS의 문자구분 기호로 사용하고 다른 확장 결과를 단어로 나눕니다.이것들필드 종결자로서의 문자

——본 항목에서 발췌3.5.7 분사

(기본값은 IFS)<space><tab><newline>


IFS 변수는 모든 단어가 아닌 확장 결과를 분할하는 데에만 사용됩니다(단어 분할 참조).

- 에서부록 B Bourne Shell과의 주요 차이점


메타 문자

인용되지 않은 문자는 다음과 같습니다.별도의 단어. 메타 문자는 공백, 탭, 줄 바꿈 또는 |, &, ;, (, ), <또는 문자 중 하나입니다 >.

——본 항목에서 발췌2. 정의


명령을 읽고 실행할 때 쉘이 수행하는 작업에 대한 간략한 설명입니다. 기본적으로 쉘은 다음을 수행합니다.

  1. 입력을 읽으십시오.
  2. 인용 규칙을 준수하여 입력을 단어와 연산자로 나눕니다. 이러한 태그는 메타 문자로 구분됩니다.
  3. 구문 분석 토큰단순 명령어와 복합 명령어로 구분됩니다.
  4. 다양한 수행쉘 확장.
  5. 필요한 리디렉션을 수행합니다.
  6. 주문 실행

——본 항목에서 발췌3.1.1 쉘 동작

이제 우리는 실제로두 개의 다른 "분사"— 초기 토큰화(2단계) 및 셸 확장에 속하는 토큰화(4단계).

초기 단어 분할(2단계)에서는 ;, (, )같은 메타 문자를 결과로 처리합니다 .파싱된(3단계) 메타 문자와 키워드를 식별할 수 있습니다.

그런 다음 Bash는 "단어 분리"를 포함하여 다양한 쉘 확장(4단계)을 수행합니다. 그 중 하나입니다. 이러한 유형의 단어 분할은 문자를 구분 기호로만 처리합니다 IFS. 메타 문자는 신경 쓰지 않습니다. 결과적으로해결되지 않은다시 말하지만, 다른 확장된 결과도 마찬가지입니다. 따라서 메타문자와 키워드는 인식되지 않습니다.

이것이 ;값 내의 변수가 a명령 구분 기호로 간주되지 않는 이유입니다. 의 값이 있더라도 해당 단어는 메타 문자로 간주되지 않으므로 명령은 가 됩니다 $a.'echo' 'x;' 'echo' 'y'a'echo x ; echo y'';'$a'echo' 'x' ';' 'echo' 'y'

요약:

메타 문자와 키워드( if, thenwhile)는 확장의 결과가 될 수 없지만 프로그램 이름, 내장 명령, 함수 및 별칭은 가능합니다. 간단한 명령을 변수에 저장할 수 있지만 복합 명령을 사용하여 동일한 작업을 수행하는 것은 허용하지 않기 때문에 약간의 혼란이 발생할 수 있습니다.

$ 'echo' 'a' ';' 'echo' 'b'  # ';' is a literal
a ; echo b

$ cmd=echo
$ "$cmd" a ; $(printf echo) b  # ';' is a metacharacter
a
b

$ cmd='echo a'  # simple command is executed properly
$ $cmd
a

$ cmd='echo a;'  # but metacharacters are not recognized
$ $cmd echo b
a; echo b

$ echo a $(echo ';') echo b  # ';' is a literal
a ; echo b

$ $(printf if) true; then echo a; fi  # parsing error
bash: syntax error near unexpected token 'then'

$ $(printf if) true; $(printf then) echo a; $(printf fi)  # looking for command "if"
bash: if: command not found
bash: then: command not found
bash: fi: command not found

eval 및 bash -c를 사용하지 않고 변수에 저장된 명령 목록에서 명령 대체를 어떻게 수행할 수 있습니까 ? (eval은 사악하기 때문입니다)

아마도 정답은 "그것도 사악하다"일 것이므로 eval을 피할 필요는 없습니다 :)

변수는 데이터를 보유합니다. 함수는 코드를 저장합니다. 변수에 코드를 넣지 마세요! ..

——기사에서 발췌명령을 변수에 넣으려고 했지만 복잡한 경우는 항상 실패합니다!

링크

유닉스 스택 교환:

Bash 쉘 명령 대체

서브셸에 변수가 표시되는 이유는 무엇입니까?

대괄호는 실제로 명령을 하위 쉘에 넣습니까?

다른:

배쉬 참조 매뉴얼

명령을 변수에 넣으려고 했지만 복잡한 경우는 항상 실패합니다!

데비안 버그 추적 시스템에서의 대화

관련 정보