Bash'de bir dizin yolu bulurken normal ifade nasıl aktarılır?


14

Kullanıcı adımı anacondaveya dizinini bulmak için küçük bir bash betiği yazdım . Ama dizini evimde bulamıyor .miniconda$HOMEminiconda2

Bunu nasıl düzeltebilirim?

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: Eğer varsa [ -d "$HOME"/miniconda2 ]; then, o zaman miniconda2 dizini bulur, bu yüzden hata kısmında yatıyor düşünüyorum"(ana|mini)conda[0-9]?"

Senaryonun genel olmasını istiyorum. Benim için miniconda2 ama diğer bazı kullanıcılar için anaconda2, miniconda3 ve benzeri olabilir.


Başka bir kullanıcı anaconda_2 veya -2 veya -may2019 kullanabilir. Peki xxxconda * daha iyi olmaz mıydı?
WinEunuuchs2Unix

2
Bash dosya adı genişletmesi, normal ifadeler yerine glob ifadeleri kullanır.
Peter Cordes

Yanıtlar:


13

Bu güzel yapmak şaşırtıcı derecede zor bir şeydir.

Temel olarak, -dnormal bir ifade kullanarak dosya adlarını eşleştirseniz bile, yalnızca tek bir argümanı test eder.

Bunun bir yolu, sorunu tersine çevirmek ve dizinler için normal ifade eşleşmesini test etmek yerine dizinleri normal ifade eşleşmesi için test etmektir. Başka bir deyişle, basit bir kabuk glob kullanarak tüm dizinler üzerinde döngü $HOMEve her bir regex karşı test, bir maç kırma, nihayet BASH_REMATCHdizi boş olmadığını test :

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

Alternatif bir yol, normal ifade yerine genişletilmiş bir kabuk glob kullanmak ve bir dizideki herhangi bir glob eşleşmesini yakalamak olabilir. Ardından dizinin boş olup olmadığını test edin:

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

İzleyen /sadece dizinlerin eşleşmesini sağlar; nullglobSıfır maçların durumunda eşsiz dize döndürmesini kabuk engeller.


Her iki özyinelemeyi yapmak için, globstarkabuk seçeneğini ( shopt -s globstar) ve ardından sırasıyla ayarlayın:

  • (normal ifade sürümü): for d in "$HOME"/**/; do

  • (genişletilmiş glob versiyonu): dirs=( "$HOME"/**/@(ana|mini)conda?([0-9])/ )


1
Dizi rotasına giderdim. Normal ifade nicelik belirteciyle aynı - sıfır veya bir eşleşmeleri ?([0-9])yerine kullanabilirsiniz . @(|[0-9])?(...)?
glenn jackman

2
~/{ana,mini}conda{0..9}*/
Extglob'a

Bu çözümlerden herhangi birini , kurulu olsa veya kurulsa bile tutacak şekilde düzenlemek için yine de var mı ? Örneğinminianaconda$HOME/sub-directories$HOME/sub-dir1/sub-dir2/miniconda2
Jenny

1
@Jenny lütfen ilgili düzenlememe bakınglobstar
steeldriver

1
@terdon evet gerçekten "doğru" şey ne tavşan deliğini aşağı gitmek istemiyordu - ben sadece genel bir yaklaşım göstermek için olduğu gibi OP'nin regex aldı
13

9

Gerçekten de, daha önce de belirtildiği gibi, bu zor. Benim yaklaşımım şudur:

  • kullanmak findve düzenli ifade yetenekleri söz konusu dizinleri bulmak için.
  • let findbir baskı xher bulundu dizin için
  • xes bir dizede saklamak
  • dize boş değilse, dizinlerden biri bulundu.

Böylece:

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

