$string
문자열이 전역 패턴과 일치하는지 알고 싶습니다 $pattern
. $string
기존 파일의 이름일 수도 있고 아닐 수도 있습니다. 어떻게 해야 하나요?
내 입력 문자열이 다음 형식이라고 가정해 보겠습니다.
string="/foo/bar"
pattern1="/foo/*"
pattern2="/foo/{bar,baz}"
$string
bash 관용어 가 일치하는지 또는 다른 임의의 전역 패턴과 일치하는지 확인하려고 합니다 . 지금까지 시도한 내용은 다음과 같습니다.$pattern1
$pattern2
[[ "$string" = $pattern ]]
$pattern
전역 패턴 대신 문자열 패턴으로 해석된다는 점을 제외하면 거의 작동합니다 .[ "$string" = $pattern ]
이 접근 방식의 문제점은 문자열이 확장된 다음 및
$pattern
확장 사이에서 문자열 비교가 수행된다는 것입니다.$string
$pattern
[[ "$(find $pattern -print0 -maxdepth 0 2>/dev/null)" =~ "$string" ]]
이 방법은 작동하지만
$string
파일이 존재하는 경우에만 가능합니다.[[ $string =~ $pattern ]]
=~
연산자가$pattern
전역 또는 와일드카드 패턴이 아닌 확장 정규식으로 해석되기 때문에 이는 작동하지 않습니다 .
답변1
이 문제에 대한 보편적인 해결책은 없습니다. 그 이유는 bash에서 중괄호 확장(일명 {pattern1,pattern2,...}
및 파일 이름 확장(일명 glob 패턴))이 별도의 것으로 처리되고 서로 다른 조건과 시간에 확장되기 때문입니다. 다음은 bash에서 수행되는 확장의 전체 목록입니다.
- 버팀대 확장
- 물결표 확장
- 매개변수 및 변수 확장
- 명령 대체
- 산술 확장
- 분사
- 경로명 확장
우리는 이들 중 하위 집합(아마도 중괄호, 물결표 및 경로 이름 확장)에만 관심이 있으므로 특정 패턴과 메커니즘을 사용하여 제어 가능한 방식으로 확장을 제한할 수 있습니다. 예를 들어:
#!/bin/bash
set -f
string=/foo/bar
for pattern in /foo/{*,foo*,bar*,**,**/*}; do
[[ $string == $pattern ]] && echo "$pattern matches $string"
done
이 스크립트를 실행하면 다음과 같은 출력이 생성됩니다.
/foo/* matches /foo/bar
/foo/bar* matches /foo/bar
/foo/** matches /foo/bar
이는 set -f
경로 이름 확장이 비활성화되어 있으므로 명령문에서 중괄호 확장과 물결표 확장만 발생하기 때문에 작동합니다 for pattern in /foo/{*,foo*,bar*,**,**/*}
. 그런 다음 [[ $string == $pattern ]]
중괄호 확장을 수행한 후 테스트 작업을 사용하여 경로 이름 확장을 테스트할 수 있습니다.
답변2
난 믿지 않아{bar,baz}
예쉘 전역 모드(물론 그렇긴 하지만 /foo/ba[rz]
)$string
하지만 일치하는 항목이 있는지 알고 싶다면 $pattern
다음과 같이 할 수 있습니다.
case "$string" in
($pattern) put your successful execution statement here;;
(*) this is where your failure case should be ;;
esac
원하는 만큼 많은 작업을 수행할 수 있습니다.
case "$string" in
($pattern1) do something;;
($pattern2) do differently;;
(*) still no match;;
esac
답변3
Patrick이 지적했듯이 "다른 종류"의 스키마가 필요합니다.
[[ /foo/bar == /foo/@(bar|baz) ]]
string="/foo/bar"
pattern="/foo/@(bar|baz)"
[[ $string == $pattern ]]
거기에는 따옴표가 필요하지 않습니다.
답변4
$string
저장된 glob 확장으로 인한 파일 경로에 있는지 확인하는 경우 $pattern
(다른 사람들이 말했듯 {foo,bar}
이 이것은 glob 연산자가 아님을 기억하십시오) 다음을 사용하십시오 zsh
.
if ()(($#)) $~pattern(NY1e['[[ $REPLY = $string ]]']); then
print -r -- "$string is among the result of the $pattern glob expansion"
fi
를 사용하면 bash
언제든지 루프를 사용할 수 있습니다.
among() (
string=$1 pattern=$2 IFS=
shopt -s nullglob extglob
for file in $pattern@(); do
[[ "$string" = "$file" ]] && return
done
false
)
if among "$string" "$pattern"; then
printf '%s\n' "$string is among the result of the $pattern glob expansion"
fi
( extglob
활성화되면 다음과 같은 것을 사용할 수 있는 ksh 확산 연산자의 하위 집합을 활성화할 수 있습니다 foo/@(foo|bar)
. @()
위의 패턴에 추가하여 전역 확산을 강제합니다. 이것이 없으면 존재하지 among foo foo
않더라도 true가 반환됩니다 foo
. 그러나 이는 시작을 의미합니다. don을 사용하면 /
끝에 패턴이 적용되지 않습니다(예: among /bin/ '/*/'
).
분할+glob 외에도 중괄호 확장은 활성화되지 않고 비활성화되지 않는 ksh
한 인수 확장 중에 수행됩니다 . / 에 해당하는 glob 연산자 도 있으므로 다음과 같이 할 수 있습니다.noglob
braceexpand
ksh93
~(N)
zsh
bash
nullglob
function among {
typeset string="$1" pattern="$2" IFS= file
set -o braceexpand +o noglob
for file in ~(N)$pattern; do
[[ "$string" = "$file" ]] && return
done
false
}
among foo/bar 'foo/{bar,baz}'
파일이 존재하는 한 true를 반환합니다 foo/bar
.
ksh의 확장된 glob 연산자는 다른 확장의 결과에서 인식되지 않습니다( a='@(x)'; echo $a
출력에 대한 POSIX 요구 사항을 준수하기 위해).@(x)
이들 모두에서 또는 문자열이 패턴과 일치하더라도 false를 반환 among .foo '*'
합니다 among /etc/issue '*'
.among /usr/local/bin '/*/bin'