शेलचेक बेसन का उपयोग नहीं करने की सलाह दे रहा है: क्यों?


25

मैं शंखनाद कर रहा हूं

मेरे पास कुछ ऐसा है

basename "${OPENSSL}" 

और मुझे निम्नलिखित सुझाव मिले

Use parameter expansion instead, such as ${var##*/}.

व्यावहारिक दृष्टिकोण से मुझे कोई अंतर नहीं दिखता

$ export OPENSSL=/opt/local/bin/openssl
$ basename ${OPENSSL}
openssl
$ echo ${OPENSSL##*/}
openssl

चूंकि basenameमें है POSIX चश्मा , मैं एक कारण है कि यह सबसे अच्छा अभ्यास किया जाना चाहिए नहीं है। कोई संकेत?


8
यह एक नई प्रक्रिया की जरूरत है जब यह करने की जरूरत नहीं है।
जोर्डनम

@ जोर्डनम मेला काफी ... मैंने दक्षता के बारे में नहीं सोचा था।
मट्टियो

3
@jordanm दूसरी ओर यह बैश के अलावा अन्य गोले पर काम करता है
Matteo

1
@JosephR। यही मैंने सोचा था लेकिन अभी पता चला है कि यह काम नहीं करता है csh। मुझे लगता cshहै कि POSIX नहीं है।
terdon

3
@terdon - csh POSIX से बहुत दूर है।
जोर्डनम

जवाबों:


25

यह दक्षता के बारे में नहीं है - यह शुद्धता के बारे में है। basenameउपयोग किए गए फ़ाइलनामों को हटाने के लिए नई लाइनों का उपयोग करता है। सामान्य स्थिति में जब आप केवल एक फाइलनाम पास करते हैं, तो यह अपने आउटपुट में एक अनुगामी न्यूलाइन जोड़ता है। चूंकि फ़ाइलनामों में स्वयं नई लाइनें हो सकती हैं, इससे इन फ़ाइलनामों को सही ढंग से संभालना मुश्किल हो जाता है।

यह इस तथ्य से और जटिल है कि लोग आमतौर पर basenameइस तरह का उपयोग करते हैं "$(basename "$file")":। यह बातें और भी मुश्किल बना देता है, क्योंकि $(command)स्ट्रिप्स सब से पीछे नई-पंक्तियों command$fileएक नई रेखा के साथ समाप्त होने वाले असंभावित मामले पर विचार करें । फिर basenameएक अतिरिक्त न्यूलाइन जोड़ देगा, लेकिन आपको गलत फ़ाइल नाम के साथ छोड़ते हुए, दोनों नई सुर्खियों को हटा "$(basename "$file")"देगा ।

इसके साथ एक और समस्या basenameयह है कि यदि $fileएक -(डैश उर्फ ​​माइनस) के साथ शुरू होता है , तो इसे एक विकल्प के रूप में व्याख्या किया जाएगा। इसे ठीक करना आसान है:$(basename -- "$file")

उपयोग करने का मजबूत तरीका basenameयह है:

# A file with three trailing newlines.
file=$'/tmp/evil\n\n\n'

# Add an 'x' so we can tell where $file's newlines end and basename's begin.
file_x="$(basename -- "$file"; printf x)"

# Strip off two trailing characters: the 'x' added by us and the newline added by basename. 
base="${file_x%??}"

एक विकल्प का उपयोग करना है ${file##*/}, जो आसान है लेकिन अपने स्वयं के बग हैं। विशेष रूप से, यह ऐसे मामलों में जहां में गलत क्या है $fileहै /या foo/


2
बहुत अच्छे अंक, +1। खुशी है कि ओपी ने मेरी बजाय यह स्वीकार कर लिया।
टेराडो

2
प्रतिवाद: यदि $fileहै तो क्या है foo/? अगर है तो क्या है /?
गिल्स एसओ- बुराई को रोकें '

2
@ गिल्स: आप सही कह रहे हैं। मुझे लगता है कि basenameदृष्टिकोण सब के बाद बेहतर है, के रूप में हैकी के रूप में शुरू कर रहा हूँ । मेरे पास सबसे अच्छे विकल्प हो सकते हैं ${${${file}%%/#}##*/}और [[ $file =~ "([^/]*)/*$" ]] && printf "%s" $match[1], दोनों ही ज़ेड-स्पेसिफिक हैं और दोनों में से कोई भी /सही ढंग से हैंडल नहीं करता है।
मैट

@terdon धन्यवाद कि आपने इसे व्यक्तिगत रूप से नहीं लिया :-)। नई सुर्खियों वाली फाइलें आम नहीं हैं लेकिन मैट में एक बिंदु है। बेशक चर प्रतिस्थापन का उपयोग करना भी अधिक कुशल है।
माटेओ

16

में प्रासंगिक लाइनों shellcheckके स्रोत कोड हैं:

checkNeedlessCommands (T_SimpleCommand id _ (w:_)) | w `isCommand` "dirname" =
    style id "Use parameter expansion instead, such as ${var%/*}."
checkNeedlessCommands (T_SimpleCommand id _ (w:_)) | w `isCommand` "basename" =
    style id "Use parameter expansion instead, such as ${var##*/}."
checkNeedlessCommands _ = return ()

स्पष्ट रूप से कोई स्पष्टीकरण नहीं दिया गया है, लेकिन फ़ंक्शन के नाम पर आधारित है ( checkNeedlessCommands) ऐसा लगता है कि @jordanm काफी सही है और यह सुझाव दे रहा है कि आप एक नई प्रक्रिया के लिए बचने से बचें।


7
स्रोत आपके साथ हो सकता है :)
जोसेफ आर।

3

dirname, basename, readlinkआदि (धन्यवाद @Marco - यह सही है) पोर्टेबिलिटी समस्या पैदा कर सकता है, जब सुरक्षा महत्वपूर्ण हो जाता है (पथ की सुरक्षा की आवश्यकता होती है)। कई सिस्टम (जैसे फेडोरा लिनक्स) इसे जगह देते हैं /binजबकि अन्य (जैसे मैक ओएसएक्स) इसे जगह पर रखते हैं /usr/bin। फिर विंडोज पर बैश है, जैसे कि साइबरविन, एमएसआईएस, और अन्य। शुद्ध बैश रहना हमेशा बेहतर होता है, जब संभव हो। (प्रति @ मार्को टिप्पणी)

BTW, सूचक को शेलचेक के लिए धन्यवाद, मैंने पहले ऐसा नहीं देखा है।


1
1) "सुरक्षा" से आपका क्या तात्पर्य है? क्या आप विस्तार से समझा सकते हैं? 2) ओपी का उल्लेख बिल्कुल नहीं है dirname। 3) बेसिक कोर यूटिलिटीज को PATH में होना चाहिए, जहां भी वे संग्रहीत हैं। मैंने अभी तक एक ऐसी प्रणाली नहीं देखी है basenameजो पेटीएम में नहीं थी। 4) मान लें कि बैश उपलब्ध है, एक पोर्टेबिलिटी समस्या है। पोर्टेबिलिटी की आवश्यकता होने पर बैश से दूर रहना और पॉसिक्स शेल का उपयोग करना हमेशा बेहतर होता है।
मार्को