Açıklama:

  • find $HOME -maxdepth 1aşağıda her şeyi bulur $HOME ancak aramayı bir düzeyle sınırlar (yani: alt dizinlere geri çekilmez).
  • -type daramayı yalnızca directories ile kısıtlar
  • -regextype egrepfindne tür düzenli ifadelerle uğraştığımızı söyler . Şeyler gibi, çünkü bu gereklidir [0-9]?ve (…|…)biraz özeldir ve find bunları varsayılan olarak tanımıyor.
  • -regex "$HOME/(ana|mini)conda[0-9]?"dikkat etmek istediğimiz gerçek düzenli ifadedir
  • -printf 'x'sadece önceki koşulları karşılayan xher şey için bir baskı yapar .

Bir maç olduğunda. -bash: -regex: command not found found one of the directories
Jenny

Merhaba PerlDuck: Teşekkürler. Çok güzel bir cevap. Ama printförneğin bir komut dosyası çalıştırdığımda bir hata alıyorum , tamam çalışıyor ama hiçbir eşleşme olmadığında printf komutunu bulamıyor ama bence yazdırılacak hiçbir şey yok mu ?. -bash: -printf: command not found no match.
Jenny

3
@Jenny Benim için iyi çalıştığı için kopyalarken bir yazım hatası yapmış olabilirsiniz. -printfbir komut değil, bir argüman find. Önceki satırın sonunda ters eğik çizgi bunu yapar.
wjandrea

1
Belirsizliği tespit etmeye devam etmek istemiyorsanız-quit , bulunan yolu yazdırdıktan sonra öneririm .
Peter Cordes

Neden asıl yolu yazdırmıyorsunuz? Zaten var, bu yüzden atmak ve xbunun yerine kullanmak için bir utanç gibi görünüyor :foundDir=$(find $HOME -maxdepth 1 -type d -regextype egrep -regex "$HOME/(ana|mini)conda[0-9]?" -print -quit); echo "found $foundDir"
terdon

2

Test etmek istediğiniz dizin adlarının bir listesi üzerinde gezinebilir ve bunlardan biri varsa üzerinde hareket edebilirsiniz:

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"

Bu çözüm, tam regex gücüne izin vermiyor, ancak kabuk globbing ve brace genişlemesi en azından gösterdiğiniz durumda eşittir. Bir dizin var olur olmaz döngü çıkar ve önceden ayarlanan değişkeni kaldırır a. Sonraki echosatırda, parametre genişletmesi ayarlanmışsa (= dir bulunamadı) ve başka “değil” ise ${a+not }hiçbir şeye genişler a.


1

Olası çözüm, aşağıda gösterildiği gibi miniconda ve anaconda'yı ayrı ayrı aramaktır

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

Ancak birisinin önerileri varsa, dizinleri ararken neden normal ifadeyi geçemediğimizi bilmek istiyorum.


2
Bunu iptal ettim - ancak daha sonra kullanıcının birden fazla eşleşen dizine sahip olması durumunda kırılacağını fark ettim (örn. Miniconda AND miniconda2)
steeldriver

@steeldriver: "Kullanıcının birden fazla eşleşen dizini varsa bozulur" Evet, bu gerçekten doğrudur. Bunu nasıl düzeltebileceğinizle ilgili önerileriniz var mı?
Jenny

@Jenny Steeldriver'ın cevabında olduğu gibi bir dizi kullanın. shopt -s nullglob; dirs=( "$HOME"/miniconda* "$HOME"/anaconda* ); if (( ${#dirs[@]} > 0 )); then ...
wjandrea

Eğer değiştirirseniz ] || [ile -okendisine hem dizin Neználkovo aynı testte aranır olarak her iki dizin bulunursa en az kırmak olmamalıdır.
Phoenix

@steeldriver ve Jenny: Sadece bir tane seçmek yerine belirsizliği bozmasını isteyebilirsiniz . Yanlış dizini seçmek yerine kullanıcının dizinini belirtmesini sağlayın. (örneğin, otomatik algılama kodunu çalıştırmak yerine dizin adını ayarlamak için komut dosyasını düzenleyin.)
Peter Cordes
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.