क्या यह $ IFS चर "बैक अप" के लिए एक समझदार दृष्टिकोण है?


19

मैं वास्तव में साथ खिलवाड़ करने से हमेशा हिचकिचाता हूँ $IFSक्योंकि यह एक ग्लोबल क्लोबिंग है।

लेकिन अक्सर यह लोडिंग स्ट्रिंग्स को एक बैश एरे में अच्छा और संक्षिप्त बना देता है, और बैश स्क्रिप्टिंग के लिए, कंसिस्टेंसी मुश्किल से आती है।

इसलिए मुझे लगता है कि यह कुछ भी नहीं की तुलना में बेहतर हो सकता है अगर मैं $IFSकिसी अन्य चर की शुरुआती सामग्री को "सहेजने" की कोशिश करता हूं और फिर $IFSकुछ के लिए उपयोग करने के तुरंत बाद इसे पुनर्स्थापित करता हूं ।

क्या यह व्यावहारिक है? या यह अनिवार्य रूप से व्यर्थ है और मुझे IFSइसके बाद के उपयोगों के लिए जो कुछ भी होना चाहिए, उसे सीधे सेट कर देना चाहिए ?


यह व्यावहारिक क्यों नहीं होगा?
3

क्योंकि परेशान IFS काम ठीक करेगा।
3

1
यह कहने के लिए कि परेशान करने वाले IFS ठीक काम करेंगे, ध्यान रखें कि यह स्थितिजन्य है: stackoverflow.com/questions/39545837/… । मेरे अनुभव में, अपने शेल दुभाषिया के लिए डिफ़ॉल्ट रूप से आईएफएस को मैन्युअल रूप से सेट करना सबसे अच्छा है, अर्थात् $' \t\n'यदि आप बैश का उपयोग कर रहे हैं। unset $IFSबस हमेशा इसे आप क्या डिफ़ॉल्ट होने की उम्मीद करेंगे बहाल नहीं करता है।
डारेल होल्ट

जवाबों:


9

आप आवश्यकतानुसार IFS को सेव और असाइन कर सकते हैं। ऐसा करने में कुछ भी गलत नहीं है। एक अस्थायी, शीघ्र संशोधन के बाद अपने सरणी असाइनमेंट उदाहरण की तरह इसके मूल्य को बचाने के लिए यह असामान्य नहीं है।

जैसा कि @llua ने आपके प्रश्न के लिए अपनी टिप्पणी में उल्लेख किया है, बस IFS को परेशान करने से डिफ़ॉल्ट व्यवहार बहाल होगा, जो स्पेस-टैब-न्यूलाइन असाइन करने के बराबर है।

यह विचार करने लायक है कि ऐसा करने के लिए स्पष्ट रूप से IFS को स्पष्ट रूप से सेट / अनसेट नहीं करना अधिक समस्याग्रस्त हो सकता है।

POSIX 2013 संस्करण से, 2.5.3 शैल चर :

कार्यान्वयन पर्यावरण में IFS के मूल्य, या पर्यावरण से IFS की अनुपस्थिति को अनदेखा कर सकता है, जिस समय शेल को लागू किया जाता है, उस स्थिति में शेल IFS को <space> <टैब> <newline> पर सेट करेगा जब इसे लागू किया जाता है ।

एक POSIX- आज्ञाकारी, आह्वान किया गया शेल अपने वातावरण से IFS को विरासत में ले सकता है या नहीं भी ले सकता है। इस प्रकार से:

  • एक पोर्टेबल स्क्रिप्ट पर्यावरण के माध्यम से भरोसेमंद रूप से IFS को विरासत में नहीं दे सकती है।
  • एक स्क्रिप्ट जो केवल डिफ़ॉल्ट विभाजन व्यवहार (या शामिल होने के मामले में "$*") का उपयोग करने का इरादा रखती है , लेकिन जो एक शेल के तहत चल सकती है जो पर्यावरण से IFS को प्रारंभ करती है, स्पष्ट रूप से पर्यावरण घुसपैठ के खिलाफ बचाव के लिए IFS को सेट / अनसेट करना होगा।

