POSIX Shell Grammar에서 여는 중괄호 뒤에 중괄호 명령 그룹에 공백이 필요한 이유는 무엇입니까?


10

TL; DR : POSIX 괄호 그룹에 {예약어 뒤에 공백이 필요 하지만 서브 쉘에 예약어 뒤에 공백이 필요한 이유는 무엇 (입니까?

POSIX 쉘 문법은 다음과 같이 중괄호 그룹과 서브 쉘을 정의합니다.

brace_group      : Lbrace compound_list Rbrace

subshell         : '(' compound_list ')'

이제 문자 그대로 읽으면 공백이 중요합니다. 이것은 다음과 같이 여는 중괄호와 괄호를 나타내는 공간이 있어야 함을 의미합니다.

{ echo hello world; }

( echo hello world )

이것은 복합 명령 정의 와도 일치 합니다 .

이러한 복합 명령 각각에는 시작 부분에 예약어 또는 제어 연산자가 있고 끝에 해당 터미네이터 예약어 또는 연산자가 있습니다.

그러나하지 않습니다 어떤 의미가 이유 (list)( list )잘 (공간 후에는 그 일 (즉, 그러나 중괄호 확장은 선도적 인 공간을 가지고 있어야 필요하지 않습니다) {echo hello;}작동하지 않을 것입니다.

물론 셸 단어로 취급되는 예약어는 필드 분할 개념과 정렬하기 위해 공백이 필요하다는 것은 당연 하지만 정의 자체는 공백을 언급하지 않습니다. 또한 복합 명령의 POSIX 정의에 의해 예약어로 간주되고 예약어 인 경우 {, (예약어 뒤에 공백 문자와 관련하여 다르게 취급되는 이유는 무엇입니까? 이제 ksh (1) 매뉴얼의 상태는 다음과 같습니다.

일련의 문자 인 단어는 따옴표없는 공백 문자 (공백, 탭 및 줄 바꿈) 또는 메타 문자 (<,>, |,;, &, (및))로 구분됩니다.

다시 말해, ksh는 (단어 구분 기호로 인식 되며, 첫 번째 단어는 명령 또는 변수 할당입니다. 그러나 POSIX (는 메타 문자 로 언급되지 않습니다 . POSIX 문법에 관한 한 가능한 설명 {은 "토큰"으로 간주되는데, 여기에 (나열되지 않습니다.

/* These are reserved words, not operator tokens, and are
   recognized when reserved words are recognized. */


%token  Lbrace    Rbrace    Bang
/*      '{'       '}'       '!'   */

이 불일치에 대한 정확한 추론은 무엇입니까?

허용되는 답변 메모 :

  • 에 허용 체크 이전 이삭의 대답 은 q를 제공하기 때문에 표준 형성 uote 직접 내 질문을 해결 자체를 :

    예를 들어, '('및 ')'는 제어 연산자이므로 <space>(list)에는 필요 하지 않습니다 . 그러나 '{'및 '}'은 {list;}에서 예약어이므로이 경우 선행 <space><semicolon>필수입니다.

  • Kusalananda의 답변 수락 . Kusalananda의 답변은 대부분 비공식적이고 직관적 인 관점에서 필자에게 필요한 것을 설명합니다. 그것은 {예약어이며 지적 (입니다. Michael Homer는 또한 의견에서 복합 명령 정의 상태 (강조 추가)에 대해서도 동일하게 언급했습니다.

    이러한 각 복합 명령에는 처음에 예약어 또는 제어 연산자 가 있습니다.

  • {유사 예약 된 단어로 정의 for또는 while쉘 문법에 나열된, (질문의 마지막 코드 블록을 참조)

  • 섹션 2.9 상태 (강조 추가) :

    특히, 표현에는 토큰 이 필요하지 않은 (토큰 중 하나가 연산자 인 경우)에서 토큰 사이의 간격이 포함됩니다 .<blank>

  • 표준은 명시 적으로 (연산자로 정의되지 않지만 연산자 (라고합니다. 구체적으로, 섹션 2.9.2

    파이프 라인이 예약어! 그리고 command1은 서브 쉘 명령이며, 응용 프로그램은 (command1의 시작 부분에있는 연산자가 하나 이상의 문자로!와 분리되어 있는지 확인해야합니다.) 예약어!

  • Digital Trauma의 스택 오버플로관한 질문은 예약어에 관한 2.4 절을 지적합니다.

    이 인식은 문자를 인용하지 않고 단어를 다음과 같이 사용하는 경우에만 발생합니다.

    -명령의 첫 단어

  • Kusalananda의 답변에서 언급했듯이 "POSIX 문법에 표시된 공백은 쉘 입력 데이터에 있어야하는 공백이 아니라 문법 자체를 표시하는 방법 일뿐입니다. 중괄호는 예약어입니다. 마이클 호머 (Michael Homer )가 논평에서 언급 한대로 "공백이 중요하다면 공간을 생산 에 포함시켜야합니다. "

경우 폐쇄.


3
공간이 그 자체로 중요한 경우 프로덕션에 나열해야합니다.
Michael Homer

2
"또한, 경우 {(화합물 명령 POSIX 정의에 의해 모두 고려 예약어"참조 "이러한 각 복합 명령에는 처음에 예약어 또는 제어 연산자 가 있습니다."
Michael Homer

2
@SergiyKolodyazhnyy 나는 공간이 중요하다면 문법에 명시적인 공백 문자 ( ' ') 를 포함해야한다고 생각 합니다. 대신 공백은 토큰이 단어라는 의미로 암시됩니다.
Kusalananda

2
토큰 클래스의 사양 정의는 가장 어색합니다. 전체 문법은 꽤 끔찍하며 스펙은 문장 내 (때로는 암묵적으로!), 문법 앞의 산문 규칙 및 문법 자체로 사물을 정의하는 내용을 혼합합니다. 아직 답을 모르고 거꾸로 작업하는 경우 이해할 수 없습니다. 어휘 규칙은 토큰에 포함 된 내용을 설명하기보다는 새로운 토큰을 시작하는 내용에 따라 거꾸로 정의됩니다. 그것은 단지 엉망입니다.
Michael Homer

1
공식 문법의 @Sergiy, 생산 (또는 생산 규칙)은 다른 것에서 무언가를 생성하는 방법을 설명합니다. 참조 en.wikipedia.org/wiki/Production_%28computer_science%29 그래서는 command : simple_command | compound_command | compound_command redirect_list | function_definition ;당신이 명령을 가질 수있는, 그것은 간단한 명령, 화합물 명령 또는 리디렉션 화합물 명령 또는 함수 정의 중 하나가 될 수 있다고하는 생산이다.
muru

답변:


6

그것은 쉘이 줄을 토큰으로 나누는 방식의 한계입니다.

쉘은 입력 파일에서 을 읽고 섹션 2 "쉘 소개"에 따라 행을 단어연산자 로 변환합니다 .

  1. 쉘은 입력을 토큰으로 나눕니다 : 단어와 연산자

{는 예약어입니다

일부 단어는 예약어입니다

예약어는 쉘에 특별한 의미가있는 단어입니다. 다음 단어는 예약어로 인식됩니다.

! { } case do done elif else esac fi for if in then until while

단어로 인식 되려면 단어를 구분 해야합니다 .

예약어는 구분 된 경우에만 인식됩니다 ...

대부분 공백 (7 점) 과 연산자로 표시됩니다.

  1. 현재 문자가 인용되지 않은 <공백>이면, 이전 문자를 포함하는 토큰이 분리되고 현재 문자는 폐기됩니다.

(연산자입니다

운영자는 스스로 대기합니다 .

반면 연산자는 구분자입니다.

어디에서 "사업자"중 하나입니다 :

3.260 연산자

쉘 명령 언어에서 제어 연산자 또는 리디렉션 연산자

리디렉션 연산자는 다음 같습니다.

리디렉션 연산자

쉘 명령 언어에서 리디렉션 기능을 수행하는 토큰. 다음 기호 중 하나입니다.

<     >     >|     <<     >>     <&     >&     <<-     <>

제어 연산자는 다음 같습니다.

3.113 제어 연산자

쉘 명령 언어에서 제어 기능을 수행하는 토큰. 다음 기호 중 하나입니다.

&   &&   (   )   ;   ;;   newline   |   ||

결론

따라서 '('및 ')'는 제어 연산자이고 '{' '}'은 예약어입니다.

그리고 당신의 질문에 대한 정확한 설명은 사양 안에 있습니다 :

예를 들어, '('및 ')'는 제어 연산자이므로 (목록)에 <space>가 필요하지 않습니다. 그러나 '{'및 '}'은 {list;}에서 예약어이므로이 경우 선행 <space> 및 <semicolon>이 필요합니다.

다음에 공백 (또는 다른 구분 기호)이 필요한 이유를 정확하게 설명합니다 {.

유효합니다 :

{ echo yes;}

이대로 :

{(echo yes);}

이:

{(echo yes)}

또는 이것조차도 :

{>/dev/tty echo yes;}

글쎄, 마지막 인용문은 정확하게 발견되었습니다! +1했습니다. 질문과 답변을 지금 검토해야합니다.
Sergiy Kolodyazhnyy

13

중괄호 괄호 차이는 괄호 (그리고 그이다 !)처럼 예약어 for, if, then괄호 제어 연산자 동안 등. 단어는 공백으로 구분해야합니다.

이것은 당신이 할 수없는 것처럼

foriin*; do

당신은 가질 수 없습니다

{somecommand;} >file

또는

if !somecommand; then

POSIX 문법에 표시된 공백은 쉘 입력 데이터에 있어야 할 공백이 아니라 문법 자체를 표시하는 방법입니다. 괄호 소유하고 있다는 사실이다 단어를 서브 쉘의 괄호 안하면서 그들이 공백으로 둘러싸인 할 필요가 있음을 의미한다 그.


1
글쎄, 이것은 거의 대답하는 것처럼 보이며 "특히 표현에는 <blank>가 필요하지 않은 곳 (토큰 중 하나가 연산자 일 때)에 토큰 사이의 간격이 포함됩니다"라고 말합니다. 단 하나의 질문 : 표준 (은 연산자로 어디에서 정의 합니까? 그것은 문법 섹션 적어도에없는
세르지 Kolodyazhnyy

@MichaelHomer 아, "제어 오퍼레이터"처럼 ;. 고마워
Kusalananda

제어 연산자는 매뉴얼 페이지 맨 위 정의에 나열되어 있습니다. 우리는 둘 다 서브 쉘을 포함하는 ()것과 같은 제어 연산자로 볼 수 있습니다 |. 그리고 { }현재 쉘에서 작동하고 서브 쉘을 포함 할 수 없다.
glenn jackman 2016 년

@Kusalananda 그것을 발견, 섹션 2.9.2 : "파이프 라인이 예약어!로 시작하고 command1이 서브 쉘 명령 인 경우, 응용 프로그램은 (command1 시작 부분의 연산자가! blank> characters. 예약어!의 동작 바로 뒤에 (연산자가 지정되어 있지 않습니다. "명확한 정의는 아니지만 표준에서는이를 (operator 라고합니다.
Sergiy Kolodyazhnyy 2018 년

@glennjackman 파이프 라인에 서브 쉘이 포함되어있는 것은 사실이지만 적절한 정의 유형은 아닙니다. 표준은 일부 구현에서 파이프 라인이 현재 쉘 실행 환경에서 실행되는 것이 좋다고 언급합니다 (어제 텍스트를 보았으므로 지금 표준을 찾았 기 때문에 표준에 있음을 알고 있습니다). 그러나 귀하의 제안은 위에서 언급 한 인용문을 찾도록 지시했습니다. 최소한 표준은 명시 적 으로 하나는 정의 하지 않지만 연산자 라고 부릅니다.
Sergiy Kolodyazhnyy 2018 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.