Làm thế nào để vượt qua regex khi tìm đường dẫn thư mục trong bash?


14

Tôi đã viết một tập lệnh bash nhỏ để tìm xem một thư mục có tên anacondahoặc minicondatrong người dùng của tôi $HOME. Nhưng nó không tìm thấy miniconda2thư mục trong nhà của tôi.

Làm thế nào tôi có thể sửa lỗi này?

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: Nếu tôi có [ -d "$HOME"/miniconda2 ]; then, thì nó tìm thấy thư mục miniconda2 nên tôi nghĩ lỗi nằm ở phần"(ana|mini)conda[0-9]?"

Tôi muốn kịch bản nói chung. Đối với tôi, đó là miniconda2 nhưng đối với một số người dùng khác, nó có thể là anaconda2, miniconda3, v.v.


Một người dùng khác có thể sử dụng anaconda_2 hoặc -2 hoặc -may2019. Vì vậy, xxxconda * sẽ tốt hơn?
WinEunuuchs2Unix

2
Mở rộng tên tệp Bash sử dụng biểu thức toàn cục, không phải biểu thức chính quy.
Peter Cordes

Câu trả lời:


13

Đây là một điều khó khăn đáng ngạc nhiên để làm độc đáo.

Về cơ bản, -dsẽ chỉ kiểm tra một đối số duy nhất - ngay cả khi bạn có thể khớp tên tệp bằng biểu thức chính quy.

Một cách sẽ là lật lại vấn đề và kiểm tra các thư mục cho một trận đấu regex thay vì kiểm tra trận đấu regex cho các thư mục. Nói cách khác, lặp qua tất cả các thư mục $HOMEbằng cách sử dụng shell shell đơn giản và kiểm tra từng thư mục dựa trên biểu thức chính quy của bạn, phá vỡ một trận đấu, cuối cùng kiểm tra xem BASH_REMATCHmảng đó có trống không:

#!/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

Một cách khác là sử dụng một quả cầu vỏ mở rộng thay cho biểu thức chính quy và ghi lại bất kỳ trận đấu toàn cầu nào trong một mảng. Sau đó kiểm tra nếu mảng không trống:

#!/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

Các trailing /đảm bảo rằng chỉ các thư mục được khớp; các nullglobngăn vỏ từ trả lại chuỗi chưa từng có trong trường hợp của zero trận đấu.


Để thực hiện đệ quy, hãy đặt globstartùy chọn shell ( shopt -s globstar) và sau đó:

  • (phiên bản regex): for d in "$HOME"/**/; do

  • (phiên bản toàn cầu mở rộng): dirs=( "$HOME"/**/@(ana|mini)conda?([0-9])/ )


1
Tôi sẽ đi theo con đường mảng. Bạn có thể sử dụng ?([0-9])thay cho @(|[0-9])- ?(...)khớp 0 hoặc một, giống như ?định lượng regex .
glenn jackman

2
Bạn thậm chí không cần extglob là bạn sử dụng mở rộng ~/{ana,mini}conda{0..9}*/
niềng răng

Có cách nào để chỉnh sửa một trong hai giải pháp này để nó sẽ được giữ ngay cả khi minihoặc anacondađược cài đặt $HOME/sub-directorieskhông? Ví dụ$HOME/sub-dir1/sub-dir2/miniconda2
Jenny

1
@Jenny xin vui lòng xem chỉnh sửa của tôi liên quanglobstar
Steeldo

1
@terdon yeah Tôi không thực sự muốn đi xuống hang thỏ của "quyền" điều để phù hợp với những gì - Tôi chỉ mất regex của OP như nó vốn có với mục đích minh họa một cách tiếp cận chung
steeldriver

9

Thật vậy, như đã đề cập, điều này là khó khăn. Cách tiếp cận của tôi là như sau:

  • sử dụng findvà khả năng regex của nó để tìm các thư mục trong câu hỏi.
  • hãy findin một xthư mục tìm thấy
  • lưu trữ xes trong một chuỗi
  • nếu chuỗi không trống, thì một trong các thư mục đã được tìm thấy.

Như vậy:

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

