बैश में डायरेक्टरी पाथ पाकर रेगेक्स कैसे पास करें?


14

मैंने एक छोटी बैश स्क्रिप्ट लिखी है ताकि यह पता लगाया जा सके कि एक निर्देशिका नाम anacondaया minicondaमेरे उपयोगकर्ता में है $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

पुनश्च: यदि मेरे पास है [ -d "$HOME"/miniconda2 ]; then, तो यह miniconda2 निर्देशिका पाता है, इसलिए मुझे लगता है कि त्रुटि भाग में है"(ana|mini)conda[0-9]?"

मैं चाहता हूं कि स्क्रिप्ट सामान्य हो। मेरे लिए, यह miniconda2 है, लेकिन कुछ अन्य उपयोगकर्ता के लिए यह anaconda2, miniconda3 आदि हो सकता है।


एक अन्य उपयोगकर्ता anaconda_2 या -2 या -may2019 का उपयोग कर सकता है। तो xxxconda * बेहतर नहीं होगा?
विनयुनुच्स 2 यूनिक्स

2
बैश फाइलन विस्तार में ग्लोब एक्सप्रेशन का उपयोग किया गया है, न कि रेक्सक्स पर।
पीटर कॉर्डेस

जवाबों:


13

यह अच्छी तरह से करने के लिए आश्चर्यजनक रूप से मुश्किल काम है।

मौलिक रूप से, -dकेवल एक तर्क का परीक्षण करेंगे - भले ही आप नियमित अभिव्यक्ति का उपयोग करके फ़ाइल नाम से मेल खा सकते हों।

एक तरीका यह होगा कि समस्या को इधर-उधर किया जाए, और डायरेक्टरी के लिए रेगेक्स मैच का परीक्षण करने के बजाय रेगेक्स मैच के लिए डायरेक्ट्री टेस्ट करें। दूसरे शब्दों में, एक सरल शेल ग्लोब का उपयोग करने में सभी निर्देशिकाओं पर लूप करें $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

  • (विस्तारित ग्लोब संस्करण): dirs=( "$HOME"/**/@(ana|mini)conda?([0-9])/ )


1
मैं सरणी मार्ग पर जाऊँगा। आप उपयोग कर सकते हैं ?([0-9])के स्थान पर @(|[0-9])- ?(...)मैचों शून्य या एक, regex के रूप में ही ?परिमाणक।
ग्लेन जैकमैन

2
आपको एक्सग्लोब की भी आवश्यकता नहीं है क्या आप ब्रेस विस्तार का उपयोग कर रहे हैं (यह सभी संभव मिलान नाम उत्पन्न करता है):~/{ana,mini}conda{0..9}*/
xenoid

वहाँ है कि यह आयोजन करेगा या तो इन समाधानों में से संपादित करें तो करने के लिए वैसे भी है यहां तक कि अगर miniया anacondaमें स्थापित किया गया है $HOME/sub-directories? उदाहरण के लिए$HOME/sub-dir1/sub-dir2/miniconda2
जेनी

1
@ जेनी कृपया मेरा संपादन देखेंglobstar
स्टीलड्रिवर

1
@terdon हाँ, मैं वास्तव में मैच के लिए "सही" चीज़ के खरगोश के छेद से नीचे नहीं जाना चाहता था - मैंने ओपी के रेगेक्स को लिया जैसे कि एक सामान्य दृष्टिकोण को दर्शाने के उद्देश्य से है
स्टीलड्राइवर

9

वास्तव में, जैसा कि पहले ही उल्लेख किया गया है, यह मुश्किल है। मेरा दृष्टिकोण निम्नलिखित है:

  • सवाल में निर्देशिकाओं को खोजने के लिए उपयोग findऔर इसकी rexx क्षमताओं।
  • चलो findएक मुद्रित xप्रत्येक पाया निर्देशिका के लिए
  • xएक स्ट्रिंग में तों स्टोर
  • यदि स्ट्रिंग गैर-रिक्त है, तो एक निर्देशिका मिली थी।

इस प्रकार:

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
जेनी

हाय पर्लडक: धन्यवाद। एक अच्छा जवाब भी। लेकिन मुझे एक त्रुटि मिलती है printfउदाहरण के लिए जब मैं स्क्रिप्ट चलाता हूं, तो यह ठीक चलता है, लेकिन यह प्रिंटफ कमांड नहीं पाता है जब कोई मैच नहीं होता है, लेकिन मुझे लगता है कि यह इसलिए है क्योंकि प्रिंट करने के लिए कुछ भी नहीं है। -bash: -printf: command not found no match.
जेनी

3
@ जेनी आपने इसे कॉपी करते समय एक टाइपो बनाया होगा, क्योंकि यह मेरे लिए ठीक काम करता है। -printfएक आदेश नहीं है, लेकिन एक तर्क है find। पिछली लाइन के अंत में बैकस्लैश यही करता है।
वंडारेड

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"

यह समाधान स्पष्ट रूप से पूर्ण रेगेक्स शक्ति की अनुमति नहीं देता है, लेकिन शेल ग्लोबिंग और ब्रेस विस्तार कम से कम आपके द्वारा दिखाए गए मामले में बराबर है। जैसे ही एक निर्देशिका मौजूद होती है, लूप बाहर निकल जाता है और पहले से निर्धारित चर को खोल देता है a। बाद की echoपंक्ति में, पैरामीटर विस्तार ${a+not } कुछ भी नहीं है अगर aसेट (= कोई dir पाया) और "नहीं" है।


1

आस-पास के संभावित कार्य मिनिकोंडा और एनाकोंडा को अलग-अलग खोज रहे हैं जैसा कि नीचे दिखाया गया है

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
मैंने इसे बढ़ा दिया - लेकिन तब एहसास हुआ कि यदि उपयोगकर्ता के पास एक से अधिक मिलान निर्देशिका (जैसे मिनीकोन्डा और मिनीकोन्डा 2) है तो
टूट जाएगी

@steeldriver: "यदि उपयोगकर्ता के पास एक से अधिक मिलान निर्देशिका है तो यह टूट जाएगी" हां, यह वास्तव में सच है। क्या आपके पास कोई सुझाव है कि इसे कैसे ठीक किया जाए?
जेनी

@ जेनी एक सरणी का उपयोग करें, जैसे कि स्टीलड्राइवर के उत्तर में। shopt -s nullglob; dirs=( "$HOME"/miniconda* "$HOME"/anaconda* ); if (( ${#dirs[@]} > 0 )); then ...
वंडारेड

यदि आप इसके ] || [साथ प्रतिस्थापित करते हैं -oतो कम से कम नहीं टूटना चाहिए यदि दोनों निर्देशिकाओं को पाया जाता है क्योंकि दोनों निर्देशिका ग्लब्स को एक ही परीक्षण में देखा जाता है।
फीनिक्स

@steeldriver और जेनी: आप इसे केवल एक लेने के बजाय अस्पष्टता पर तोड़ सकते हैं । उपयोगकर्ता को गलत निर्देशिका चुनने के बजाय उनकी निर्देशिका निर्दिष्ट करें। (उदाहरण के लिए ऑटो-डिटेक्शन कोड को चलाने के बजाय dir नाम सेट करने के लिए स्क्रिप्ट को संपादित करें।)
पीटर कॉर्ड्स
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.