एनबी यह समझना महत्वपूर्ण है कि इस चर्चा के लिए "आह्वान" शब्द का एक विशेष अर्थ है। एक शेल केवल तब ही लागू किया जाता है जब इसे स्पष्ट रूप से अपने नाम का उपयोग करके बुलाया जाता है (एक #!/path/to/shellशबंग सहित )। एक उपधारा - जैसे कि द्वारा बनाया जा सकता है $(...)या cmd1 || cmd2 &- एक आह्वानित शेल नहीं है, और इसका IFS (इसके निष्पादन के अधिकांश वातावरण के साथ) इसके माता-पिता के समान है। एक आह्वान किया गया शेल $अपने पिड का मूल्य निर्धारित करता है , जबकि उप-भाग इसे विरासत में मिलाते हैं।


यह महज एक पांडित्य नहीं है; इस क्षेत्र में वास्तविक विचलन है। यहां एक संक्षिप्त स्क्रिप्ट है जो कई अलग-अलग गोले का उपयोग करके परिदृश्य का परीक्षण करती है। यह एक संशोधित :शेल में एक संशोधित IFS (सेट ) को निर्यात करता है जो तब अपने डिफ़ॉल्ट IFS को प्रिंट करता है।

$ cat export-IFS.sh
export IFS=:
for sh in bash ksh93 mksh dash busybox:sh; do
    printf '\n%s\n' "$sh"
    $sh -c 'printf %s "$IFS"' | hexdump -C
done

IFS को आम तौर पर निर्यात के लिए चिह्नित नहीं किया जाता है, लेकिन, अगर यह होता है, तो ध्यान दें कि कैसे bash, ksh93 और mksh अपने पर्यावरण की उपेक्षा करते हैं IFS=:, जबकि डैश और बिजीबॉक्स इसे सम्मानित करते हैं।

$ sh export-IFS.sh

bash
00000000  20 09 0a                                          | ..|
00000003

ksh93
00000000  20 09 0a                                          | ..|
00000003

mksh
00000000  20 09 0a                                          | ..|
00000003

dash
00000000  3a                                                |:|
00000001

busybox:sh
00000000  3a                                                |:|
00000001

कुछ संस्करण जानकारी:

bash: GNU bash, version 4.3.11(1)-release
ksh93: sh (AT&T Research) 93u+ 2012-08-01
mksh: KSH_VERSION='@(#)MIRBSD KSH R46 2013/05/02'
dash: 0.5.7
busybox: BusyBox v1.21.1

भले ही bash, ksh93, और mksh पर्यावरण से IFS को इनिशियलाइज़ न करें, लेकिन वे अपने संशोधित IFS को फिर से एक्सपोर्ट करते हैं।

यदि किसी कारण से आपको पर्यावरण के माध्यम से IFS को आंशिक रूप से पारित करने की आवश्यकता है, तो आप IFS का उपयोग करके ऐसा नहीं कर सकते हैं; आपको किसी भिन्न चर के लिए मान असाइन करने और निर्यात के लिए उस चर को चिह्नित करने की आवश्यकता होगी। फिर बच्चों को अपने आईएफएस को उस मूल्य को स्पष्ट रूप से निर्दिष्ट करने की आवश्यकता होगी।


मैं देखता हूं, इसलिए अगर मैं विरोधाभास कर सकता हूं, तो यह स्पष्ट रूप से अधिक पोर्टेबल है कि IFSअधिकांश स्थितियों में मूल्य को स्पष्ट रूप से निर्दिष्ट करें जहां इसका उपयोग किया जाना है, और इसलिए यह अक्सर अपने मूल मूल्य को "संरक्षित" करने के प्रयास में बहुत उत्पादक नहीं है।
स्टीवन लू

1
सर्वोपरि मुद्दा यह है कि यदि आपकी स्क्रिप्ट IFS का उपयोग करती है, तो यह सुनिश्चित करने के लिए IFS को स्पष्ट रूप से सेट / अनसेट करना चाहिए कि इसका मूल्य वही है जो आप चाहते हैं। आमतौर पर, आपकी स्क्रिप्ट का व्यवहार IFS पर निर्भर करता है, यदि कोई अनकोटेड पैरामीटर एक्सपेंशन, अनक्वॉटेड कमांड सब्स्टीट्यूशंस, अनक्वॉटेड एरिथमेटिक एक्सपेंसेस, reads, या डबल-कोटेड रेफरेंस हैं $*। वह सूची मेरे सिर के ठीक ऊपर है, इसलिए यह व्यापक नहीं हो सकती है (विशेषकर जब आधुनिक गोले के POSIX- एक्सटेंशन पर विचार करते हुए)।
बेयरफुट IO

10

सामान्य तौर पर, शर्तों को डिफ़ॉल्ट पर वापस करना एक अच्छा अभ्यास है।

हालांकि, इस मामले में, इतना नहीं।

क्यों?:

इसके अलावा, IFS मान को संग्रहीत करने में समस्या है।
यदि मूल IFS परेशान था, तो कोड IFS="$OldIFS"IFS को सेट कर देगा "", न कि उसे परेशान नहीं करेगा।

वास्तव में IFS का मान रखने के लिए (भले ही परेशान), इसका उपयोग करें:

${IFS+"false"} && unset oldifs || oldifs="$IFS"    # correctly store IFS.

IFS="error"                 ### change and use IFS as needed.

${oldifs+"false"} && unset IFS || IFS="$oldifs"    # restore IFS.

IFS वास्तव में परेशान नहीं हो सकता। यदि आप इसे अनसेट करते हैं, तो शेल इसे डिफ़ॉल्ट मान में बदल देता है। इसलिए आपको इसे सहेजते समय वास्तव में इसके लिए जांच करने की आवश्यकता नहीं है।
फिल्मब्रांडेन

खबरदार है कि में bash, unset IFSसेट किए बिना भारतीय विदेश सेवा करने में विफल रहता है, तो यह एक माता पिता के संदर्भ (समारोह संदर्भ) में स्थानीय और वर्तमान संदर्भ में घोषित नहीं किया गया था।
स्टीफन चेजेलस

5

आप वैश्विक रूप से क्लोबिंग के बारे में संकोच करने के लिए सही हैं। डर नहीं, कभी भी वास्तविक वैश्विक को संशोधित किए बिना IFS, या एक बोझिल और त्रुटि से बचाने / नृत्य को बहाल करने के बिना स्वच्छ कार्य कोड लिखना संभव है ।

आप ऐसा कर सकते हैं:

  • एकल आह्वान के लिए IFS सेट करें:

    IFS=value command_or_function

    या

  • सेट के अंदर IFS सेट करें:

    (IFS=value; statement)
    $(IFS=value; statement)

उदाहरण

  • किसी सरणी से अल्पविराम-युक्त स्ट्रिंग प्राप्त करने के लिए:

    str="$(IFS=, ; echo "${array[*]-}")"

    ध्यान दें: -केवल एक खाली सरणी को सुरक्षित करने के लिए एक डिफ़ॉल्ट मानset -u प्रदान करने के खिलाफ है जब परेशान (जो इस मामले में खाली स्ट्रिंग जा रहा है)

    IFSसंशोधन subshell द्वारा पैदा की अंदर ही लागू होता है $() आदेश प्रतिस्थापन । इसका कारण यह है कि उप-समूह के पास इनवॉइसिंग शेल के चर की प्रतियां हैं और इसलिए वे अपने मूल्यों को पढ़ सकते हैं, लेकिन उपधारा द्वारा किए गए किसी भी संशोधन केवल उपधारा की प्रतिलिपि को प्रभावित करते हैं न कि माता-पिता के चर को।

    आप यह भी सोच रहे होंगे: क्यों न सबस्क्रिप्शन को छोड़ें और ऐसा करें:

    IFS=, str="${array[*]-}"  # Don't do this!

    यहां कोई कमांड इनवोकेशन नहीं है, और इस लाइन को दो स्वतंत्र बाद के चर असाइनमेंट के रूप में व्याख्या किया गया है, जैसे कि यह थे:

    IFS=,                     # Oops, global IFS was modified
    str="${array[*]-}"

    अंत में, आइए बताते हैं कि यह संस्करण क्यों काम नहीं करेगा:

    # Notice missing ';' before echo
    str="$(IFS=, echo "${array[*]-}")" # Don't do this! 

    echoआदेश वास्तव में इसके साथ बुलाया जाएगा IFSकरने के लिए चर सेट ,है, लेकिन echoपरवाह या उपयोग नहीं करता है IFS"${array[*]}"एक स्ट्रिंग में विस्तार करने का जादू (उप) शेल द्वारा ही echoकिया जाता है, इससे पहले कि इसे भी लागू किया जाए।

  • पूरी फ़ाइल में पढ़ने के लिए (जिसमें NULLबाइट्स नहीं हैं ) नाम के एक एकल चर में VAR:

    IFS= read -r -d '' VAR < "${filepath}"

    नोट: IFS=जैसा है IFS=""और IFS=''सभी, जिनमें से सभी IFS को खाली स्ट्रिंग पर सेट करते हैं, जो इससे बहुत अलग है unset IFS: यदि IFSसेट नहीं किया गया है, तो सभी बैश फंक्शंस का व्यवहार जो आंतरिक रूप से उपयोग IFSहोता है, ठीक उसी तरह है जैसे IFSकि डिफ़ॉल्ट मान था $' \t\n'

    IFSखाली स्ट्रिंग पर सेट करना सुनिश्चित करता है कि अग्रणी और अनुगामी व्हॉट्सएप संरक्षित है।

    -d ''या -d ""केवल एक को अपनी वर्तमान मंगलाचरण को रोकने के लिए पढ़ा बताता है NULLहमेशा की तरह नई पंक्ति के बजाय बाइट,।

  • $PATHइसके :सीमांकक के साथ विभाजित करने के लिए :

    IFS=":" read -r -d '' -a paths <<< "$PATH"

    यह उदाहरण विशुद्ध रूप से सचित्र है। सामान्य मामले में जहां आप एक सीमांकक के साथ बंटवारा कर रहे हैं, व्यक्तिगत क्षेत्रों के लिए यह संभव है कि उस परिसीमन में (एक बचा हुआ संस्करण) समाहित हो। एक .csvफ़ाइल की एक पंक्ति में पढ़ने की कोशिश करने के बारे में सोचें, जिसके कॉलम में स्वयं अल्पविराम हो सकता है (किसी तरह से बच या उद्धृत)। इस तरह के मामलों के लिए उपरोक्त स्निपेट काम नहीं करेगा।

    उस ने कहा, आप इस तरह के :-पथों का सामना करने की संभावना नहीं रखते हैं $PATH। हालांकि UNIX / Linux pathnames को सम्‍मिलित करने की अनुमति है :, ऐसा लगता है कि बैश ऐसे रास्तों को संभालने में सक्षम नहीं होगा, यदि आप उन्हें अपनी जोड़कर और उनमें $PATHनिष्पादन योग्य फ़ाइलों को संग्रहीत करने का प्रयास करते हैं , क्योंकि कॉलस को बच / पार्स करने के लिए कोई कोड नहीं है : बैश का सोर्स कोड 4.4

    अंत में, ध्यान दें कि स्निपेट परिणामी सरणी के अंतिम तत्व के रूप में एक अनुगामी न्यूलाइन को जोड़ देता है (जैसा कि अब-हटाई गई टिप्पणियों में @ StéphaneChazelas द्वारा कहा जाता है), और यदि इनपुट खाली स्ट्रिंग है, तो आउटपुट एक एकल-तत्व होगा सरणी, जहां तत्व में एक नई रेखा ( $'\n') होगी।

प्रेरणा

old_IFS="${IFS}"; command; IFS="${old_IFS}"वैश्विक IFSको छूने वाले मूल दृष्टिकोण स्क्रिप्ट के सबसे सरल के लिए अपेक्षा के अनुरूप काम करेंगे। हालाँकि, जैसे ही आप कोई जटिलता जोड़ते हैं, यह आसानी से टूट सकता है और सूक्ष्म मुद्दों का कारण बन सकता है:

  • यदि commandएक बैश फ़ंक्शन है जो ग्लोबल को भी संशोधित करता है IFS(या तो सीधे, या दृश्य से छिपा हुआ है, अभी तक किसी अन्य फ़ंक्शन के अंदर जिसे वह कॉल करता है), और ऐसा करते समय गलती old_IFSसे सेव / रिस्टोर करने के लिए एक ही वैश्विक चर का उपयोग करता है , तो आपको एक बग मिलता है।
  • जैसा कि @Gilles की इस टिप्पणी में बताया गया है , यदि मूल स्थिति असंतुष्ट थी , तो IFSभोली बचाने और बहाल करने से काम नहीं चलेगा, और यदि सामान्य रूप से (गलत) इस्तेमाल किया गया set -u(उर्फ set -o nounset) शेल विकल्प है, तो इसका परिणाम भी ठीक नहीं होगा। बल में है।
  • कुछ शेल कोड के लिए मुख्य निष्पादन प्रवाह के लिए एसिंक्रोनस रूप से निष्पादित करना संभव है, जैसे सिग्नल हैंडलर (देखें help trap)। यदि वह कोड वैश्विक रूप से भी संशोधित होता है IFSया यह मान लेता है कि इसका कोई विशेष मूल्य है, तो आप सूक्ष्म कीड़े प्राप्त कर सकते हैं।

आप अधिक मजबूत बचत / पुनर्स्थापना अनुक्रम तैयार कर सकते हैं (जैसे कि इनमें से कुछ या सभी समस्याओं से बचने के लिए इस अन्य उत्तर में प्रस्तावित एक है। हालांकि, आपको शोर बॉयलरप्लेट कोड के उस टुकड़े को दोहराना होगा, जहां आपको अस्थायी रूप से एक कस्टम की आवश्यकता है IFS। " कोड पठनीयता और रखरखाव को कम करता है।

पुस्तकालय जैसी लिपियों के लिए अतिरिक्त विचार

IFSविशेष रूप से शेल फ़ंक्शन लाइब्रेरीज़ के लेखकों के लिए एक चिंता का विषय है, जिन्हें वैश्विक कोड ( IFS, शेल ऑप्शंस, ...) की परवाह किए बिना अपने कोड को मज़बूती से सुनिश्चित करने की आवश्यकता है , और यह भी कि राज्य को परेशान किए बिना (इनवोकर्स भरोसा कर सकते हैं) उस पर हमेशा स्थिर रहने के लिए)।

पुस्तकालय कोड लिखते समय, आप IFSकिसी विशेष मूल्य (डिफ़ॉल्ट एक भी नहीं) या यहां तक ​​कि बिल्कुल भी सेट होने पर भरोसा नहीं कर सकते । इसके बजाय, आपको IFSकिसी भी स्निपेट के लिए स्पष्ट रूप से सेट करने की आवश्यकता है जिसका व्यवहार निर्भर करता है IFS

यदि IFSकोड की प्रत्येक पंक्ति में स्पष्ट रूप से आवश्यक मान पर सेट किया जाता है (भले ही वह डिफ़ॉल्ट एक हो) जहां इस उत्तर में वर्णित दो तंत्रों में से जो भी उपयोग करता है, वह मूल्य प्रभाव को स्थानीय बनाने के लिए उपयुक्त है, तो कोड दोनों है वैश्विक राज्य से स्वतंत्र और पूरी तरह से इसे बंद करने से बचा जाता है। इस दृष्टिकोण का स्क्रिप्ट पढ़ने वाले किसी व्यक्ति के लिए यह बहुत स्पष्ट करने का अतिरिक्त लाभ है जो IFSन्यूनतम एक पाठीय लागत पर इस एक आदेश / विस्तार के लिए मायने रखता है (यहां तक ​​कि सबसे बुनियादी बचत / पुनर्स्थापना की तुलना में)।

क्या कोड IFSवैसे भी प्रभावित है ?

सौभाग्य से, ऐसे कई परिदृश्य नहीं हैं जहाँ IFSमायने रखते हैं (यह मानते हुए कि आप हमेशा अपने विस्तार को उद्धृत करते हैं ):

  • "$*"और "${array[*]}"विस्तार
  • का आमंत्रण readमें निर्मित कई चर (लक्षित कर read VAR1 VAR2 VAR3) या किसी सरणी चर ( read -a ARRAY_VAR_NAME)
  • readएक एकल चर को लक्षित करने के आह्वान जब यह व्हाट्सएप या गैर-व्हाट्सएप वर्णों में अग्रणी / अनुगामी आता है IFS
  • शब्द-विभाजन (जैसे कि अनछुए विस्तार के लिए, जिसे आप प्लेग से बचना चाहते हैं )
  • कुछ अन्य कम आम परिदृश्य (देखें: IFS @ ग्रेगस विकी )

मैं यह नहीं कह सकता कि मैं इसके साथ $ PATH को विभाजित करने के लिए समझता हूं : किसी भी घटक के मानने वाले सीमांकक में a: खुद वाक्य है। :जब :सीमांकक होता है तो घटक कैसे हो सकते हैं ?
स्टीफन चेजेलस

@ StéphaneChazelas खैर, :अधिकांश UNIX / Linux फाइल सिस्टम पर फ़ाइल नाम में उपयोग करने के लिए एक वैध चरित्र है, इसलिए यह एक नाम वाले निर्देशिका के साथ पूरी तरह से संभव है :। संभवत: कुछ गोले :जैसे कुछ का उपयोग करके पीएटीएच में भागने का प्रावधान है \:, और फिर आपको ऐसे कॉलम दिखाई देंगे जो वास्तविक सीमांकक नहीं हैं (ऐसा लगता है कि बैश इस तरह के भागने की अनुमति नहीं देता है। निम्न स्तर के फ़ंक्शन का उपयोग तब किया जाता है जब $PATHसिर्फ खोज के लिए खोज की जाती है :। एक सी स्ट्रिंग: git.savannah.gnu.org/cgit/bash.git/tree/general.c#n891 )।
SLS

मैंने उम्मीद की कि उत्तर को और अधिक स्पष्ट करने के $PATHलिए बंटवारे के उदाहरण को संशोधित किया जाए :
SLS

1
एसओ में आपका स्वागत है! इस तरह के एक गहराई से उत्तर के लिए धन्यवाद :)
स्टीवन लू

1

क्या यह व्यावहारिक है? या यह अनिवार्य रूप से व्यर्थ है और मुझे इसके उपयोग के लिए जो कुछ भी होना चाहिए, उसे सीधे IFS को सीधे सेट करना चाहिए?

IFS सेटिंग करने के लिए टाइपो को जोखिम में क्यों डालना है $' \t\n'जब आपको करना है

OIFS=$IFS
do_your_thing
IFS=$OIFS

वैकल्पिक रूप से, यदि आप किसी भी चर सेट / संशोधित करने की आवश्यकता नहीं है, तो आप एक उपखंड को कॉल कर सकते हैं:

( IFS=:; do_your_thing; )

यह खतरनाक है क्योंकि यह काम नहीं करता है अगर IFSशुरू में परेशान था।
गिलेस एसओ- बुराई को रोकना '
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.