$ echo $(echo x; echo y)
x y
$ a='echo x; echo y'
$ echo $($a) # expect 'x y'
x; echo y
- 명령 대체가 왜 이렇게 작동합니까?
- 및를 사용하지 않고
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개의 매개 변수 , 즉 기본적으로 가지고 있는 명령을 사용하여 이를 실행합니다 .)echo
a;
echo
b
$(...)
echo
a;
echo
b
echo $('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. 정의
명령을 읽고 실행할 때 쉘이 수행하는 작업에 대한 간략한 설명입니다. 기본적으로 쉘은 다음을 수행합니다.
- 입력을 읽으십시오.
- 인용 규칙을 준수하여 입력을 단어와 연산자로 나눕니다. 이러한 태그는 메타 문자로 구분됩니다.
- 구문 분석 토큰단순 명령어와 복합 명령어로 구분됩니다.
- 다양한 수행쉘 확장.
- 필요한 리디렉션을 수행합니다.
- 주문 실행
——본 항목에서 발췌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
, then
등 while
)는 확장의 결과가 될 수 없지만 프로그램 이름, 내장 명령, 함수 및 별칭은 가능합니다. 간단한 명령을 변수에 저장할 수 있지만 복합 명령을 사용하여 동일한 작업을 수행하는 것은 허용하지 않기 때문에 약간의 혼란이 발생할 수 있습니다.
$ '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을 피할 필요는 없습니다 :)
변수는 데이터를 보유합니다. 함수는 코드를 저장합니다. 변수에 코드를 넣지 마세요! ..
——기사에서 발췌명령을 변수에 넣으려고 했지만 복잡한 경우는 항상 실패합니다!