시스템 메시지

시스템 메시지

시스템 메시지

운영 체제: OS X

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

배경

타임머신이 내 모든 git/nodejs 프로젝트에서 일련의 디렉터리와 파일을 제외하길 원합니다. 내 프로젝트 디렉토리는 다음 위치에 있습니다.~/code/private/그리고~/code/public/그래서 bash 루프를 사용하여 실행해 보았습니다 tmutil.

질문

간결한 버전

내가 가지고 있었다면계획된문자열 변수 k, for 루프 안이나 앞에 와일드카드를 만드는 방법:

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

답변1

강제를 사용하여 또 다른 평가를 수행할 수도 있지만 eval실제로는 그럴 필요가 없습니다. ( eval파일 이름에 와 같은 특수 문자가 포함되면 심각한 문제가 발생하기 시작합니다 $.) 문제는 와일드카드가 아니라 물결표 확장입니다.

와일드카드 발생뒤쪽에변수 확장, 변수가 인용되지 않은 경우 다음과 같습니다 (*) .

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

따옴표가 없는 확장에서 발생하는 또 다른 일은 토큰화입니다. IFS이는 문제의 패턴에 문자(보통 공백)가 포함된 경우 문제가 됩니다. 이 문제를 방지하려면 IFS단어 분리를 빈 문자열로 설정하여 비활성화해야 합니다.

다시 말씀드리지만, 이는 여러분이 한 것과 유사하며 작동합니다.

$ IFS=
$ 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

이것은명확하게 문서화큰 타격의 경우:

확장 순서는 중괄호 확장, 매개변수 및 변수 확장,...입니다.

물결표 확장은 변수 확장 전에 발생하므로 변수 내의 물결표는 확장되지 않습니다. 간단한 수정 방법은 $HOME대신 또는 전체 경로를 사용하는 것입니다.

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


또 다른 한가지:

다음과 같이 패턴을 반복하면:

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

$exclude인용되지 않았으므로 분할되고 와일드카드 로 표시됩니다 . 따라서 현재 디렉터리에 이 패턴과 일치하는 항목이 포함되어 있으면 다음과 같이 확장됩니다.

$ IFS=
$ i="$HOME/public/foo"
$ exclude="*.launch"
$ touch $i/real.launch
$ for j in $exclude ; do           # glob, no match
    printf "%s\n" "$i"/$j ; done
/home/foo/public/foo/real.launch

$ touch ./hello.launch
$ for j in $exclude ; do           # glob, matches in current dir!
    printf "%s\n" "$i"/$j ; done
/home/foo/public/foo/hello.launch  # not the expected result

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

$ IFS=
$ exclude=("*.launch")
$ exclude+=("*.not this")
$ for j in "${exclude[@]}" ; do printf "%s\n" "$i"/$j ; done
/home/foo/public/foo/real.launch
/home/foo/public/foo/some file.not this

그러나 패턴이 아무것도 일치하지 않으면 기본적으로 그대로 유지됩니다. 따라서 디렉토리가 비어 있으면 .../*.launch인쇄됩니다.


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를 사용하여 검색 깊이를 제한할 수 있다고 생각 하지만 그것은 다른 질문입니다.)

답변2

나중에 여러 상황에서 사용하기 위해 문자열 대신 배열로 저장할 수 있으며 정의할 때 글로빙이 발생하도록 할 수 있습니다. 귀하의 경우 예를 들면 다음과 같습니다.

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

답변3

그리고 zsh:

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

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

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

${^array}stringis로 확장됩니다 $array[1]string $array[2]string.... $=varis는 변수(다른 쉘에서는 기본적으로 이 작업을 수행합니다!) 및 $~varglobbing 변수(다른 쉘에서는 기본적으로 이 작업을 수행합니다(일반적으로 이 작업을 수행하지 않으려면 $f위의 다른 쉘에 대한 참조)를 수행 해야 합니다. )).

(N)전역 한정자가 켜져 있나요?빈 공이 확장팩으로 생산된 각 구체에 대해 $^array1/$^array2. 이로 인해 일치하지 않는 경우 glob이 비어 있게 확장됩니다. 이는 또한 non-glob을 1로 바꾸는 경우도 발생합니다 ~/code/private/foo/Thumbs.db. 즉, 해당 특정 항목이 존재하지 않으면 포함되지 않습니다.

답변4

오래된 게시물이지만 여기에서 우연히 발견하여 다른 사람들에게 도움이 될 것이라고 생각했습니다 :)

bash에는 glob()과 유사한 함수가 있습니다. 한 줄에 하나의 값을 출력하는 compgen -G라고 불리므로 작동하려면 readarray가 필요합니다.

이 시도:

i='~/code/public/*'
j='*.launch'
k=$i/$j # $k='~/code/public/*/*.launch'
readarray -t files < <(compgen -G "$k") # $k is globbed here
for i in "${files[@]}"
do
    echo "Found -$i-"
done

관련 정보