@ मर्को इन मुद्दों को इंगित करने के लिए धन्यवाद। हाँ आप सही हैं कि उपयोगिताओं के रास्ते पर हैं। लेकिन जहां कोई बैश स्क्रिप्ट में अतिरिक्त सुरक्षा प्रदान करना चाहता है, यह पूर्ण पथ प्रदान करने के लिए अच्छा अभ्यास है। इसलिए '/ बिन / बेसन' को कॉल करना RedHat सिस्टम के लिए काम करेगा, लेकिन यह एक मैक पर एक त्रुटि उत्पन्न करेगा। यह बैश कुकबुक में बेहतर तरीके से समझाया गया है, जहां लगभग 600 पृष्ठों में से एक चौथाई इस विषय के लिए समर्पित है। हमने अपने फ्री सोर्स सिक्योर-लिब में वहां के सुरक्षा मुद्दों के समाधान के लिए एक कठिन प्रयास किया है ।
AsymLabs

@ मार्को प्वाइंट 4 एक वैध टिप्पणी है लेकिन प्रश्न (ओपी) के साथ शुरू होता है और शेलचेक के चारों ओर लिखा जाता है जिसे दोनों श / बैश लिपियों की जांच करने के लिए डिज़ाइन किया गया है। इसलिए हमें यह मान लेना चाहिए कि यह डिफ़ॉल्ट रूप से पॉज़िक्स नहीं है।
AsymLabs

@ मार्को मार्ग वातावरण चर की सुरक्षा आसानी से समझौता किया जा सकता है। उदाहरण के लिए, मुझे कुछ साल पहले यह जानकर बहुत आश्चर्य हुआ था कि स्थानीय रूप से स्थापित रूबी आरवीएम, सिस्टम पथ से पहले पथ द्वारा रखा गया है।
असिमलैब्स

ओपी ने विशेष रूप से POSIX का उल्लेख किया है posixऔर प्रश्न को टैग किया गया है और नहीं bash। मैं किसी भी संकेतक को खोजने में विफल रहा कि ओपी को बैश समाधान की आवश्यकता है। आपका कथन "शुद्ध बैश रहना हमेशा बेहतर होता है" केवल स्पष्ट गलत है, मुझे क्षमा करें।
मार्को
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.