bashでディレクトリパスを見つけるときに正規表現を渡す方法は?


14

ディレクトリに名前が付けられているanacondaminiconda、ユーザーにあるかを確認するために、小さなbashスクリプトを作成しました$HOME。しかしminiconda2、自宅のディレクトリは見つかりません。

どうすれば修正できますか?

if [ -d "$HOME"/"(ana|mini)conda[0-9]?" ]; then
    echo "miniconda directory is found in your $HOME"
else
    echo "anaconda/miniconda is not found in your $HOME"
fi

PS:私が持っている場合[ -d "$HOME"/miniconda2 ]; then、それはminiconda2ディレクトリを見つけるので、エラーは部分にあると思います"(ana|mini)conda[0-9]?"

スクリプトを一般的にしたいです。私にとっては、miniconda2ですが、他のユーザーにとっては、anaconda2、miniconda3などになる可能性があります。


別のユーザーがanaconda_2または-2または-may2019を使用する場合があります。xxxconda *はもっと良くないでしょうか?
WinEunuuchs2Unix

2
Bashファイル名の展開では、正規表現ではなく、glob式を使用します。
ピーター

回答:


13

これは、驚くほどトリッキーなことです。

基本的に、-d単一の引数のみをテストします-正規表現を使用してファイル名を一致させることができた場合でも。

1つの方法は、問題を回避し、ディレクトリの正規表現一致をテストする代わりに、正規表現一致のディレクトリをテストすることです。言い換えれば、単純なシェルglobを使用してすべてのディレクトリをループし、$HOMEそれぞれを正規表現に対してテストし、一致を破り、最後にBASH_REMATCH配列が空でないかどうかをテストします。

#!/bin/bash

for d in "$HOME"/*/; do
  if [[ $d =~ (ana|mini)conda[0-9]? ]]; then
    break;
  fi
done

if ((${#BASH_REMATCH[@]} > 0)); then
    echo "anaconda/miniconda directory is found in your $HOME"
  else
    echo "anaconda/miniconda is not found in your $HOME"
fi

別の方法は、正規表現の代わりに拡張シェルグロブを使用し、配列内のグロブの一致をキャプチャすることです。次に、配列が空でないかどうかをテストします。

#!/bin/bash

shopt -s extglob nullglob

dirs=( "$HOME"/@(ana|mini)conda?([0-9])/ )

if (( ${#dirs[@]} > 0 )); then
  echo "anaconda/miniconda directory is found in your $HOME"
else
  echo "anaconda/miniconda is not found in your $HOME"
fi

末尾/により、ディレクトリのみが一致することが保証されます。これnullglobにより、シェルは一致がゼロの場合に一致しない文字列を返しません。


どちらかを再帰的にするには、globstarシェルオプション(shopt -s globstar)を設定してから、それぞれ:

  • (正規表現バージョン): for d in "$HOME"/**/; do

  • (拡張globバージョン): dirs=( "$HOME"/**/@(ana|mini)conda?([0-9])/ )


1
アレイルートに行きます。-の?([0-9])代わりに使用できます。正規表現数量詞と同じ、0または1に一致します。@(|[0-9])?(...)?
グレンジャックマン

2
ブレース拡張を使用する場合、extglobさえ必要ありません(これにより、一致する可能性のあるすべての名前が生成されます)~/{ana,mini}conda{0..9}*/
。– xenoid

それが保持するようにいずれかのこれらのソリューションの編集にとにかくありさえあればminianacondaに搭載されていますか$HOME/sub-directories?例えば$HOME/sub-dir1/sub-dir2/miniconda2
ジェニー

1
@ジェニーは、私の編集を参照してくださいglobstar
steeldriver

1
@terdonええ、私は実際に一致する「正しい」ことのウサギの穴を降りたくはありませんでした-私は一般的なアプローチを説明する目的でOPの正規表現をそのまま取りました
-steeldriver

9

確かに、すでに述べたように、これには注意が必要です。私のアプローチは次のとおりです。

  • 使用findとその正規表現機能を、問題のディレクトリを見つけます。
  • find印刷させてx、各foundディレクトリのために
  • 保管する xesを文字列にます
  • 文字列が空でない場合、ディレクトリの1つが見つかりました。

したがって:

