$()의 명령으로 {1,2}가 인쇄되지 않는 이유는 무엇입니까?

$()의 명령으로 {1,2}가 인쇄되지 않는 이유는 무엇입니까?

내가 있는 디렉터리에는 두 개의 텍스트 파일이 있습니다.

$ touch test1.txt
$ touch test2.txt

Bash를 사용하여 특정 패턴을 사용하여 파일을 나열하려고 하면 작동합니다.

$ ls test?.txt
test1.txt  test2.txt
$ ls test{1,2}.txt
test1.txt  test2.txt

그러나 다음에 포함된 명령으로 패턴이 생성되는 경우 $():

$ ls $(echo 'test?.txt')
test1.txt  test2.txt
$ ls $(echo 'test{1,2}.txt')
ls: cannot access test{1,2}.txt: No such file or directory

여기서 무슨 일이 일어나고 있는 걸까요? 이 모드가 {1,2}작동하지 않는 이유는 무엇입니까?

답변1

그것은 두 가지의 조합입니다. 첫째, 중괄호 확장은 파일 이름에 대한 패턴 일치가 아닙니다. 순수한 텍스트 대체입니다.`a[bc]d`(괄호)와 `a{b,c}d`(중괄호)의 차이점은 무엇입니까?. 둘째, 큰따옴표( ) 이외의 명령 대체 결과를 사용하면 ls $(…)전체 재분석이 아닌 패턴 일치(및 분할: "split+glob" 연산자)만 발생합니다.

를 사용하면 ls $(echo 'test?.txt')이 명령은 echo 'test?.txt'문자열 test?.txt(끝에 줄 바꿈 포함)을 출력합니다. 명령 대체는 문자열을 생성합니다 test?.txt(명령 대체는 후행 개행을 제거하므로 최종 개행 없이). 따옴표가 없는 이 대체는 토큰화를 수행하여 공백 문자가 없기 때문에 단일 문자열 목록을 생성합니다 test?.txt(보다 정확하게는 에 문자가 없습니다). 단일 요소 목록의 각 요소는 조건부 와일드카드 확장을 거치며 $IFS문자열에 와일드카드가 있으므로 와일드카드 확장이 발생합니다. ?패턴이 test?.txt하나 이상의 파일 이름과 일치하므로 목록 요소는 패턴과 일치하는 파일 이름 목록으로 대체되어 test?.txt및 를 포함하는 두 요소 목록이 생성됩니다. 마지막으로 두 개의 매개변수와 을 사용하여 호출이 이루어집니다.test1.txttest2.txtlstest1test2

를 사용하면 ls $(echo 'test{1,2}')이 명령은 echo 'test{1,2}'문자열 test{1,2}(끝에 줄 바꿈 포함)을 출력합니다. 명령 대체 결과는 string 입니다 test{1,2}. 따옴표가 없는 이 대체는 토큰화를 수행하여 개별 문자열 목록을 생성합니다 test{1,2}. 단일 요소 목록의 각 요소는 조건부 와일드카드 확장을 거치지만 문자열에 와일드카드가 없으므로 아무 작업도 수행되지 않습니다(요소는 그대로 유지됩니다). ls단일 인수로 호출 됩니다 test{1,2}.

비교를 위해 다음과 같은 일이 발생합니다 ls $(echo test{1,2}). 이 명령은 echo test{1,2}문자열 test1 test2(끝에 줄 바꿈 포함)을 출력합니다. 명령 대체 결과는 문자열입니다 test1 test2(마지막 개행 문자 없음). 따옴표가 없는 이 대체는 단어 분할을 수행하여 두 개의 문자열 test1합계를 생성합니다 test2. 그런 다음 두 문자열 모두 와일드카드를 포함하지 않으므로 단독으로 남겨두므로 ls두 인수 test1및 를 사용하여 호출이 이루어집니다 test2.

답변2

확장 순서는 중괄호 확장, 인수 및 변수 확장, 산술 확장, 명령 대체(왼쪽에서 오른쪽으로 수행)입니다.

명령 대체 후에는 중괄호 확장이 발생하지 않습니다. eval을 사용하여 또 다른 확장을 강제할 수 있습니다.

eval echo $(echo '{1,2}lala')

결과는 다음과 같습니다

1lala 2lala

답변3

이 질문은 파일 이름 확장(와일드카드)에서 중괄호 확장을 분리하고 다른 모든 확장보다 먼저 수행하기로 bash결정했기 때문에 대상이 되었습니다.bash

bash맨페이지 에서 :

확장 순서는 중괄호 확장, 매개변수 및 변수 확장, 산술 확장, 명령 대체(왼쪽에서 오른쪽으로 수행됨)입니다.

귀하의 예에서는 bash명령 대체( )를 수행 $(echo ...)한 후에만 중괄호가 표시되지만 너무 늦었습니다.

이는 경로 이름 확장(전역) 이전에(그리고 일부는 일부로서) 중괄호 확장을 수행하는 다른 모든 쉘과 다릅니다. 여기 csh에는 브래킷 확장이 처음 발명된 곳이 포함되지만 이에 국한되지는 않습니다 .

$ csh -c 'ls `echo "test{1,2}.txt"`'
test1.txt test2.txt
$ ksh -c 'ls $(echo "test{1,2}.txt")'
test1.txt  test2.txt

$ var=nope var1=one var2=two bash -c 'echo $var{1,2}'
one two
$ var=nope var1=one var2=two csh -c 'echo $var{1,2}'
nope1 nope2

cshzsh후자의 예는 , 또는 의 예 와 동일합니다 ksh93.mkshfish

또한 버팀대 확장에 주의하세요.와일드카드의 일부로glob(3)또한 라이브러리 기능(적어도 Linux 및 모든 BSD에서는)과 기타 독립적 구현(예: perl:)을 통해 사용할 수 있습니다 perl -le 'print join " ", <test{1,2}.txt>'.

왜 이것이 다르게 수행되었는지 뒤에는 이야기가 있을 수 있지만 bashFWIW 논리적 설명을 찾을 수 없으며 모든 사후 합리화가 설득력이 없다고 생각합니다.

답변4

시도해 보세요:::

ls $(에코 테스트{1,2}\.txt)

백슬래시를 사용하세요. 이제 잘 작동합니다. 또한 이전 포스터에서 말했듯이 따옴표를 제거했습니다. 여기서 점은 패턴을 일치시키는 데 사용되지 않고 문자 그대로 마침표로 간주됩니다.

관련 정보