파일 이름이 중괄호로 확장된 glob 패턴 집합 중 하나와 일치하는지 프로그래밍 방식으로 어떻게 확인할 수 있나요?

파일 이름이 중괄호로 확장된 glob 패턴 집합 중 하나와 일치하는지 프로그래밍 방식으로 어떻게 확인할 수 있나요?

$string문자열이 전역 패턴과 일치하는지 알고 싶습니다 $pattern. $string기존 파일의 이름일 수도 있고 아닐 수도 있습니다. 어떻게 해야 하나요?

내 입력 문자열이 다음 형식이라고 가정해 보겠습니다.

string="/foo/bar"
pattern1="/foo/*"
pattern2="/foo/{bar,baz}"

$stringbash 관용어 가 일치하는지 또는 다른 임의의 전역 패턴과 일치하는지 확인하려고 합니다 . 지금까지 시도한 내용은 다음과 같습니다.$pattern1$pattern2

  1. [[ "$string" = $pattern ]]

    $pattern전역 패턴 대신 문자열 패턴으로 해석된다는 점을 제외하면 거의 작동합니다 .

  2. [ "$string" = $pattern ]

    이 접근 방식의 문제점은 문자열이 확장된 다음 및 $pattern확장 사이에서 문자열 비교가 수행된다는 것입니다.$string$pattern

  3. [[ "$(find $pattern -print0 -maxdepth 0 2>/dev/null)" =~ "$string" ]]

    이 방법은 작동하지만 $string파일이 존재하는 경우에만 가능합니다.

  4. [[ $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 연산자 도 있으므로 다음과 같이 할 수 있습니다.noglobbraceexpandksh93~(N)zshbashnullglob

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'

관련 정보