xString=$(find $HOME -maxdepth 1 \
                     -type d \
                     -regextype egrep \
                     -regex "$HOME/(ana|mini)conda[0-9]?" \
                     -printf 'x');
if [ -n "$xString" ]; then
    echo "found one of the directories";
else
    echo "no match.";
fi

説明:

  • find $HOME -maxdepth 1以下をすべて$HOME 検索しますが、検索を1レベルに制限します(つまり、サブディレクトリに再帰しません)。
  • -type d検索をdirectories のみに制限します
  • -regextype egrep告げるfindどのタイプの正規表現我々が対処。[0-9]?やのようなもの(…|…)はいくらか特別でありfind 、デフォルトではそれらを認識しないため、これが必要です。
  • -regex "$HOME/(ana|mini)conda[0-9]?"実際の 正規表現です探したいです
  • -printf 'x' 前の条件を満たすxすべてのものに対してを出力するだけです。

一致する場合。-bash: -regex: command not found found one of the directories
ジェニー

こんにちはPerlDuck:ありがとう。いい答えも。しかし、printfたとえばスクリプトを実行するとエラーが発生しますが、スクリプトは正常に実行されますが、一致しない場合はprintfコマンドが見つかりませんが、印刷するものがない可能性があるためだと思いますか?-bash: -printf: command not found no match.
ジェニー

3
@Jennyコピーするときにタイプミスをしたかもしれません。-printfはコマンドではなく、の引数findです。これが、前の行の最後にあるバックスラッシュが行うことです。
wjandrea

1
あいまいさを引き続き検出したい-quit場合を除き、見つかったパスを印刷した後にお勧めします。
ピーター

そして、実際のパスを印刷してみませんか?あなたはすでにそれを持っているので、それを捨ててx代わりに使うのは恥ずかしいようです:foundDir=$(find $HOME -maxdepth 1 -type d -regextype egrep -regex "$HOME/(ana|mini)conda[0-9]?" -print -quit); echo "found $foundDir"
-terdon

2

テストするディレクトリ名のリストをループし、それらのいずれかが存在する場合は、それを操作できます。

a=0
for i in {ana,mini}conda{,2}; do
  if [ -d "$i" ]; then
    unset a
    break
  fi
done
echo "anaconda/miniconda directory is ${a+not }found in your $HOME"

このソリューションは明らかに完全な正規表現の力を考慮していませんが、シェルのグロビングとブレースの展開は少なくともあなたが示した場合には等しくなります。1つのディレクトリが存在するとすぐにループが終了し、以前に設定された変数の設定が解除されますa。後続のecho行では、パラメータの展開 は、設定されている${a+not }場合a(= dirが見つからない場合)、その他の場合は「not」に展開されます。


1

可能な回避策は、以下に示すように、minicondaとanacondaを別々に検索することです

if [ -d "$HOME"/miniconda* ] || [ -d "$HOME"/anaconda* ]; then
    echo "miniconda directory is found in your $HOME"
else
    echo "anaconda/miniconda is not found in your $HOME"
fi

しかし、誰かが提案がある場合、ディレクトリを検索するときに正規表現を渡すことができない理由を知りたいです。


2
私はこれを支持しました-しかし、ユーザーが複数の一致するディレクトリ(たとえば、miniconda AND miniconda2)を持っている場合、それが壊れることに気付きました
-steeldriver

@steeldriver:「ユーザーが複数の一致するディレクトリを持っている場合は中断します」はい、それは本当です。修正方法はありますか?
ジェニー

@Jenny steeldriverの答えのように、配列を使用します。shopt -s nullglob; dirs=( "$HOME"/miniconda* "$HOME"/anaconda* ); if (( ${#dirs[@]} > 0 )); then ...
wjandrea

同じテストで両方のディレクトリglobが検索されるため、両方のディレクトリが見つかったとしても、少なくともそれを置き換え] || [-oも壊れないはずです。
フェニックス

@steeldriverとJenny:1つだけを選ぶのではなく、あいまいさを解消したいかもしれませ。間違ったディレクトリを選択する代わりに、ユーザーにディレクトリを指定させます。(たとえば、自動検出コードを実行する代わりに、ディレクトリ名を設定するためにスクリプトを編集します。)
Peter Cordes
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.