Giải trình:

  • find $HOME -maxdepth 1tìm thấy mọi thứ bên dưới $HOME nhưng giới hạn tìm kiếm ở một cấp độ (nghĩa là: nó không tái diễn thành các thư mục con).
  • -type dhạn chế tìm kiếm chỉ các directories
  • -regextype egrepcho findbiết loại biểu thức chính quy mà chúng ta đối phó. Điều này là cần thiết bởi vì những thứ như [0-9]?(…|…)hơi đặc biệt và find không nhận ra chúng theo mặc định.
  • -regex "$HOME/(ana|mini)conda[0-9]?"biểu thức chính quy thực tế mà chúng tôi muốn tìm kiếm
  • -printf 'x'chỉ in một điều xcho mọi điều thỏa mãn các điều kiện trước đó.

Khi có một trận đấu. -bash: -regex: command not found found one of the directories
Jenny

Xin chào PerlDuck: Cảm ơn. Một câu trả lời tốt đẹp quá. Nhưng tôi gặp lỗi printfVí dụ khi tôi chạy tập lệnh, nó chạy ổn nhưng nó không tìm thấy lệnh printf khi không có kết quả khớp nhưng tôi nghĩ đó là vì không có gì để in? -bash: -printf: command not found no match.
Jenny

3
@Jenny Bạn có thể đã mắc lỗi đánh máy khi sao chép nó, vì nó hoạt động tốt với tôi. -printfkhông phải là một lệnh mà là một đối số để find. Đó là những gì dấu gạch chéo ở cuối dòng trước đó.
wjandrea

1
Tôi muốn đề xuất -quitsau khi in đường dẫn tìm thấy, trừ khi bạn muốn tiếp tục phát hiện sự mơ hồ.
Peter Cordes

Và tại sao không in đường dẫn thực tế? Bạn đã có nó rồi, nên có vẻ xấu hổ khi loại bỏ nó và sử dụng xthay thế:foundDir=$(find $HOME -maxdepth 1 -type d -regextype egrep -regex "$HOME/(ana|mini)conda[0-9]?" -print -quit); echo "found $foundDir"
terdon

2

Bạn có thể lặp qua danh sách các tên thư mục bạn muốn kiểm tra và hành động theo nó nếu một trong số chúng tồn tại:

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"

Giải pháp này rõ ràng không cho phép toàn bộ sức mạnh regex, nhưng mở rộng toàn cầu và niềng răng bằng nhau ít nhất là trong trường hợp bạn đã trình bày. Vòng lặp thoát ngay khi một thư mục tồn tại và bỏ đặt biến đã đặt trước đó a. Trong echodòng tiếp theo , việc mở rộng tham số ${a+not } sẽ mở rộng thành không có gì nếu ađược đặt (= không tìm thấy dir) và không phải là khác.


1

Công việc có thể xảy ra xung quanh là tìm kiếm miniconda và anaconda riêng biệt như hình dưới đây

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

Nhưng nếu ai đó có đề xuất, tôi muốn biết lý do tại sao chúng ta không thể vượt qua regex khi tìm kiếm thư mục.


2
Tôi đã nâng cấp điều này - nhưng sau đó nhận ra nó sẽ bị hỏng nếu người dùng có nhiều hơn một thư mục phù hợp (ví dụ: miniconda VÀ miniconda2)
Steeller

@steel ấn: "nó sẽ bị hỏng nếu người dùng có nhiều hơn một thư mục phù hợp" Vâng, điều đó thực sự đúng. Bạn có bất cứ đề nghị làm thế nào để sửa chữa nó?
Jenny

@Jenny Sử dụng một mảng, như trong câu trả lời của Steeldo. shopt -s nullglob; dirs=( "$HOME"/miniconda* "$HOME"/anaconda* ); if (( ${#dirs[@]} > 0 )); then ...
wjandrea

Nếu bạn thay thế ] || [bằng -onó thì ít nhất không nên phá vỡ nếu cả hai thư mục được tìm thấy vì cả hai khối thư mục đều được tìm kiếm trong cùng một bài kiểm tra.
Phượng hoàng

@steel ấn và Jenny: bạn có thể muốn nó phá vỡ sự mơ hồ thay vì chỉ chọn một. Làm cho người dùng chỉ định thư mục của họ thay vì có thể chọn sai. (ví dụ: chỉnh sửa tập lệnh để đặt tên thư mục thay vì chạy mã tự động phát hiện.)
Peter Cordes
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.