글로브가 bash에서 일치하는지 테스트하십시오.


223

나는 하나의 파일의 존재를 확인하려는 경우, 나는 그것을 사용하기 위해 테스트 할 수 있습니다 test -e filename또는 [ -e filename ].

글로브가 있고 글로브와 이름이 일치하는 파일이 있는지 알고 싶습니다. glob은 0 개의 파일과 일치하거나 (이 경우 아무 것도 할 필요가 없음) 1 개 이상의 파일과 일치 할 수 있습니다 (이 경우 무언가를 수행해야 함). 글로브에 일치하는 것이 있는지 어떻게 테스트 할 수 있습니까? (나는 얼마나 많은 일치 항목이 있는지 상관하지 않으며 하나의 if명령문으로 루프 없이이 작업을 수행 할 수있는 것이 가장 좋습니다 (간단히 읽을 수 있기 때문에).

( test -e glob*글로브 파일이 둘 이상 일치하면 실패합니다.)


3
아래의 내 대답이 다른 모든 종류의 해킹 방식으로 '명확하게 맞습니다'. 그것은 한 줄짜리 쉘 내장이며 영원히 사용되어 왔으며 '이 특정 작업을위한 의도 된 도구'로 보입니다. 사용자가 여기에서 허용 된 답변을 실수로 참조 할까 걱정됩니다. 누구든지 나를 바로 고쳐 주시면 여기에 내 의견을 철회하겠습니다. 잘못되어서 배우는 것이 행복합니다. 차이가 그렇게 극도로 나타나지 않으면이 문제를 제기하지 않을 것입니다.
Brian Chrisman

1
이 질문에 내가 가장 좋아하는 솔루션은 find 명령 어떤 쉘 (심지어 비 Bourne의 껍질)에서 작동하지만 GNU 찾을 필요하며, compgen 명령 분명히 Bashism이다. 너무 안타깝게도 두 가지 답변을 모두 받아 들일 수 없습니다.
Ken Bloom

참고 :이 질문은 요청 된 이후 편집되었습니다. 원래 제목은 "glob가 bash에서 일치하는지 테스트"입니다. 답변을 게시 한 후 특정 셸 'bash'가 질문에서 삭제되었습니다. 질문 제목을 편집하면 내 답변에 오류가있는 것 같습니다. 누군가 가이 변경 사항을 수정하거나 해결할 수 있기를 바랍니다.
Brian Chrisman

답변:


178

배쉬 전용 솔루션 :

compgen -G "<glob-pattern>"

패턴을 벗어나면 일치하는 패턴으로 미리 확장됩니다.

종료 상태는 다음과 같습니다.

  • 일치하지 않는 경우 1
  • '하나 이상의 일치 항목'은 0

stdoutglob와 일치하는 파일 목록입니다 .
간결함과 잠재적 부작용 최소화 측면에서 이것이 최선의 선택이라고 생각합니다.

업데이트 : 사용 요청 예.

if compgen -G "/tmp/someFiles*" > /dev/null; then
    echo "Some files exist."
fi

9
참고 compgenA는 bash는 명령에 내장 특이하고 내장 지정된 명령어 POSIX 표준 유닉스 쉘의 일부가 아닙니다. pubs.opengroup.org/onlinepubs/9699919799 pubs.opengroup.org/onlinepubs/9699919799/utilities/… 따라서 다른 쉘로의 이식성이 문제가되는 스크립트에서는 사용하지 마십시오.
Diomidis Spinellis

1
bash 내장 기능이없는 비슷한 효과는 glos에 작동하고 ls와 같은 파일이 일치하지 않으면 실패하는 다른 명령을 사용하는 if ls /tmp/*Files 2>&1 >/dev/null; then echo exists; fi것 같습니다 .-코드 골프에 유용합니까? glob과 일치하지 않아야하는 glob와 동일한 파일이 있으면 실패하지만,이 경우 더 큰 문제가있을 수 있습니다.
Dewi Morgan

4
@DewiMorgan 이것은 더 간단합니다 :if ls /tmp/*Files &> /dev/null; then echo exists; fi
Clay Bridges

에 대한 자세한 내용은 compgen참조, man bash또는에help compgen
엘 teedee

2
예, 인용하거나 파일 이름 와일드 카드가 미리 확장됩니다. compgen "dir / *. ext"
Brian Chrisman

169

nullglob 쉘 옵션은 실제로 bashism입니다.

nullglob 상태를 지루하게 저장하고 복원 할 필요가 없도록, glob를 확장하는 서브 쉘 내에서만 설정합니다.

if test -n "$(shopt -s nullglob; echo glob*)"
then
    echo found
else
    echo not found
fi

더 나은 휴대 성과보다 유연한 글 로빙을 위해서는 find를 사용하십시오 :

if test -n "$(find . -maxdepth 1 -name 'glob*' -print -quit)"
then
    echo found
else
    echo not found
fi

명시 적 -print -quit 조치는 기본 내재 된 -print 조치 대신 찾기에 사용 되므로 찾기 기준과 일치하는 첫 번째 파일을 찾은 즉시 찾기 가 종료됩니다. 많은 파일이 일치하는 경우 또는 이보다 훨씬 빠르게 실행되어야 하며 확장 된 명령 행을 과도하게 채울 가능성을 피할 수 있습니다 (일부 쉘은 길이가 4K로 제한됨).echo glob*ls glob*

경우 찾기가 과도 같은 느낌 가능성에 맞게 파일의 수가 작고, 사용 합계 :

if stat -t glob* >/dev/null 2>&1
then
    echo found
else
    echo not found
fi

10
find정확히 맞은 것 같습니다. 쉘이 확장을 수행하지 않고 (다른 확장 명령으로 확장되지 않은 glob를 전달하기 때문에) 쉘 사이에서 이식 가능합니다 (사용하는 모든 옵션이 POSIX에 의해 지정되지는 않았지만). ls -d glob*첫 번째 경기에 도달하면 중지되기 때문에 (이전에 허용 된 답변).
Ken Bloom

1
shopt -u failglob이 옵션은 어떻게 든 충돌하는 것처럼 보이기 때문에이 답변이 필요할 수 있습니다 .
Calimo

find솔루션뿐만 아니라 더 글로브 문자와 파일 이름을 일치합니다. 이 경우에 내가 원하는 것입니다. 그래도 알아야 할 것.
우리는 모두 모니카

1
다른 누군가가 내 대답을 편집하여 분명히 그렇게하기로 결정했기 때문에.
flabdablet

1
unix.stackexchange.com/questions/275637/…-maxdepth 은 POSIX 찾기 옵션 을 교체하는 방법에 대해 설명합니다 .
Ken Bloom

25
#!/usr/bin/env bash

# If it is set, then an unmatched glob is swept away entirely -- 
# replaced with a set of zero words -- 
# instead of remaining in place as a single word.
shopt -s nullglob

M=(*px)

if [ "${#M[*]}" -ge 1 ]; then
    echo "${#M[*]} matches."
else
    echo "No such files."
fi

2
nullglob단일 결과가 패턴 자체와 같은지 확인하는 대신 잘못된 "일치하지 않음"세트를 피할 수 있습니다. 일부 패턴은 패턴 자체와 정확히 동일한 이름 (예 a*b: a?b또는 예는 아님 [a]) 과 일치 할 수 있습니다 .
Chris Johnsen

실제로 이것이 glob와 같은 이름의 파일이 있을 가능성 이 거의 없을 것이라고 생각합니다 . (예 : 누군가가 달렸 음 touch '*py') 그러나 이것은 다른 좋은 방향으로 나를 가리 킵니다.
Ken Bloom

나는 이것을 가장 일반적인 버전으로 좋아한다.
Ken Bloom

그리고 가장 짧습니다. 일치하는 항목이 하나만있는 경우 "$M"에 대한 속기로 사용할 수 있습니다 "${M[0]}". 그렇지 않으면 배열 변수에 glob 확장이 이미 있으므로 glob를 다시 확장하는 대신 목록으로 다른 항목에 전달하는 것이 좋습니다.
Peter Cordes

좋은. [if [[ $M ]]; then ...
Tobia

22

나는 좋아한다

exists() {
    [ -e "$1" ]
}

if exists glob*; then
    echo found
else
    echo not found
fi

파일이 많지 않은 한 읽기 쉽고 효율적입니다.
주요 단점은 외형보다 훨씬 미묘하다는 것입니다. 때로는 긴 의견을 추가해야한다는 느낌이 듭니다.
일치하는 항목이 있으면 "glob*"셸에서 확장되고 모든 일치 항목이에 전달되어 exists()첫 번째 항목을 확인하고 나머지 항목은 무시합니다.
일치하는 "glob*"것이 없으면 전달 exists()되어 존재하지 않는 것으로 발견됩니다.

편집 : 오 탐지가있을 수 있습니다, 의견을 참조하십시오


13
glob이 *.[cC]( 예 : 파일 이 아니 c거나 C파일이지만 파일 이있을 수 있음) 거짓 인 경우 false를 리턴 하거나 , *.[cC]그로부터 확장 된 첫 번째 파일이 예를 들어 존재하지 않는 파일에 대한 symlink이거나 에 액세스 할 수없는 디렉토리입니다 (를 추가하려는 경우 || [ -L "$1" ]).
Stephane Chazelas 2016 년

흥미 롭군 Shellcheck는 -e일치하는 항목이 0 개 또는 1 개일 때 globbing 만 작동한다고보고합니다 . 여러 경기에서 작동하지 않습니다 [ -e file1 file2 ]. 이론적 근거와 제안 된 솔루션에 대해서는 github.com/koalaman/shellcheck/wiki/SC2144 를 참조하십시오 .
Thomas Praxl

10

globfail을 설정했다면이 미친 것을 사용할 수 있습니다 (실제로는 안됩니다)

shopt -s failglob # exit if * does not match 
( : * ) && echo 0 || echo 1

또는

q=( * ) && echo 0 || echo 1

2
멍청한 실패의 환상적인 사용. 절대 사용해서는 안되지만 정말 아름답습니다. :)
Brian Chrisman

당신은 parens 안에 쇼핑을 넣을 수 있습니다. 그렇게하면 테스트에만 영향을줍니다.(shopt -s failglob; : *) 2>/dev/null && echo exists
flabdablet

8

test -e에는 깨진 심볼릭 링크가 존재하지 않는다고 생각하는 불행한 경고가 있습니다. 그래서 당신도 그것들을 확인하고 싶을 수도 있습니다.

function globexists {
  test -e "$1" -o -L "$1"
}

if globexists glob*; then
    echo found
else
    echo not found
fi

4
Stephane Chazelas가 Dan Bloch의 답변을 지적한 것처럼 여전히 특수 문자가있는 파일 이름에 대한 거짓 양성을 수정하지는 않습니다. (널 글로브로 원숭이를 제외하고는 제외).
Peter Cordes

3
당신은 피할 수 없을시 -o-a에서 test/ [. 예를 들어, 대부분의 구현 $1=있는 경우 여기에서 실패합니다 . [ -e "$1" ] || [ -L "$1" ]대신 사용하십시오 .
Stephane Chazelas

4

그의 아이디어에 따라 MYYN의 대답을 다소 단순화하려면 다음을 수행하십시오.

M=(*py)
if [ -e ${M[0]} ]; then
  echo Found
else
  echo Not Found
fi

4
닫기 그러나 일치 [a]하는 파일은 이름 [a]은 있지만 이름 은없는 파일은 a무엇입니까? 나는 아직도 nullglob이것을 좋아 한다. 일부는 이것을 pedantic으로 볼 수도 있지만, 합리적 일 정도로 충분히 정확할 수도 있습니다.
Chris Johnsen

@ sondra.kinsey 잘못되었습니다. glob [a]a리터럴 파일 이름 이 아니라 일치해야합니다 [a].
tripleee 2016 년

4

flabdablet의 대답을 기반으로 , 나를 위해 가장 쉬운 (가장 빠른 것은 아님) 것처럼 찾기 자체 를 사용 하는 동안 쉘에서 glob 확장을 그대로 두는 것 같습니다.

find /some/{p,long-p}ath/with/*globs* -quit &> /dev/null && echo "MATCH"

또는 다음과 if같이 :

if find $yourGlob -quit &> /dev/null; then
    echo "MATCH"
else
    echo "NOT-FOUND"
fi

이것은 내가 이미 stat를 사용하여 제시 한 버전과 동일하게 작동합니다. stat보다 find가 얼마나 쉬운 지 잘 모르겠습니다.
flabdablet

3
&> 리디렉션은 bashism이며 다른 쉘에서는 조용히 잘못된 일을 수행합니다.
flabdablet

이것은 글로브의 find경로를 받아들이고 더 간결하기 때문에 (불필요한 -maxdepth등) flabdablet의 대답 보다 낫습니다 . 또한 각각의 추가 glob 일치에 stat대한 추가 작업을 계속하지 않기 때문에 그의 대답 보다 낫 stat습니다. 이것이 효과가없는 코너 케이스에 기여할 수 있다면 고맙겠습니다.
drwatsoncode

1
추가 고려 사항 -maxdepth 0을 추가하면 조건을 추가 할 때 더 많은 유연성을 허용하기 때문에 추가 할 것 입니다. 예를 들어 결과를 일치하는 파일로만 제한한다고 가정합니다. 시도 할 수도 find $glob -type f -quit있지만 glob이 파일과 일치하지 않지만 파일이 포함 된 디렉토리 (재귀 적으로도) 와 일치하면 true를 반환합니다 . 반대로 find $glob -maxdepth 0 -type f -quitglob 자체가 하나 이상의 파일과 일치하는 경우에만 true를 리턴합니다. 주 maxdepth디렉토리 성분을 갖는에서 글로브를 방지하지 않습니다. (참고 2>로 충분합니다. 필요 없음 &>)
drwatsoncode

2
find처음 에 사용하는 요점은 쉘이 잠재적으로 거대한 glob 일치 목록을 생성하고 정렬하지 않도록하는 것입니다. find -name ... -quit최대 하나의 파일 이름과 일치합니다. 스크립트가 쉘로 생성 된 glob match 목록을 전달하는 데 의존하는 경우 find, 호출 find하면 불필요한 프로세스 시작 오버 헤드 만 발생합니다. 비어 있지 않은지 직접 결과 목록을 테스트하는 것이 더 빠르고 명확합니다.
flabdablet

4

또 다른 해결책이 있습니다.

if [ "$(echo glob*)" != 'glob*' ]

이것은 나에게 잘 작동합니다. 내가 놓친 코너 사례가 있습니까?


2
파일 이름이 실제로 'glob *'인 경우를 제외하고 작동합니다.
이안 켈링

glob를 변수로 전달하는 데 사용됩니다. 둘 이상의 일치 항목이있을 때 "너무 많은 인수"오류가 발생합니다. "$ (echo $ GLOB)"는 단일 문자열을 반환하지 않거나 적어도 단일 문자열로 해석되지 않으므로 너무 많은 인수 오류
DKebler

@DKebler : 큰 따옴표로 묶여 있기 때문에 단일 문자열로 해석해야합니다.
user1934428

3

Bash에서는 배열을 사용할 수 있습니다. glob이 일치하지 않으면 배열에 기존 파일과 일치하지 않는 단일 항목이 포함됩니다.

#!/bin/bash

shellglob='*.sh'

scripts=($shellglob)

if [ -e "${scripts[0]}" ]
then stat "${scripts[@]}"
fi

참고 : nullglob설정 한 경우 scripts빈 배열이되므로 대신 또는를 사용 [ "${scripts[*]}" ]하여 테스트해야 [ "${#scripts[*]}" != 0 ]합니다. 당신이 유무에 관계없이 반드시 작업이 라이브러리를 작성하는 경우 nullglob, 당신이 원하는 것

if [ "${scripts[*]}" ] && [ -e "${scripts[0]}" ]

이 방법의 장점은 glob 작업을 반복하지 않고 작업하려는 파일 목록을 가지고 있다는 것입니다.


왜 nullglob이 설정되어 있고 배열이 비어있는 경우에도 테스트 할 수 if [ -e "${scripts[0]}" ]...없습니까? 쉘 옵션 명사 설정 가능성도 허용 합니까?
johnraff

@ johnraff, 예, 일반적으로 nounset활성화되어 있다고 가정 합니다. 또한 파일의 존재 여부를 확인하는 것보다 문자열을 테스트하는 것이 (약간) 저렴할 수 있습니다. 그럼에도 불구하고 방금 방금 수행 했으므로 디렉토리 내용이 OS 캐시에서 최신 상태 여야합니다.
Toby Speight

1

이 가증은 효과가있는 것 같습니다.

#!/usr/bin/env bash
shopt -s nullglob
if [ "`echo *py`" != "" ]; then
    echo "Glob matched"
else
    echo "Glob did not match"
fi

아마도 sh가 아닌 bash가 필요할 것입니다.

nullglob 옵션을 사용하면 일치하는 항목이 없으면 glob가 빈 문자열로 평가되기 때문에 작동합니다. 따라서 echo 명령에서 비어 있지 않은 출력은 glob이 무언가와 일치 함을 나타냅니다.


당신이 사용해야합니다if [ "`echo *py`" != "*py"]
yegle

1
라는 파일이 있으면 제대로 작동하지 않습니다 *py.
Ryan C. Thompson

아무 파일 끝이없는 경우 py, `echo *py`로 평가됩니다 *py.
yegle

1
예. 그러나라는 단일 파일이있는 경우에도 마찬가지 *py입니다. 이는 잘못된 결과입니다.
Ryan C. Thompson

내가 틀렸다면 정정하지만 일치하는 파일이 없으면 *py스크립트가 "Glob matched"를 표시합니까?
yegle

1

나는이 대답을 보지 못했기 때문에 거기에 넣을 것이라고 생각했다.

set -- glob*
[ -f "$1" ] && echo "found $@"

1
set -- glob*
if [ -f "$1" ]; then
  echo "It matched"
fi

설명

에 대한 일치하지 않을 경우 glob*, 다음 $1이 포함됩니다 'glob*'. 파일이 존재하지 않기 -f "$1"때문에 테스트 는 사실 glob*이 아닙니다.

이것이 대안보다 나은 이유

이것은 sh와 함께 작동하며 ksh와 bash입니다. 하위 셸을 만들지 않습니다. $(..)`...`명령 서브 쉘을 생성; 프로세스를 분기하므로이 솔루션보다 느립니다.


1

이처럼 (을 포함하는 테스트 파일 pattern) :

shopt -s nullglob
compgen -W *pattern* &>/dev/null
case $? in
    0) echo "only one file match" ;;
    1) echo "more than one file match" ;;
    2) echo "no file match" ;;
esac

compgen -G우리는 더 많은 경우를보다 정확하게 구별 할 수 있기 때문에 훨씬 낫습니다 .

하나의 와일드 카드로만 작업 가능 *


0
if ls -d $glob > /dev/null 2>&1; then
  echo Found.
else
  echo Not found.
fi

일치하는 항목이 많거나 파일 액세스가 느리면 시간이 많이 걸릴 수 있습니다.


1
[a]파일 [a]이 있고 파일 이 없을 때 와 같은 패턴을 사용 하는 경우 잘못된 답변을 제공합니다 a. 일치해야하는 유일한 파일 a이 실제로는 없지만 "발견"이라고 표시됩니다 .
Chris Johnsen 8:30에

이 버전은 일반적인 POSIX / bin / sh (bashisms없이)에서 작동해야하며, 필요한 경우 glob에는 대괄호가 없으므로 어쨌든 걱정할 필요가 없습니다. 끔찍한 병리학. 그러나 어떤 파일이 glob과 일치하는지 테스트하는 좋은 방법이 없다고 생각합니다.
Ken Bloom

0
#!/bin/bash
set nullglob
touch /tmp/foo1 /tmp/foo2 /tmp/foo3
FOUND=0
for FILE in /tmp/foo*
do
    FOUND=$((${FOUND} + 1))
done
if [ ${FOUND} -gt 0 ]; then
    echo "I found ${FOUND} matches"
else
    echo "No matches found"
fi

2
이 버전은 정확히 하나의 파일이 일치하면 실패하지만 nullglob셸 옵션 을 사용하여 FOUND = -1 kludge를 피할 수 있습니다 .
Ken Bloom

@ 켄 : 흠, 나는 nullglobkludge를 호출하지 않습니다 . 단일 결과를 원래 패턴과 비교하는 것은 kludge (그리고 잘못된 결과가 발생하기 쉽습니다)를 사용 nullglob하지 않습니다.
Chris Johnsen 4:30에

@Chris : 당신이 잘못 읽은 것 같아요. 나는 nullglobkludge를 부르지 않았다 .
Ken Bloom

1
@ 켄 : 실제로, 나는 잘못 읽었습니다. 잘못된 비평에 대해 사과드립니다.
Chris Johnsen

-1
(ls glob* &>/dev/null && echo Files found) || echo No file found

5
일치하는 디렉토리 glob*가 있고 해당 디렉토리를 나열 할 쓰기가없는 경우에도 false를 리턴 합니다.
Stephane Chazelas 2016 년

-1

ls | grep -q "glob.*"

가장 효율적인 솔루션은 아니지만 (디렉토리에 많은 파일이있는 경우 속도가 느려질 수 있음) 간단하고 읽기 쉬우 며 정규 표현식이 일반 bash glob 패턴보다 강력하다는 장점이 있습니다.


-2
[ `ls glob* 2>/dev/null | head -n 1` ] && echo true

1
더 나은 답변을 얻으려면 코드에 설명을 추가하십시오.
MR
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.