bash glob를 문자열 변수로 만드는 방법은 무엇입니까?


14

시스템 정보

운영체제 : OS X

bash : GNU bash, 버전 3.2.57 (1)-릴리스 (x86_64-apple-darwin16)

배경

Time Machine에서 모든 git / nodejs 프로젝트에서 디렉토리 및 파일 세트를 제외하고 싶습니다. 내 프로젝트 디렉토리에있는 ~/code/private/그리고 ~/code/public/내가 할 루프 bash에 사용하려고 해요 그래서 tmutil.

발행물

짧은 버전

계산 된 string 변수 가있는 경우 kfor 루프 앞이나 오른쪽으로 가져 가려면 어떻게해야합니까?

i='~/code/public/*'
j='*.launch'
k=$i/$j # $k='~/code/public/*/*.launch'

for i in $k # I need $k to glob here
do
    echo $i
done

아래 긴 버전에서는을 볼 수 k=$i/$j있습니다. 따라서 for 루프에서 문자열을 하드 코딩 할 수 없습니다.

긴 버전

#!/bin/bash
exclude='
*.launch
.classpath
.sass-cache
Thumbs.db
bower_components
build
connect.lock
coverage
dist
e2e/*.js
e2e/*.map
libpeerconnection.log
node_modules
npm-debug.log
testem.log
tmp
typings
'

dirs='
~/code/private/*
~/code/public/*
'

for i in $dirs
do
    for j in $exclude
    do
        k=$i/$j # It is correct up to this line

        for l in $k # I need it glob here
        do
            echo $l
        #   Command I want to execute
        #   tmutil addexclusion $l
        done
    done
done

산출

그들은 움켜 쥐지 않았다. 내가 원하는 것이 아닙니다.

~/code/private/*/*.launch                                                                                   
~/code/private/*/.DS_Store                                                                                  
~/code/private/*/.classpath                                                                                 
~/code/private/*/.sass-cache                                                                                
~/code/private/*/.settings                                                                                  
~/code/private/*/Thumbs.db                                                                                  
~/code/private/*/bower_components                                                                           
~/code/private/*/build                                                                                      
~/code/private/*/connect.lock                                                                               
~/code/private/*/coverage                                                                                   
~/code/private/*/dist                                                                                       
~/code/private/*/e2e/*.js                                                                                   
~/code/private/*/e2e/*.map                                                                                  
~/code/private/*/libpeerconnection.log                                                                      
~/code/private/*/node_modules                                                                               
~/code/private/*/npm-debug.log                                                                              
~/code/private/*/testem.log                                                                                 
~/code/private/*/tmp                                                                                        
~/code/private/*/typings                                                                                    
~/code/public/*/*.launch                                                                                    
~/code/public/*/.DS_Store                                                                                   
~/code/public/*/.classpath                                                                                  
~/code/public/*/.sass-cache                                                                                 
~/code/public/*/.settings                                                                                   
~/code/public/*/Thumbs.db                                                                                   
~/code/public/*/bower_components                                                                            
~/code/public/*/build                                                                                       
~/code/public/*/connect.lock                                                                                
~/code/public/*/coverage                                                                                    
~/code/public/*/dist                                                                                        
~/code/public/*/e2e/*.js                                                                                    
~/code/public/*/e2e/*.map                                                                                   
~/code/public/*/libpeerconnection.log                                                                       
~/code/public/*/node_modules                                                                                
~/code/public/*/npm-debug.log                                                                               
~/code/public/*/testem.log                                                                                  
~/code/public/*/tmp                                                                                         
~/code/public/*/typings

작은 따옴표는 Bash에서 쉘 보간을 중지하므로 변수를 큰 따옴표로 묶을 수 있습니다.
Thomas N

@ThomasN 아니오, 작동하지 않습니다. k계산 된 문자열이며 루프까지 계속 유지해야합니다. 내 긴 버전을 확인하십시오.
John Siu

@ThomasN 명확하게하기 위해 짧은 버전을 업데이트했습니다.
John Siu

답변:


18

으로 다른 평가 라운드를 강제 할 수 eval있지만 실제로는 필요하지 않습니다. (그리고 eval파일 이름에 특수 문자가 포함되는 순간 심각한 문제가 발생하기 시작합니다 $.) 문제는 globbing이 아니라 물결표 확장입니다.

변수가 인용되지 않은 경우 다음과 같이 변수 확장 후 글 로빙이 발생 합니다 (*) :

$ x="/tm*" ; echo $x
/tmp

따라서 같은 맥락에서 이것은 당신이 한 것과 비슷하며 작동합니다.

$ mkdir -p ~/public/foo/ ; touch ~/public/foo/x.launch
$ i="$HOME/public/*"; j="*.launch"; k="$i/$j"
$ echo $k
/home/foo/public/foo/x.launch

그러나 물결표와 함께하지 않습니다 :

$ i="~/public/*"; j="*.launch"; k="$i/$j"
$ echo $k
~/public/*/*.launch

