web-dev-qa-db-ja.com

正規表現でファイル名を一致させる1つのライナー?

小さなスクリプトがあります。

#!/bin/bash
# test for regular expressions to match...
DIR="/search/path/"
NAME="FOO[0-9][0-9]_<bar|dog|cat>"

for FILE in `find ${DIR} -maxdepth 1 -type f -name "*\.[dD][oO][cC]"` 
do
    BASENAME=`basename ${FILE}`
    FILENAME="${BASENAME%.*}"

    if [[ "${FILENAME}" == ${NAME} ]]
    then
            echo "Found $FILENAME"
    else
            echo "$FILENAME not matching..!"
    fi

done

このスクリプトでは、FOO[0-9[0-9]_で始まり、次にbardog、またはcatのいずれかで始まるすべてのファイルを照合します。しかし、bogまたはcogまたはcarのような他のものが存在する場合、[〜#〜] not [〜#〜]一致する必要があります。

[a-z][a-z][a-z]すると、一致します...

私はすでに次のようなことを試しました:

NAME="FOO[0-9][0-9]_(bar|dog|cat)"
or
NAME="FOO[0-9][0-9]_bar|dog|cat"
or
NAME="FOO[0-9][0-9]_[bar|dog|cat]"
or
NAME="FOO[0-9][0-9]_'bar|dog|cat'"

しかし、正規表現に関するドキュメントでは、完全に一致するものを見つけることができませんでした。

使用するメインスクリプトははるかに複雑で、さまざまなサブプロセスがハングしているため、1行で記述する必要があります。

これは可能ですか...?

3
SHLelieveld

文字列の一致のみを使用する場合は、=~(拡張正規表現に一致する)を使用する必要があります。

if [[ "${FILENAME}" =~ "FOO[0-9][0-9]_(bar|dog|cat)" ]]

または

NAME="FOO[0-9][0-9]_(bar|dog|cat)"
if [[ "${FILENAME}" =~ ${NAME} ]]

オリジナルに合わせるため。

==は常にグロビング一致(またはグロブが無効になっている場合は完全一致)であり、正規表現では使用できません。

または、スクリプトにさらに変更を加えることができる場合は、GNU findを使用していると想定して、findでフィルタリングできます。

find ${DIR} -maxdepth 1 -type f -regextype posix-extended -regex ".*/FOO[0-9][0-9]_(bar|dog|cat)\.[Dd][Oo][Cc]"

-regextype posix-extendedfindに拡張正規表現を使用することを伝えます。.*/はファイル名だけでなくパス全体と一致するため、正規表現自体は-regexで始まります。)

1
Stephen Kitt

find の出力をループしないでください。代わりにfindを使用して、パス名でフィードするスクリプトを実行します。

_#!/bin/bash

topdir='/search/path'
pattern='FOO[0-9][0-9]_(bar|dog|cat)'

find "$topdir" -type f -maxdepth 1 -iname '*.doc' -exec bash -c '
    pattern=$1; shift
    for pathname do
        stem=$( basename "${pathname%.*}" )
        if [[ "$stem" =~ $pattern ]]; then
            printf "Found %s\n" "$pathname"
        else
            printf "No match in %s\n" "$pathname"
        fi
    done' bash "$pattern" {} +
_

findで_-iname_を使用すると、ファイル名で大文字と小文字を区別しない一致が行われることに注意してください。

関連:

または、bashを使用して簡単に:

_#!/bin/bash

topdir='/search/path'
pattern='FOO[0-9][0-9]_(bar|dog|cat)'

shopt -s globstar nullglob

for pathname in "$topdir"/**/*.[Dd][Oo][Cc]; do
    stem=$( basename "${pathname%.*}" )
    if [[ "$stem" =~ $pattern ]]; then
        printf "Found %s\n" "$pathname"
    else
        printf "No match in %s\n" "$pathname"
    fi
done
_

または、全体でファイル名グロブを使用する:

_#!/bin/bash

topdir='/search/path'

shopt -s globstar nullglob extglob

for pathname in "$topdir"/**/*FOO[0-9][0-9]_@(bar|dog|cat)*.[Dd][Oo][Cc]
do
    printf "Found %s\n" "$pathname"
done
_
  • globstarは、_**_グロブパターンを有効にします。これは、_*_のように機能しますが、パス名のスラッシュ全体で一致します。
  • nullglobは、一致しないグロブパターンを空の文字列に展開します。
  • extglobは、いくつかの拡張globパターンを使用可能にします。これらの中には、括弧内のパターンのいずれかに一致する@(...)があります。

ワンライナーの場合は、

_shopt -s globstar nullglob extglob; printf 'Found %s\n' "$topdir"/**/*FOO[0-9][0-9]_@(bar|dog|cat)*.[Dd][Oo][Cc]
_

しかし、一致が見つからなかった場合でも、文字列Found(1行に1つだけ)が出力されます。

0
Kusalananda