나는 두 개의 텍스트 파일이 있는 디렉터리에 있습니다.
$ 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.txt
및 를 포함하는 두 요소 목록이 생성됩니다 test2.txt
. 마지막으로 는 두 개의 인수 와 ls
함께 호출됩니다 .test1
test2
를 사용하면 ls $(echo 'test{1,2}')
명령이 echo 'test{1,2}'
문자열을 출력합니다 test{1,2}
(최종 개행 포함). 명령 대체 결과는 문자열입니다 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
그 문제는 에 매우 특정한데 , 이는 파일 이름 확장(globbing)에서 중괄호 확장을 분리하고 다른 모든 확장보다 먼저 수행하기로 bash
결정했기 때문입니다 .bash
맨페이지 에서 bash
:
확장 순서는 다음과 같습니다: 중괄호 확장; 물결표 확장, 매개변수 및 변수 확장, 산술 확장 및 명령 대체(왼쪽에서 오른쪽으로 수행됨) 단어 분리; 및 경로 이름 확장.
귀하의 예에서는 너무 늦었을 때 bash
명령 대체()를 수행한 후에만 중괄호가 표시됩니다 .$(echo ...)
이는 경로 이름 확장(globbing) 직전에(그리고 일부는 일부로서) 중괄호 확장을 수행하는 다른 모든 쉘과 다릅니다. 여기에는 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
후자의 예 는 csh
, zsh
, 또는 에서도 동일합니다 .ksh93
mksh
fish
또한 버팀대 확장에 주목하세요.글로빙의 일환으로glob(3)
또한 라이브러리 기능(적어도 Linux 및 모든 BSD에서는)과 기타 독립적 구현(예: perl: )을 통해 사용할 수 있습니다 perl -le 'print join " ", <test{1,2}.txt>'
.
왜 그것이 다르게 수행되었는지는 bash
아마도 그 뒤에 숨겨진 이야기가 있을 것입니다. 그러나 FWIW 나는 어떤 논리적 설명도 찾을 수 없었고 모든 사후 합리화는 설득력이 없다고 생각합니다.
답변4
시도해 보세요:::
ls $(에코 테스트{1,2}\.txt)
백슬래시를 사용합니다. 이제 작동합니다. 또한 이전 포스터에서 말했듯이 따옴표를 제거하십시오. 도트는 패턴 일치를 위한 것이 아니라 문자 그대로 여기서는 마침표로 간주됩니다.