在bash中查找目录路径时如何传递正则表达式?


14

我已经写了一个小bash脚本找到,如果一个名为anacondaminiconda在我的用户$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文件名扩展使用全局表达式而不是正则表达式。
彼得·科德斯

Answers:


13

做得好是一件令人惊讶的棘手的事情。

从根本上讲,-d即使您可以使用正则表达式匹配文件名,也只会测试单个参数。

一种方法是解决问题,然后测试目录是否匹配正则表达式,而不是测试目录是否匹配正则表达式。换句话说,使用一个简单的shell 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

另一种方法是使用扩展的外壳glob代替正则表达式,并捕获数组中的所有glob匹配项。然后测试数组是否为非空:

#!/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防止外壳在零匹配的情况下返回不匹配的字符串。


要进行递归,请设置globstarshell选项(shopt -s globstar),然后分别设置:

  • (正则表达式版本): for d in "$HOME"/**/; do

  • (扩展的glob版本): dirs=( "$HOME"/**/@(ana|mini)conda?([0-9])/ )


1
我会走阵列路线。您可以?([0-9])代替@(|[0-9])- ?(...)匹配零或一,与正则表达式?量词相同。
格伦·杰克曼(

2
您甚至不需要extglob,而是使用大括号扩展名(这会生成所有可能的匹配名称):~/{ana,mini}conda{0..9}*/
xenoid

反正是有编辑任一方案,使其保持甚至如果mini还是anaconda安装在$HOME/sub-directories?例如$HOME/sub-dir1/sub-dir2/miniconda2
詹妮(Jenny)

1
@Jenny,请参阅我的相关编辑globstar
Steeldriver '19

1
@terdon是的,我真的不想钻研什么是“正确的”东西-我只是按原样使用了OP的正则表达式,目的是说明一般性的方法
Steeldriver

9

确实,如前所述,这很棘手。我的方法如下:

  • 使用find及其正则表达式功能来查找相关目录。
  • 让我们为找到的每个目录find打印一个x
  • xes 存储在字符串中
  • 如果字符串非空,则找到目录之一。

从而:

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 但将搜索限制在一个级别(即:它不会递归到子目录中)。
  • -type d将搜索限制为仅d目录
  • -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在打印找到的路径后,除非您继续检测歧义。
彼得·科德斯

为什么不打印实际路径?您已经拥有它,因此丢弃它而使用它似乎是一个耻辱xfoundDir=$(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"

该解决方案显然不能充分发挥正则表达式的功能,但是至少在您所展示的情况下,外壳的globbing和大括号的扩展相等。一旦存在一个目录并取消设置先前设置的变量,循环就会退出a。在下echo一行中,如果已设置(=未找到目录),则参数扩展 ${a+not }将扩展为空,否则将扩展为a“ 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和miniconda2),它将损坏
steeldriver

@steeldriver:“如果用户具有多个匹配目录,它将中断”是的,的确如此。您对如何解决有什么建议?
詹妮

@Jenny使用数组,就像在Steeldriver的答案中一样。shopt -s nullglob; dirs=( "$HOME"/miniconda* "$HOME"/anaconda* ); if (( ${#dirs[@]} > 0 )); then ...
wjandrea

如果您] || [-o它替换,则在找到两个目录的情况下至少不应中断,因为在同一测试中同时查找了两个目录glob。
凤凰城,

@steeldriver和Jenny:您可能希望打破歧义,而不是只挑一个。让用户指定其目录,而不是选择错误的目录。(例如,编辑脚本以设置目录名称,而不是运行自动检测代码。)
Peter Cordes
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.