exec 및 find에서 정규식을 사용하는 방법은 무엇입니까?

exec 및 find에서 정규식을 사용하는 방법은 무엇입니까?

매개변수 내에서 결과(파일명)를 기반으로 exec정규식을 사용할 수 있습니까 find? 예를 들어 다음과 같은 일부 매개변수를 기반으로 "실행"할 수 있기를 원합니다.

find . -name pattern -regex "foo (regex1) bar (Regex2)" -exec something $1 $2 ;

답변1

실행하려는 명령의 정규식에는 캡처링 그룹을 사용할 수 없습니다. 제한된 일치를 사용하는 경우 find -regex명령에서 몇 가지 추가 일치를 수행해야 합니다. 셸을 호출하고 자체 패턴 일치 구문을 사용하여 이를 수행할 수 있습니다. 예를 들어, foobar가 상수 문자열이고 다음과 regex1일치할 수 없는 경우 bar:

find … -exec sh -c '
  x=${0#foo}
  y=${x#*bar}
  x=${x%%bar*}
  something "$x" "$y"
' {} \;

셸을 호출하면 약간의 오버헤드가 발생합니다. 셸을 일괄적으로 호출하면 성능이 약간 향상될 수 있습니다.

find … -exec sh -c '
  for item do
    item=${item#foo}
    y=${item#*bar}
    x=${item%%bar*}
    something "$x" "$y"
  done
' sh {} +

regex1몇 가지 필터링을 수행했으므로 및 를 초과하는 일치하는 셸 패턴으로 벗어날 수 있지만 regex2특정 경로 형식의 경우 동일한 부분과 일치합니다. 일반 쉘 패턴으로 foo및 를 표현할 수 없는 경우 bar정규식만큼 강력한 추가 패턴인 , , 및 를 지원하는 ksh 또는 bash를 @(alter|native)호출 *(zero-or-more)할 수 +(one-or-more)있습니다 .?(optional)!(negated)배쉬에서, 이러한 모드를 활성화해야 합니다 shopt -s extglob. ksh에서는 로컬로 사용할 수 있습니다.

Bash에는 사용할 수 있는 정규식 일치 구문이 있습니다.조건문: [[ $STRING =~ REGEXP ]]. 정규식은 ERE(예 find -regextype posix-egrep: )입니다. (Zsh에는 비슷한 것이 있습니다. ksh는 있지만 =~캡처 그룹을 노출하지 않습니다.) 캡처 그룹은 BASH_REMATCH배열로 사용할 수 있습니다.

find … -exec bash -c '
  for item do
    [[ item =~ foo(regex1)bar(regex2) ]]
    something "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}"
  done
' bash {} +

또 다른 방법은결과 인쇄 및 필터링를 누른 다음 xargs호출 프로그램을 호출합니다. 첫 번째와 두 번째 인수를 연속 용어로 정렬하고 실행합니다 xargs -n 2. xargs의 이상한 인용 형식을 피하기 위해 널 바이트를 구분 기호로 사용하거나 -d '\n'엄격한 라인별 구문 분석을 사용하십시오. sed와 같은 최신 GNU 도구는 레코드를 구분하기 위해 개행 문자 대신 널 바이트를 사용할 수 있습니다.

find … -print0 |
sed -z 's/^foo\(regex1\)bar\(regex2\)$/\1\x00\2/'
| xargs -n2 -0 something

또 다른 접근 방식은 ksh93, bash 또는 zsh의 재귀 와일드카드 기능 찾기를 포기하고 사용하는 것입니다. **/하위 디렉터리를 재귀적으로 일치시킵니다. 이는 부울 커넥터가 포함된 복잡한 조회 표현식에서는 불가능하지만 대부분의 경우에는 충분합니다. 예를 들어, bash에서는 다음과 같이 디렉터리에 대한 심볼릭 링크로 반복됩니다 find -L.

shopt -s extglob globstar
for x in **/*bar*; do
  if [[ item =~ foo(regex1)bar(regex2) ]]; then
    something "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}"
  fi
done

zsh에서:

for x in **/*bar*; do
  if [[ item =~ foo(regex1)bar(regex2) ]]; then
    something $match[1] $match[2]
  fi
done

관련 정보