이것은 Bash에 대해 명확하게 문서화 되어 있습니다.

확장 순서는 다음과 같습니다. 괄호 확장; 물결표 확장, 매개 변수 및 변수 확장, ...

변수 확장 전에 물결 확장이 발생하므로 변수 내부의 물결이 확장되지 않습니다. 쉬운 해결 방법은 $HOME대신 전체 경로 를 사용 하는 것입니다.

(* 변수에서 globs를 확장하는 것은 일반적으로 원하는 것이 아닙니다)


또 다른 한가지:

다음과 같이 패턴을 반복 할 때 :

exclude="foo *bar"
for j in $exclude ; do
    ...

$exclude인용되지 않은 것처럼 , 그것은 둘 다 나뉘었고,이 시점에서도 움켜 잡았습니다. 따라서 현재 디렉토리에 패턴과 일치하는 것이 포함되어 있으면 다음과 같이 확장됩니다.

$ i="$HOME/public/foo"
$ exclude="*.launch"
$ touch $i/real.launch
$ for j in $exclude ; do           # split and glob, no match
    echo "$i"/$j ; done
/home/foo/public/foo/real.launch

$ touch ./hello.launch
$ for j in $exclude ; do           # split and glob, matches in current dir!
    echo "$i"/$j ; done
/home/foo/public/foo/hello.launch  # not the expected result

이 문제를 해결하려면 분할 된 문자열 대신 배열 변수를 사용하십시오.

$ exclude=("*.launch")
$ exclude+=("something else")
$ for j in "${exclude[@]}" ; do echo "$i"/$j ; done
/home/foo/public/foo/real.launch
/home/foo/public/foo/something else

추가 보너스로 배열 항목에는 분할 문제없이 공백이 포함될 수도 있습니다.


find -path대상 파일의 디렉토리 레벨이 마음에 들지 않으면으로 비슷한 작업을 수행 할 수 있습니다. 예를 들어 다음으로 끝나는 경로를 찾으려면 /e2e/*.js:

$ dirs="$HOME/public $HOME/private"
$ pattern="*/e2e/*.js"
$ find $dirs -path "$pattern"
/home/foo/public/one/two/three/e2e/asdf.js

우리는 이전과 같은 이유로 $HOME대신 사용해야 하고 명령 줄 에서 인용 부호를 지정하여 분할 해야 하지만 셸에서 실수로 확장되지 않도록 인용해야합니다.~$dirsfind$pattern

( -maxdepth당신이 신경 쓰는다면 검색의 깊이를 제한하기 위해 GNU find를 가지고 놀 수 있다고 생각합니다 . 그러나 그것은 약간 다른 문제입니다.)


당신은 하나의 대답 find입니까? for-loop가 복잡 해짐에 따라 실제로 그 경로도 탐색하고 있습니다. 그러나 나는 '경로'에 어려움을 겪고 있습니다.
John Siu

물결표 '~'에 대한 귀하의 정보는 귀하에게 더 큰 문제입니다. 마지막 답변과 설명을 다른 답변에 게시하겠습니다. 그러나 당신에게 완전한 신용 : D
John Siu

@JohnSiu, 예, 찾기를 사용하는 것이 처음 떠오른 것입니다. 정확한 필요에 따라 사용 가능할 수도 있습니다. (또는 일부 용도로는 더 좋습니다.)
ilkkachu

1
@kevinarpe, 배열은 기본적으로 그 의미를 가지고 있다고 생각합니다. 그렇습니다 "${array[@]}"(따옴표와 함께!)는 요소를 더 이상 나누지 않고 구별되는 단어로 확장하기 위해 문서화되었습니다 ( herehere 참조 ).
ilkkachu

1
@sixtyfive, 음, [abc]의 표준 인 글로브 패턴 처럼 ?, 나는 여기에 그들 모두의 커버를 갈 필요는 생각하지 않습니다.
ilkkachu

4

나중에 많은 경우에 사용하기 위해 문자열 대신 배열로 저장하고 정의 할 때 globbing이 발생하도록 할 수 있습니다. 예를 들어 다음과 같습니다.

k=(~/code/public/*/*.launch)
for i in "${k[@]}"; do

