さて、正規表現を学び始めましたが、他の正規表現の代わりにどこでも正規表現を使う練習をしたいと思います。
拡張子を持つファイルを見つけようとしたときに、このような状況が発生しました。sh or md
$ find . regex ".*\.(sh|md)$"
.
./bogus.py
./cofollow.py
./data8.txt
./example.sh
./longest_word_2.sh
./posit_param.sh
./cobroadcast2.py
残念ながら、それは出力されます/bogus.py
。
BREルールを見つけて脱出しようとしました。()
$ find . -regex ".*\.\(sh|md\)$"
#get nothing return
一連の検索の最後に -regextype ソリューションを取得しました。正規表現 - ファイルの検索
$ find . -regextype posix-extended -iregex ".*\.(sh|md)$"
./example.sh
./longest_word_2.sh
./posit_param.sh
$ find . -regextype egrep -iregex ".*\.(sh|md)$"
./example.sh
./longest_word_2.sh
./posit_param.sh
./table_regex_bat.md
さらに、優れたモジュラーソリューション
$ find -type f | egrep ".*\.(sh|md)$"
./example.sh
./longest_word_2.sh
./posit_param.sh
./table_regex_bat.md
ただし、BSDには述部を使用してこれらの操作を実行するための近道があります-E
。
$ /usr/bin/find -E . -regex ".*\.(sh|md)$"
./example.sh
./longest_word_2.sh
./posit_param.sh
私は私のコードと技術を移植可能にするためにGNUツールだけを使用することにしました。
そのため、「find -regextype egrep」というエイリアスを指定し始め、
残念ながらfindはパスで$ 1を取得しました。
どうすれば問題を簡単に解決できますか?
答え1
alias
パラメータを渡すために使用しないでください。移植性がなく、インタラクティブシェルでのみ便利です。代わりに関数を使用して、パラメータを目的のパスに渡してください。
regexFind() {
(( "$#" )) || { printf 'Insufficient arguments provided \n' >&2; return 1; }
find "$1" -regextype egrep -iregex ".*\.(sh|md)$"
}
関数を次のように呼び出します。
regexFind "/home/foo/bar"
また、結果に追加するには、bash
globファイルの独自の方法もあることに注意してください。機能するには、いくつかの拡張シェルオプションを有効にするだけです。このオプションを有効にし-s
て-u
無効にします。
nullglob
拡張されていないグローバル結果を有効な一致として無視できます。したがって、*.sh
andで終わるファイルを一致させたい場合は、*.md
特定のディレクトリに移動して次のようにします。
shopt -s nullglob
fileList=(*.sh)
fileList+=(*.md)
shopt -u nullglob
そして、以下のように結果を印刷してみてください。ファイル名がトークン化されないようにするには、拡張子を引用することを忘れないでください。
printf '%s\n' "${fileList[@]}"
答え2
GNUのデフォルトの正規表現は、BREではなく、いくつかの古代バージョンのGNUの正規表現です(たとえば、BREとERE間のいくつかのハイブリッド、サポートされているが必要でサポートされています)find
。emacs
+
\(...\)
|
\|
BSD の場合、find
デフォルトは BRE です。この-E
オプションを使用してEREを有効にすることができます。
alias efind='find -E'
または:
efind() { find -E "$@"; }
GNUは、オプションではなく述語を介してfind
EREを有効にします。-regextype posix-extended
述語は、ファイル名の後に(存在する場合)、オプションの後、または-regex
使用の前に表示する必要があります。-iregex
GNUfind
構文は次のとおりです。
find [options] [files] [predicates]
^
したがって、その位置(表示された位置)に挿入する必要があります^
。
したがって、ラッパー関数またはスクリプトを定義するときにこの点を考慮する必要があります。すべてのオプションとファイル名をスキップして、-regextype posix-extended
その後に挿入してください。
efind() (
found_predicate=false
for arg do
"$found_predicate" || case $arg in
(-[LPDd]|-[OD]*) ;; # skip options
(-*|['()!'])
set -- "$@" -regextype posix-extended
found_predicate=true;;
esac
set -- "$@" "$arg"
shift
done
exec find "$@"
)
その他の注意事項:
- 最初の印刷は
bogus.py
BREを使用したためではなく、述語ではなくファイル名として扱われますregex
。-regex
regex
find . | egrep ...
ファイルパスが複数行で構成される可能性があるため、無効です。 GNUツールまたは互換ツールを使用すると、NULで区切られたレコードを処理できますfind . -print0 | grep -zE ...
(tr '\0' '\n'
または表示に使用される場合はパイプで接続)。
答え3
find . -type f \( -name '*.sh' -o -name '*.md' \)
find
これは正規表現マッチングをサポートする必要がないため、すべての実装に適用されます。
より柔軟にするには:
suffixfind () (
dir=$1
shift
for suf do
set -- "$@" -o -name "*.$suf"
shift
done
shift
find "$dir" -type f \( "$@" \)
)
同様のシェルで動作するこのヘルパーシェル関数は、sh
最初のコマンドライン引数を選択して変数に入れますdir
。次に、関数-name "*.<suf1>" -o -name "*.<suf2>" (etc.)
のコマンドラインからすべてのファイル名サフィックスのリストを設定し、find
そのリストを呼び出して$dir
。
こうして使えばいいと思います。
suffixfind /usr sh md txt
.sh
名前で終わるか、パスの内または下にある.md
すべての一般ファイルを見つけます。.txt
/usr
bash
配列とローカル変数を使用してbash
上記の内容をより詳細に変換する方法は次のとおりです。
suffixfind () {
local dir=$1
shift
local names
names=( -name "*.$1" )
shift
for suf do
names+=( -o -name "*.$suf" )
done
find "$dir" -type f \( "${names[@]}" \)
}
GNUツールと移植性に関する言及については、Linux以外のシステムでもGNUツールを使用できますが、g
ツール名にプレフィックスが付いていることに注意してください。したがって、GNUはfind
これをシステムの基本的な実装とgfind
区別することができます。find
したがって、「GNU移植可能な」方法は、gfind
実際にGNUかどうかをテストする前に使用可能かどうかをテストする必要があります。これを行うまでは(おそらくステータスと出力を返すテストを通して)GNUを扱っているという事実は不便です。find
find
find --version
find