시스템 정보
OS : OS X
bash : GNU bash, 버전 3.2.57 (1) -release (x86_64-apple-darwin16)
배경
타임 머신이 모든 git / nodejs 프로젝트에서 일련의 디렉토리와 파일을 제외하도록하고 싶습니다. 내 프로젝트 디렉토리가 ~/code/private/
및 ~/code/public/
에 있으므로 bash 루핑을 사용하여 tmutil
를 수행합니다.
문제
짧은 버전
계산 된 문자열 변수 k
, for- loop :
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
댓글
답변
eval
를 사용하여 또 다른 평가를 강제 할 수 있지만 실제로는 필요하지 않습니다. (그리고 eval
가 시작됩니다. 파일 이름에 $
와 같은 특수 문자가 포함 된 순간 심각한 문제가 발생합니다.) 문제는 “글 로빙이 아니라 물결표 확장입니다.
글 로빙이 발생합니다. 변수 확장 후 변수가 인용되지 않은 경우 (*) :
$ 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
또는 전체 경로를 대신 사용하는 것입니다.
(* 변수에서 glob을 확장하는 것은 일반적으로 원하는 것이 아닙니다)
다른 사항 :
여기에서와 같이 패턴을 반복 할 때 :
exclude="foo *bar" for j in $exclude ; do ...
$exclude
는 인용되지 않았으므로이 시점에서 분할되고 또한 globbed됩니다. 따라서 현재 디렉토리에 패턴과 일치하는 항목이 포함되어 있으면 다음과 같이 확장됩니다.
$ 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
iv 이전과 같은 이유로 ~
대신 id = “09073477e5”>
,$dirs
는find
명령 줄이 분할되도록하지만$pattern
는 셸에 의해 실수로 확장되지 않도록 따옴표로 묶어야합니다.
(GNU find에서 -maxdepth
를 사용하여 관심이 있다면 검색의 깊이를 제한 할 수 있다고 생각합니다. 그러나 그것은 “약간 다른 문제입니다.)”
댓글
-
find
에 대한 답변이 하나입니까? for-loop가 복잡해지고 있으므로 실제로 그 경로를 탐색하고 있습니다. 하지만 ‘ -path ‘에 문제가 있습니다. - 물결표에 대한 귀하의 정보 ‘ ~ ‘는 주요 문제에 더 직접적입니다. 최종 대본과 설명을 다른 답변에 게시하겠습니다. 하지만 여러분 께 전적인 감사를드립니다. : D
- @JohnSiu, 네, 처음 떠오른 것은 find 사용이었습니다. 정확한 필요에 따라 사용할 수도 있습니다. (또는 일부 용도의 경우 더 좋습니다.)
- @kevinarpe, 배열은 기본적으로이를위한 것이라고 생각합니다. 예,
"${array[@]}"
(따옴표 포함! )이 문서화되어 있습니다 ( 여기 및 여기 참조). 더 분할합니다. - @sixtyfive,
[abc]
는 글로브 패턴 의 표준 부분입니다. div id = “716cf7cb1f”> , ‘ 여기에서 모든 항목을 다루기 위해 ‘ 필요하다고 생각하지 않습니다. .
Answer
나중에 사용하기 위해 문자열 대신 배열로 저장할 수 있습니다. 당신이 그것을 정의 할 때 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
댓글
-
$exclude
에 와일드 카드가 어떻게 포함되어 있는지 확인하세요. ‘ d는 split + glob 연산자를 사용하기 전에 globbing을 비활성화하고$i/$j
사용하지 않도록 복원해야합니다.eval
를 사용하지만"$i"/$j
- 당신과 일까 츄 모두 좋은 대답을합니다. 그러나 그의 대답은 문제를 식별했습니다.
를 사용합니다.
Answer
@ilkkachu 답변이 주요 문제를 해결했습니다. p>
V1
그러나 exclude
에는 와일드 카드 (*)가있는 항목과없는 항목이 모두 포함되어 있으며 존재하지 않을 수도 있습니다. 결국, $i/$j
를 globbing 한 후에 추가 검사가 필요합니다. 여기에서 찾은 결과를 공유하고 있습니다.
#!/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
는 일반 문자열 연결이됩니다. 그러나 파일 / 디렉터리가 존재하지 않습니다.
/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
를 globbing하면 원래 문자열이 반환됩니다.
V2
V2는 작은 따옴표를 사용합니다. 깨끗한 결과를 얻으려면 eval
및 shopt -s nullglob
. 파일 / 디렉터리 최종 확인이 필요하지 않습니다.
#!/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
,$exclude
의 glob은 해당$exclude
확장시 (그리고eval
에 문제가 있음). ‘for i in $dir
및for l in $k
에 대해 글 로빙을 사용하도록 설정하고 싶지만. 후자 앞에는 ‘
set -f
를, 다른쪽에는set +f
를 원합니다. 보다 일반적으로 ‘ 사용하기 전에 split + glob 연산자를 조정하려고합니다. 어쨌든 ‘echo $l
에 대해 split + glob을 원하지 않으므로$l
거기에 인용되어야합니다. - @St é phaneChazelas v1 또는 v2를 언급하고 있습니까? v2의 경우
exclude
및dirs
모두 작은 따옴표 (), so no globbing till
eval`) 안에 있습니다. - 글 로빙은 목록 컨텍스트에서 인용되지 않은 변수 확장 에서 발생합니다. 즉, 변수를 인용하지 않은 채로 둡니다. 때때로 split + glob 연산자를 호출합니다. ‘ 스칼라 변수에 대한 할당이 globbing되지 않습니다.
foo=*
및foo='*'
는 동일하지만echo $foo
및echo "$foo"
는 동일하지 않습니다 (bash
, ‘는 zsh, fish 또는 rc와 같은 셸에서 수정되었습니다. 위 링크도 참조하세요. / i>이 연산자를 사용하고 싶지만 일부에서는 분할 부분 만 사용하고 일부에서는 glob 부분 만 사용합니다. - @St é phaneChazelas 감사합니다. 정보 !!! 언젠가 나를 데려 갔지만 지금은 걱정을 이해합니다. 이것은 매우 귀중합니다 !! 감사합니다 !!!
답변
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)
는 해당 $^array1/$^array2
nullglob 을 켜는 glob 한정자입니다.
확장. 그러면 glob이 일치하지 않을 때 아무 것도 확장되지 않습니다. 또한~/code/private/foo/Thumbs.db
와 같은 non-glob을 하나로 변환합니다. 즉, 특정 항목이 존재하지 않으면 포함되어 있지 않습니다.
코멘트
- 정말 좋습니다. 테스트를 거쳐 작동합니다. 그러나 zsh는 다음과 같은 경우 줄 바꿈에 더 민감 해 보입니다. 작은 따옴표를 사용합니다.
exclude
를 묶는 방식이 출력에 영향을줍니다. - @JohnSiu, 오, 그래요. ‘ 맞습니다. split + glob 및
$^array
는 빈 요소가 삭제되었는지 확인하기 위해 별도의 두 단계로 수행해야합니다 (편집 참조).zsh
의 버그가 발생하면 ‘ 메일 링리스트에 문제를 올릴 것입니다. - v2를 만들었습니다. 더 깔끔하지만 zsh 스크립트만큼 작지 않은 bash의 경우 lol
Answer
Old 게시하지만 여기에서 우연히 발견했습니다. 다른 사람들에게 도움이 될 수 있습니다. 🙂
bash에는 일종의 glob () 함수가 있습니다 … compgen -G라고합니다. 한 줄에 값을 출력하므로 작동하려면 readarray가 필요합니다.
p>
해보기 :
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
k
는 계산 된 문자열이며 sta가 필요합니다. y 루프까지 그렇게. 긴 버전을 확인해주세요.