또는 나중 예제에서 eval일부 문자열 이 필요합니다.

dirs=(~/code/private/* ~/code/public/*)
for i in "${dirs[@]}"; do
    for j in $exclude; do
        eval "for k in $i/$j; do tmutil addexclusion \"\$k\"; done"
    done
done

1
참고 어떻게 $exclude와일드 카드를 포함, 당신은 사용하기 전에 비활성화 대체 (globbing)에 필요할 것 분할 + 글로브의 그것에 연산자를하고 그것을 복원 $i/$j하지 를 사용 eval하지만 사용"$i"/$j
스테판 Chazelas가

당신과 일 카추 모두 좋은 대답을합니다. 그러나 그의 대답은 문제를 식별했습니다. 그래서 그에게 신용.
John Siu

2

@ ilkkachu 답변은 주요 글 로빙 문제를 해결했습니다. 그에게 완전한 신용.

V1

그러나 exclude와일드 카드 (*)를 사용하거나 사용하지 않는 항목 이 포함되어 있기 때문에 모든 항목이 존재하지 않을 수도 있기 때문에의 globbing 후 추가 검사가 필요합니다 $i/$j. 내가 찾은 결과를 여기에서 공유하고 있습니다.

#!/bin/bash
exclude="
*.launch
.DS_Store
.classpath
.sass-cache
.settings
Thumbs.db
bower_components
build
connect.lock
coverage
dist
e2e/*.js
e2e/*.map
libpeerconnection.log
node_modules
npm-debug.log
testem.log
tmp
typings
"

dirs="
$HOME/code/private/*
$HOME/code/public/*
"

# loop $dirs
for i in $dirs; do
    for j in $exclude ; do
        for k in $i/$j; do
            echo -e "$k"
            if [ -f $k ] || [ -d $k ] ; then
                # Only execute command if dir/file exist
                echo -e "\t^^^ Above file/dir exist! ^^^"
            fi
        done
    done
done

출력 설명

다음은 상황을 설명하기위한 부분 출력입니다.

/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/a.launch
    ^^^ Above file/dir exist! ^^^
/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/b.launch
    ^^^ Above file/dir exist! ^^^
/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/.DS_Store
    ^^^ Above file/dir exist! ^^^

위는 자명하다.

/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/.classpath
/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/.sass-cache
/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/.settings
/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/Thumbs.db
/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/bower_components
/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/build
/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/connect.lock
/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/coverage
/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/dist

위 항목은 제외 항목 ( $j)에 와일드 카드가 없기 때문에 $i/$j일반 문자열 연결이됩니다. 그러나 파일 / dir이 존재하지 않습니다.

/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/e2e/*.js
/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/e2e/*.map

위의 제외 항목 ( $j)에 와일드 카드가 포함되어 있지만 파일 / 디렉토리가 일치하지 않으면 $i/$j원래 문자열을 반환하는 것만으로 표시됩니다.

V2

V2는 작은 따옴표를 사용 eval하고 shopt -s nullglob깨끗한 결과를 얻을 수 있습니다. 파일 / dir 최종 점검이 필요하지 않습니다.

#!/bin/bash
exclude='
*.launch
.sass-cache
Thumbs.db
bower_components
build
connect.lock
coverage
dist
e2e/*.js
e2e/*.map
libpeerconnection.log
node_modules
npm-debug.log
testem.log
tmp
typings
'

dirs='
$HOME/code/private/*
$HOME/code/public/*
'

for i in $dirs; do
    for j in $exclude ; do
        shopt -s nullglob
        eval "k=$i/$j"
        for l in $k; do
            echo $l
        done
        shopt -u nullglob
    done
done

하나의 문제는 점에서이다 for j in $exclude,의 globs와 $exclude그 시점에서 확장 얻을 수 $exclude확장 (및 호출 eval이에하는 문제에 대한 질문입니다). 당신은 사용할 대체 (globbing) 싶어 for i in $dir하고 for l in $k,하지만하지 않는 for j in $exclude. 당신 set -f은 후자와 앞을 원할 것 set +f입니다. 보다 일반적으로 split + glob 연산자를 사용하기 전에 튜닝하고 싶을 것입니다. 어쨌든에 대해 split + glob을 원하지 echo $l않으므로 $l인용해야합니다.
Stéphane Chazelas

@ StéphaneChazelas v1 또는 v2를 참조하고 있습니까? v2의 모두 excludedirs(작은 따옴표에 ), so no globbing till eval`.
존 시우

글 로빙은 목록 컨텍스트에서 인용되지 않은 변수 확장 시 발생 합니다 (변수를 인용하지 않은 채로 두는 것)는 때때로 우리가 split + glob 연산자 라고 부릅니다 . 스칼라 변수에 대한 할당에는 글 로빙이 없습니다. foo=*foo='*'동일합니다. 그러나 echo $fooecho "$foo"되지 않습니다 (쉘이 좋아에 bash, 그것은 zsh을, 생선이나 RC와 같은 쉘에서 수정 된 것, 또한 위의 링크를 참조). 여기 않는 그 연산자를 사용하기를 원하지만 어떤 장소에서 한 스플릿 부분을, 그리고 다른 사람만을 글로브 부분입니다.
Stéphane Chazelas

@ StéphaneChazelas 정보 주셔서 감사합니다 !!! 언젠가 나를 데려 갔지만 지금은 우려를 이해합니다. 이것은 매우 귀중한 !! 감사합니다!!!
John Siu

1

zsh:

exclude='
*.launch
.classpath
.sass-cache
Thumbs.db
...
'

dirs=(
~/code/private/*
~/code/public/*
)

for f ($^dirs/${^${=~exclude}}(N)) {
  echo $f
}

${^array}string로 확장하는 것 $array[1]string $array[2]string...입니다. $=var변수 (뭔가 다른 조개는 기본적으로 할!)에 단어 분할을 수행하는 것입니다은 $~var당신이 일반적으로 그들을 원하지 않는 경우, 당신은 인용해야했을 것입니다 (기본적으로도 (변수에 뭔가 다른 쉘 글 로빙 않습니다 $f에 위 다른 껍질)).

(N)는 확장으로 인해 해당 glob 각각에 대해 nullglob 를 설정 하는 glob 한정자입니다 $^array1/$^array2. 그래야 글로브가 일치하지 않을 때 아무 것도 확장되지 않습니다. 그것은 또한 비 글로브 ~/code/private/foo/Thumbs.db를 하나로 바꾸는 것입니다. 즉, 특정 항목이 존재하지 않으면 포함되지 않습니다.


정말 좋습니다. 나는 테스트하고 작동합니다. 그러나 작은 따옴표를 사용할 때 zsh가 줄 바꿈에 더 민감한 것으로 보입니다. exclude동봉 되는 방식 이 출력에 영향을줍니다.
John Siu

@ JohnSiu, 네, 맞습니다. split + glob 인 것 같고 $^array빈 요소가 버려지는 것을 확인하기 위해 두 가지 단계로 수행해야합니다 (편집 참조). 의 버그처럼 보입니다 zsh. 메일 링리스트에서 문제를 제기하겠습니다.
Stéphane Chazelas

나는 bash 용 v2를
John Siu
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.