2011-11-22 7 views
9

के लिए पिछले दो डीआईआर निकालने में मुझे लगता है कि मैं कुछ आसान, बैश में विफल रहा हूं। मेरे पास एक स्ट्रिंग वेरिएबल है जो निर्देशिका के लिए पूर्ण पथ रखता है। मैं अंतिम दो निर्देशिकाओं को दूसरी स्ट्रिंग में असाइन करना चाहता हूं। उदाहरण के लिए, अगर मेरे पास है:बैश: पथनाम

DIRNAME = /a/b/c/d/e 

मैं करना चाहते हैं:

DIRNAME2 = d/e 

मैं एक साधारण बैश निर्माण या sed आदेश है कि यह करना होगा नहीं है यकीन है, लेकिन यह मुझे भागने है। मैं basename या dirname के सामान्यीकृत संस्करण की तरह सॉर्ट करता हूं जो सिर्फ नाम के चरम हिस्सों को वापस नहीं करता है।

धन्यवाद! डेव

+1

मैं बस बेसनाम के बेसनाम और डायरनाम लेता हूं और उन्हें वापस जोड़ता हूं। –

उत्तर

7
DIRNAME=/a/b/c/d/e 
D2=$(dirname $DIRNAME) 
DIRNAME2=$(basename $D2)/$(basename $DIRNAME) 

या, एक पंक्ति में:

DIRNAME2=$(basename $(dirname $DIRNAME))/$(basename $DIRNAME) 

बैक उद्धरण के साथ है कि खेल की कोशिश मत करो जब तक आप स्वपीड़न में भारी कर रहे हैं। और यदि पथ में रिक्त स्थान हो सकते हैं, तो परिवर्तनीय नाम के चारों ओर डबल कोट्स का उपयोग करें।

यह लगभग किसी भी खोल (यहां तक ​​कि 7 वें संस्करण यूनिक्स बोर्न शैल के साथ काम करेगा - यदि आप बैक-कोट्स का उपयोग करने के मस्तिष्क में शामिल हैं)। bash में, अन्य तंत्र उपलब्ध हैं - अन्य उत्तरों में से कुछ विकल्पों को चित्रित किया गया है, हालांकि expr भी एक पुराना स्कूल समाधान है (यह 7 वें संस्करण यूनिक्स में भी मौजूद था)।

DIRNAME2=`basename \`dirname "$DIRNAME"\``/`basename "$DIRNAME"` 

(घोंसले के दो स्तर भी भयंकर नहीं है, तीन मुश्किल हो जाता है!)

+0

वाह, यह स्टैक ओवरफ्लो पर मेरी पहली पोस्ट है, और मैं प्रतिक्रियाओं से बेहद खुश हूं! मैं w/आपका एक-लाइन बेसनाम-डायरनाम उत्तर गया, क्योंकि यह केवल एक स्क्रिप्ट है जिसे मैं संख्यात्मक नौकरियां जमा करने के हिस्से के रूप में चलाऊंगा। लेकिन मैं इस थ्रेड को बहुत सी स्लिम बैश स्ट्रिंग ट्रिक्स के लिए सहेज दूंगा! – user1059256

+0

+1, यह सबसे अर्थपूर्ण समाधान है। – harpo

1

मुझे लगता है कि globs का उपयोग कर एक छोटा रास्ता है, लेकिन:

$ DIRNAME='a/b/c/d/e' 
$ LAST_TWO=$(expr "$DIRNAME" : '.*/\([^/]*/[^/]*\)$') 
$ echo $LAST_TWO 
d/e 
2

मैं विशेष रूप से पथ ट्रिमिंग के लिए एक विधि के बारे में पता नहीं है, लेकिन आप निश्चित रूप से bash's regular expression matching साथ यह कर सकते हैं:

DIRNAME=/a/b/c/d/e 
if [[ "$DIRNAME" =~ ([^/]+/+[^/]+)/*$ ]]; then 
    echo "Last two: ${BASH_REMATCH[1]}" 
else 
    echo "No match" 
fi 

नोट: मैंने पथ में कुछ अनुमत-लेकिन-सामान्य चीज़ों को संभालने के लिए पैटर्न की तुलना में थोड़ा अधिक जटिल बना दिया है: यह स्लेश का पीछा करते हुए ट्रिम करता है, और कई (अनावश्यक) स्लेश सहन करता है पिछले दो नामों के बीच तों। उदाहरण के लिए, इसे "/ a/b/c // d //" पर चलाना "c // d" से मेल खाता है।

-1
function getDir() { 
echo $1 | awk -F/ ' 
{ 
    n=NF-'$2'+1; 
    if(n<1)exit; 
    for (i=n;i<=NF;i++) { 
     printf("/%s",$i); 
    } 
}' 
} 

dir="https://stackoverflow.com/a/b/c/d/e" 
dir2=`getDir $dir 1` 
echo $dir2 

आप इस समारोह से पिछले से निर्देशिका के किसी भी संख्या प्राप्त कर सकते हैं। अपने मामले के रूप में चलाते के लिए,

dir2=`getDir $dir 2`; 

आउटपुट:/डी/ई

1
DIRNAME="a/b/c/d/e" 
DIRNAME2=`echo $DIRNAME | awk -F/ '{print $(NF-1)"_"$(NF)}'` 

DIRNAME2 then has the value d_e 

बदलें अंडरस्कोर जो कुछ भी आप चाहते हैं।

8

अनावश्यक प्रक्रियाओं से बचने के लिए, जितना मैं कर सकता हूं, उतना ही मैं बिल्टिन का उपयोग करना पसंद करता हूं। चूंकि आपकी स्क्रिप्ट सिगविन या अन्य ओएस के तहत चल सकती है, जिनकी प्रक्रिया निर्माण बहुत महंगा है।

मुझे लगता है कि यह बहुत लंबा नहीं है, तो आप सिर्फ दो dirs निकालना चाहते हैं:

base1="${DIRNAME##*/}" 
dir1="${DIRNAME%/*}" 
DIRNAME2="${dir1##*/}/$base1" 

यह भी विशेष वर्ण एक और आदेशों को क्रियान्वित करने में शामिल समस्याओं से बचने कर सकते हैं।

+1

+1 सर्वश्रेष्ठ समाधान IMHO। – helpermethod

0
dirname=/a/b/c/d/e 
IFS=/ read -a dirs <<< "$dirname" 
printf "%s/%s\n" "${dirs[-2]}" "${dirs[-1]}"