Bash의 중첩 괄호 확장 미스터리


19

이:

$ echo {{a..c},{1..3}}

이것을 생성합니다 :

a b c 1 2 3

어느 것이 좋지만 설명하기가 어렵습니다.

$ echo {a..c},{1..3}

준다

a,1 a,2 a,3 b,1 b,2 b,3 c,1 c,2 c,3

이것은 어딘가에 기록되어 있습니까? 강타 참조 (그것을 사용하는 예제있는 경우에도)을 언급하지 않습니다.

답변:


18

글쎄, 한 번에 한 층씩 풀려났다.

X{{a..c},{1..3}}Y

로 확대 된 것으로 설명되어 있습니다 X{a..c}Y X{1..3}Y(의 그 X{A,B}Y확대 XA XBA{a..c}B{1..3}), 자체로 확대되고 문서화 XaY XbY XcY X1Y X2Y X3Y.

문서화 할 가치가있는 것은 중첩 될 수 있다는 것입니다 (예를 들어 첫 번째 }는 첫 번째 가 닫히지 않습니다 {).

쉘이 각 닫힘 에 차례로 작용하는 것처럼 내부 괄호를 먼저 해결하도록 선택할 수 있다고 가정 }합니다.

  1. X{{a..c},{1..3}}
  2. X{a,{1..3}}Y X{b,{1..3}}Y X{c,{1..3}}Y

    (즉되는 A{a..c}B확장 AaB AbB AcBA이다 X{하고 B있다 ,{1..3}Y)

  3. X{a,1}Y X{a,2}Y X{a,3}Y X{b,1}Y X{b,2}Y X{b,3}Y X{c,1}Y X{c,2}Y X{c,3}Y

  4. XaY X1Y XaY Xa2...

하지만 난 찾을 수없는 그 (예를 들어 설명에서 케빈의 예 참조),이 여전히 확장이 완료 될 순서에 일부 모호함, 그리고 그 방법이 아니다 것, 특히보다 직관적으로도 유용 csh중괄호를 도입하는 것이 (쉘 70 년대 후반에 확장이 이루어졌고, {1..3}이후에 (1995 년) zsh, {a..c}이후에 (2004 년에) 양식이 bash만들어졌다.

참고 것을 csh(처음부터의 참조 2BSD (1979) 매뉴얼 페이지는 중괄호 확장은 중첩 될 수 있다는 사실 문서를했다)하지만 명시 적으로 확장 될 것입니다 방법 중첩 된 중괄호 확장 말하지 않았다. 그러나 1979 년 csh코드 를보고 어떻게 수행되었는지 확인할 수 있습니다. 중첩을 실제로 명시 적으로 처리하는 방법과 외부 괄호에서 시작하여 어떻게 해결되는지 확인하십시오.

어쨌든, 나는 확장이 어떻게 {a..c},{1..3}베어링을 가질 수 있는지 알지 못한다 . 거기에서는 ,중괄호 확장의 연산자가 아니며 (중괄호 안에 있지 않으므로) 일반 문자처럼 취급됩니다.


외부 괄호가 내부 괄호보다 먼저 해결되어야한다는 것이 이상하게 보입니다.
Hauke ​​Laging 2013

@ stéphane-chazelas이 표현을 파싱 할 수있는 두 가지 확실한 방법이 있습니다. 왜 다른 방법이 아닌 한 가지 방법으로 구문 분석됩니까? 귀하의 의견은 설명을하지 않는 것 같습니다.
igal

따라서 그 설명은 의미가 있지만, 이것이 "확장 된 것으로 문서화되어 있다면 ..." URL이 있습니까?
xenoid

@xenoid 업데이트 된 솔루션을 참조하십시오.
igal

1
@ (모두) : 확장을 고려하십시오 /dev/{h,s}d{a..d}{1..4,}. 이제 그것도 포함하도록 확장한다고 가정 /dev/null하고 /dev/zero. 괄호 확장이 내부에서 제대로 작동하면 확장이 실제로 성가신 것입니다. 그러나 외부에서 작동하기 때문에 매우 사소합니다./dev/{null,zero,{h,s}d{a..d}{1..4,}}
Kevin

7

여기에 짧은 대답이 있습니다. 첫 번째 표현식에서 쉼표는 구분 기호로 사용되므로 중괄호 확장은 두 개의 중첩 된 하위 표현식의 연결 일뿐입니다. 두 번째 표현식에서 쉼표 자체는 단일 문자 하위 표현식으로 처리되므로 제품 표현식 형성됩니다.

누락 된 것은 괄호 확장이 수행되는 방법에 대한 정의였습니다. 다음은 세 가지 참조입니다.

더 자세한 설명은 다음과 같습니다.


이 표현식의 결과를 비교했습니다.

$ echo {{a..c},{1..3}}
a b c 1 2 3

이 표현의 결과에 :

$ echo {a..c},{1..3}
a,1 a,2 a,3 b,1 b,2 b,3 c,1 c,2 c,3

당신은 이것이 설명하기 어렵다, 즉 이것은 반 직관적이라고 말합니다. 빠진 것은 중괄호 확장이 처리되는 방식에 대한 공식적인 정의입니다. 당신은주의 배쉬 매뉴얼 전체 정의를 제공하지 않습니다.

조금 검색했지만 누락 된 (완전하고 공식적인) 정의도 찾을 수 없었습니다. 그래서 나는 소스 코드로 갔다.

소스에는 몇 가지 유용한 주석이 있습니다. 첫 번째는 괄호 확장 알고리즘에 대한 고급 개요입니다.

Basic idea:

Segregate the text into 3 sections: preamble (stuff before an open brace),
postamble (stuff after the matching close brace) and amble (stuff after
preamble, and before postamble).  Expand amble, and then tack on the
expansions to preamble.  Expand postamble, and tack on the expansions to
the result so far.

따라서 중괄호 확장 토큰의 형식은 다음과 같습니다.

<PREAMBLE><AMBLE><POSTAMBLE>

확장의 주요 진입 점 brace_expand은 다음과 같이 설명되는 함수입니다 .

Return an array of strings; the brace expansion of TEXT.

따라서 brace_expand함수는 중괄호 확장 표현식을 나타내는 문자열을 가져와 확장 된 문자열의 배열을 리턴합니다.

이 두 관측 값을 결합하면 앰블이 문자열 목록으로 확장되고 각 문자열이 프리앰블에 연결됩니다. 그 후, 포스트 앰블은 스트링리스트로 확장되고, 포스트 앰블리스트 내의 각 스트링은 프리앰블 / 앰블리스트 내의 각 스트링에 연결된다 (즉, 두리스트의 곱이 형성된다). 그러나 이것은 앰블과 포스트 앰블이 어떻게 처리되는지는 설명하지 않았습니다. 운 좋게도 그것을 설명하는 의견이 있습니다. 앰블은 expand_amble정의 앞에 다음 주석이 붙은 함수로 처리됩니다 .

Expand the text found inside of braces.  We simply try to split the
text at BRACE_ARG_SEPARATORs into separate strings.  We then brace
expand each slot which needs it, until there are no more slots which
need it.

코드의 다른 곳에서 BRACE_ARG_SEPARATOR가 쉼표로 정의되어 있음을 알 수 있습니다. 이를 통해 앰블이 쉼표로 구분 된 문자열 목록이며 일부는 중괄호 확장 표현식 일 수도 있습니다. 그런 다음이 문자열은 단일 배열을 형성합니다. 마지막으로 after expand_amble라는 brace_expand함수가 postamble에서 재귀 적으로 호출되는 것을 볼 수 있습니다 . 이것은 우리에게 알고리즘에 대한 완전한 설명을 제공합니다.

이 발견을 뒷받침하는 다른 (비공식) 참고 문헌이 있습니다.

참고로 Bash Hackers Wiki를 확인하십시오 . 결합 및 중첩 에 대한 섹션은 문제를 다루지 않지만 페이지는 중괄호 확장의 구문 / 문법을 제공하므로 귀하의 질문에 대답합니다. 구문은 다음과 같은 패턴으로 제공됩니다.

{string1,string2,...,stringN}

{<START>..<END>}

<PREAMBLE>{........}

{........}<POSTSCRIPT>

<PREAMBLE>{........}<POSTSCRIPT>

구문 분석은 다음과 같이 설명됩니다.

중괄호 확장은 임의의 문자열을 생성하는 데 사용됩니다. 지정된 문자열은 선택적인 주변 프리앰블 및 포스트 스크립트와 가능한 모든 조합 을 생성하는 데 사용됩니다 .

다른 참고 자료는 Bash Beginner 's Guide를 참조 하십시오.

Brace expansion is a mechanism by which arbitrary strings may be generated. Patterns to be brace-expanded take the form of an optional PREAMBLE, followed by a series of comma-separated strings between a pair of braces, followed by an optional POSTSCRIPT. The preamble is prefixed to each string contained within the braces, and the postscript is then appended to each resulting string, expanding left to right.

따라서 중괄호 확장 표현식을 구문 분석하기 위해 왼쪽에서 오른쪽으로 이동하여 각 표현식을 확장하고 연속 문자열 (문자열 연결 조작과 관련하여)을 형성합니다.

이제 첫 표현을 보자.

{{a..c},{1..3}}

Bash Hacker 's Wiki의 언어로 첫 번째 형식과 일치합니다.

{string1,string2,...,stringN}

여기서 N=2, string1={a..c}string2={1..3}- 내측 브레이스 확장 먼저 수행되고 그들 각각의 형식 인 {<START>..<END>}. 또는 이것이 앰블 (프리앰블 또는 포스트 앰블 없음)만으로 구성되는 중괄호 확장 표현식이라고 말할 수 있습니다. 앰블은 쉼표로 구분 된 목록이므로 한 번에 한 슬롯 씩 목록을 살펴보고 필요한 경우 추가 확장을 수행합니다. 인접한 표현이 없기 때문에 곱이 생성되지 않습니다 (쉼표는 구분 기호로 사용됨).

다음으로 두 번째 표현을 봅시다 :

{a..c},{1..3}

Bash Hacker 's Wiki의 언어에서이 표현은 다음 형식과 일치합니다.

{........}<POSTSCRIPT>

여기서 포스트 스크립트는 하위 표현식 ,{1..3}입니다. 또는이 표현식에 amble ( {a..c}) 및 postamble ( ,{1..3}) 이 있다고 말할 수 있습니다 . 앰블은 목록으로 확장 된 a b c다음 각 앰블은 포스트 앰블 확장에서 각 문자열과 연결됩니다. postamble은 재귀 적으로 처리됩니다 : preamble ,과 amble이 {1..3}있습니다. 이 목록으로 확장됩니다 ,1 ,2 ,3. 두 목록 a b c,1 ,2 ,3다음 제품 목록을 형성하도록 결합된다 a,1 a,2 a,3 b,1 b,2 b,3 c,1 c,2 c,3.

괄호 "[]"는 배열을 나타내고 "+"는 배열 연결을 나타내고 "*"는 데카르트 곱을 나타냅니다 (연결과 관련하여).

첫 번째 표현식이 확장되는 방법은 다음과 같습니다 (한 줄에 한 단계 씩).

{{a..c},{1..3}}
{a..c} + {1..3}
[a b c] + [1 2 3]
a b c 1 2 3

그리고 다음은 두 번째 표현이 확장 된 방법입니다.

{a..c},{1..3}
{a..c} * ,{1..3}
[a b c] * [,1 ,2 ,3]
a,1 a,2 a,3 b,1 b,2 b,3 c,1 c,2 c,3

2

내 이해는 이것입니다 :

안쪽 괄호가 항상 (항상처럼) 해결되어

{{a..c},{1..3}}

으로

{a,b,c,1,2,3}

,가 중괄호 안에 있기 때문에 중괄호 요소 만 분리합니다.

그러나

{a..c},{1..3}

,중괄호 안에 있지 않습니다. 즉, 양쪽에 중괄호 순열을 일으키는 일반적인 문자입니다.


이렇게 {a..c}하려면 다음 중 하나를 결의 a,b,c또는 a b c습도와 다우 존스에 따라? 산뜻한.
kubanczyk 2011

약간 혼란스러워 보입니다. 경우 {{a..c},{1..3}}와 동일합니다 {a,b,c,1,2,3}, 다음 안됩니다 {{a..c}.{1..3}}과 동일 {a,b,c.1,2,3}? 물론 그렇지 않습니다.
ilkkachu

@ilkkachu 왜 같은가요? ,괄호 확장 분리 문자 .입니다. 왜 평범한 인물이 특별한 것과 같은 결과를 가져와야합니까? c.1가새 요소입니다. 그러나에 왼쪽과 오른쪽에 중괄호 확장을위한 앵커입니다. 으로 외부 중괄호 중괄호 확장에 사용되는 자신의 내용은 중괄호 확장 형식을 가지고 있기 때문에 그 내용이 그 형식이 없기 때문에 그들이 아니다. {a..c}.{1..3}.,.
Hauke ​​Laging

@HaukeLaging, 글쎄,로 {{a..c},{1..3}}바뀌면 , 와 {a,b,c,1,2,3}사이에 쉼표가 나타납니다 . 왜 같은 방식으로 나타나지 않습니까? @kubanczyk의 의견은 똑같이 쉼표가 표시되면 확장이 쉼표를 생성하는 시점과 그렇지 않은 시점을 어떻게 알 수 있습니까? 물론 그 자체로는 쉼표를 생성하지 않고 단어 목록을 생성한다는 것입니다. 그래서 아무것도로 전환하지 도착 또는 . abc{a..c}.{1..3}{a,b,c,1,2,3}{a,b,c.1,2,3}
ilkkachu

@kubanczyk 당신은 당신이 이해하지 못하는 답변을 재미있게해서는 안됩니다.
Hauke ​​Laging
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.