एक / बिन / श समारोह के लिए अंतिम तर्क कैसे प्राप्त करें


11

लागू करने का एक बेहतर तरीका क्या है print_last_arg?

#!/bin/sh

print_last_arg () {
    eval "echo \${$#}"  # this hurts
}

print_last_arg foo bar baz
# baz

(यदि यह थे, कहते हैं, #!/usr/bin/zshके बजाय #!/bin/shमैं जानता हूँ कि चाहते हैं कि क्या करना है। मेरे समस्या के लिए यह लागू करने के लिए एक उचित तरीका खोजने है #!/bin/sh।)

EDIT: उपरोक्त केवल एक मूर्खतापूर्ण उदाहरण है। मेरा लक्ष्य अंतिम तर्क को प्रिंट करना नहीं है , बल्कि एक शेल फ़ंक्शन के भीतर अंतिम तर्क को संदर्भित करने का एक तरीका है।


EDIT2: मैं इस तरह के अस्पष्ट शब्द के लिए माफी माँगता हूँ। मुझे इस बार ठीक होने की उम्मीद है।

यदि /bin/zshइसके बजाय /bin/sh, मैं ऐसा कुछ लिख सकता था

#!/bin/zsh

print_last_arg () {
    local last_arg=$argv[$#]
    echo $last_arg
}

अभिव्यक्ति $argv[$#]एक उदाहरण है जो मैंने अपने पहले EDIT में शेल फ़ंक्शन के अंतिम तर्क को संदर्भित करने के तरीके के रूप में वर्णित किया है ।

इसलिए, मुझे वास्तव में इस तरह अपना मूल उदाहरण लिखना चाहिए:

print_last_arg () {
    local last_arg=$(eval "echo \${$#}")   # but this hurts even more
    echo $last_arg
}

... यह स्पष्ट करने के लिए कि जो मैं कर रहा हूं वह असाइनमेंट के दाईं ओर रखने के लिए एक कम भयानक बात है।

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


unix.stackexchange.com/q/145522/117549 #? / bin / sh की विभिन्न संभावनाओं को इंगित करता है - क्या आप इसे किसी भी तरह से कम कर सकते हैं?
जेफ स्कालर

मुझे संपादन पसंद है, लेकिन शायद आपने यह भी देखा होगा कि यहां एक उत्तर है जो अंतिम तर्क को संदर्भित करने का एक गैर-विनाशकारी साधन प्रदान करता है ...? आप समानता नहीं करना चाहिए var=$( eval echo \${$#})करने के लिए eval var=\${$#}- दो एक जैसे कुछ भी नहीं कर रहे हैं।
माइकस

1
यकीन नहीं होता कि मुझे आपका अंतिम नोट मिल गया है, लेकिन अब तक दिए गए लगभग सभी उत्तर इस मायने में गैर-विनाशकारी हैं कि वे चल रहे स्क्रिप्ट तर्कों को संरक्षित करते हैं। केवल shiftऔर set -- ...आधारित समाधान तब तक विनाशकारी हो सकते हैं जब तक कि उन कार्यों में उपयोग नहीं किया जाता है जहां वे हानिरहित हैं।
1

@jlliagre - लेकिन वे अभी भी मुख्य में विनाशकारी हैं - उन्हें डिस्पोजेबल संदर्भ बनाने की आवश्यकता है ताकि वे खोज करने के लिए नष्ट हो सकें। लेकिन ... अगर आपको किसी भी तरह से दूसरा संदर्भ मिलता है - तो सिर्फ एक ही क्यों नहीं आपको अनुक्रमणिका की अनुमति देता है? वहाँ काम के लिए उपकरण का उपयोग करने के साथ कुछ गलत है? एक्सपेंडेबल इनपुट के रूप में शेल एक्सपेंशन की व्याख्या करना इवेल का काम है। और करने के बारे में काफी अलग कुछ भी नहीं है eval "var=\${$#}"जब की तुलना में var=${arr[evaled index]}, सिवाय इसके कि $#एक गारंटीकृत सुरक्षित मूल्य है। पूरे सेट की नकल क्यों करें, इसे तब नष्ट करें जब आप इसे सीधे अनुक्रमित कर सकते हैं?
मिकसेर्व

1
शेल के मुख्य भाग में किए गए लूप के लिए @mikeserv ए सभी तर्कों को अपरिवर्तित छोड़ रहा है। मैं सहमत हूँ कि सभी तर्कों को पूरा करना बहुत ही गैर-अनुकूलित है, खासकर यदि उनमें से हजारों को शेल में पारित किया जाता है और मैं इस बात से भी सहमत हूं कि सीधे अंतिम सूचकांक को उचित सूचकांक के साथ एक्सेस करना सबसे अच्छा जवाब है (और मुझे समझ में नहीं आता कि इसे क्यों उतारा गया था? ) लेकिन इससे परे, वास्तव में कुछ भी विनाशकारी नहीं है और कोई अतिरिक्त संदर्भ नहीं बनाया गया है।
जूलियाग्रे

जवाबों:


1
eval printf %s${1+"'\n' \"the last arg is \${$#"\}\"}

... या तो स्ट्रिंग प्रिंट होगा the last arg isएक के बाद <अंतरिक्ष> , अंतिम तर्क का मूल्य, और पीछे <न्यू लाइन> अगर वहाँ कम से कम 1 तर्क, वरना, शून्य तर्क के लिए, यह सब पर कुछ भी नहीं प्रिंट होगा।

अगर तुमने किया:

eval ${1+"lastarg=\${$#"\}}

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

यहाँ एक और एक है कि इसी तरह काम करेगा है, हालांकि यह पूरी आर्ग सरणी दो बार कॉपी करने की आवश्यकता होती है, (और एक की आवश्यकता है printfमें $PATHबॉर्न शैल के लिए) :

if   [ "${1+:}" ]
then set "$#" "$@" "$@"
     shift    "$1"
     printf %s\\n "$1"
     shift
fi

टिप्पणियाँ विस्तारित चर्चा के लिए नहीं हैं; इस वार्तालाप को बातचीत में स्थानांतरित कर दिया गया है ।
terdon

2
FYI करें, आपका पहला सुझाव "बुरा प्रतिस्थापन" त्रुटि के साथ विरासत बॉर्न शेल के तहत विफल रहता है यदि कोई तर्क मौजूद नहीं है। शेष दोनों उम्मीद के मुताबिक काम करते हैं।
jlliagre

@ जीलग्रे - शुक्रिया। मैं उस शीर्ष एक के बारे में इतना निश्चित नहीं था - लेकिन अन्य दो के बारे में बहुत सुनिश्चित था। यह सिर्फ इस बात के प्रदर्शनकारी होने के लिए था कि संदर्भ इनलाइन द्वारा कैसे तर्कों को एक्सेस किया जा सकता है। अधिमानतः एक फ़ंक्शन कुछ के साथ खुल जाएगा जैसे ${1+":"} return- क्योंकि जो कुछ भी करना चाहता है या किसी भी तरह के साइड इफेक्ट्स को जोखिम में डालता है अगर उसे कुछ भी नहीं कहा जाता है? गणित उसी के लिए सुंदर है - यदि आप सुनिश्चित कर सकते हैं कि आप evalपूरे दिन एक सकारात्मक पूर्णांक का विस्तार कर सकते हैं। $OPTINDइसके लिए बहुत अच्छा है।
माइकस

3

यहाँ एक सरल तरीका है:

print_last_arg () {
  if [ "$#" -gt 0 ]
  then
    s=$(( $# - 1 ))
  else
    s=0
  fi
  shift "$s"
  echo "$1"
}

(@ cuonglm के आधार पर अपडेट किया गया कि मूल तब विफल हो गया जब कोई तर्क पारित नहीं हुआ; यह अब एक रिक्त पंक्ति को प्रतिध्वनित करता है - elseयदि वांछित है तो उस व्यवहार को खंड में बदल दें)


सोलारिस पर / usr / xpg4 / bin / sh का उपयोग करना आवश्यक है; मुझे बताएं कि क्या Solaris #! / bin / sh एक आवश्यकता है।
जेफ स्कालर

यह प्रश्न उस विशिष्ट स्क्रिप्ट के बारे में नहीं है जिसे मैं लिखने की कोशिश कर रहा हूं, बल्कि एक सामान्य कैसे-कैसे। मैं इसके लिए एक अच्छा नुस्खा ढूंढ रहा हूं। बेशक, अधिक पोर्टेबल, बेहतर।
कोजो

$ (()) गणित सोलारिस / बिन / श में विफल रहता है; कुछ का उपयोग करें: यदि आवश्यक हो तो शिफ्ट `एक्सप्र $ # - 1`।
जेफ स्कालर

वैकल्पिक रूप से सोलारिस / बिन / श के लिए,echo "$# - 1" | bc
जेफ स्कालर

1
कि वास्तव में क्या मैं लिखना चाहते थे, लेकिन यह 2 खोल मैंने कोशिश की पर विफल - NetBSD, जो जाहिरा तौर पर एक Almquist खोल है कि यह समर्थन नहीं करता है :(
जेफ स्कालर

3

उद्घाटन पोस्ट के उदाहरण को देखते हुए (रिक्त स्थान के बिना स्थितीय तर्क):

print_last_arg foo bar baz

डिफ़ॉल्ट के लिए IFS=' \t\n', कैसे के बारे में:

args="$*" && printf '%s\n' "${args##* }"

के सुरक्षित विस्तार के लिए "$*", IFS सेट करें (प्रति @ StéphaneChazelas):

( IFS=' ' args="$*" && printf '%s\n' "${args##* }" )

लेकिन उपरोक्त विफल हो जाएगा यदि आपकी स्थिति संबंधी दलीलों में रिक्त स्थान हो सकते हैं। उस स्थिति में, इसके बजाय इसका उपयोग करें:

for a in "$@"; do : ; done && printf '%s\n' "$a"

ध्यान दें कि इन तकनीकों के उपयोग से बचें evalऔर साइड-इफेक्ट्स न हों।

Shellcheck.net पर परीक्षण किया गया


1
यदि अंतिम तर्क में स्थान होता है तो पहला विफल हो जाता है।
congonglm

1
ध्यान दें कि पहले वाले ने भी POSIX खोल में काम नहीं किया था
cuonglm

@ cuonglm अच्छी तरह से देखा जाता है, अब आपका सही अवलोकन शामिल है।
12

यह यह भी मानता है कि $IFSअंतरिक्ष का पहला चरित्र है।
स्टीफन चेज़लस

1
यह होगा अगर वहाँ पर्यावरण में कोई $ IFS है, अन्यथा अनिर्दिष्ट। लेकिन क्योंकि आपको IFS को लगभग हर बार सेट करने की आवश्यकता होती है, इसलिए आप विभाजन + ग्लोब ऑपरेटर का उपयोग करते हैं (बिना किसी विस्तार को छोड़ें), IFS के साथ काम करने का एक तरीका यह है कि जब भी आपको आवश्यकता हो इसे सेट करें। यह IFS=' 'सिर्फ यह स्पष्ट करने के लिए कि इसका उपयोग करने के विस्तार के लिए इस्तेमाल किया जा रहा है यहाँ नुकसान नहीं होगा "$*"
स्टीफन चेज़लस

3

हालाँकि यह सवाल सिर्फ 2 साल से अधिक पुराना है, मैंने सोचा कि मैं कुछ हद तक बैक्टीरिया का विकल्प साझा करूँगा।

print_last_arg () {
    echo "${@:${#@}:${#@}}"
}

चलो इसे चलाते हैं

print_last_arg foo bar baz
baz

बैश शेल पैरामीटर विस्तार

संपादित करें

और भी संक्षिप्त: echo "${@: -1}"

(अंतरिक्ष का ध्यान रखें)

स्रोत

MacOS 10.12.6 पर परीक्षण किया गया, लेकिन अधिकांश उपलब्ध * निक्स फ्लेवर्स पर अंतिम तर्क भी लौटा देना चाहिए ...

बहुत कम झड़ता है ¯\_(ツ)_/¯


यह स्वीकृत उत्तर होना चाहिए। इससे भी बेहतर होगा: echo "${*: -1}"जिसके shellcheckबारे में कोई शिकायत नहीं करेगा।
टॉम हेल

4
यह एक सादे POSIX श के साथ काम नहीं करेगा, ${array:n:m:}एक विस्तार है। (प्रश्न ने स्पष्ट उल्लेख किया है /bin/sh)
ilkachachu

2

POSIXly:

while [ "$#" -gt 1 ]; do
  shift
done

printf '%s\n' "$1"

(यह दृष्टिकोण पुराने बॉर्न शेल में भी काम करता है)

अन्य मानक उपकरणों के साथ:

awk 'BEGIN{print ARGV[ARGC-1]}' "$@"

(यह पुराने के साथ काम नहीं करेगा awk, जिसके पास नहीं था ARGV)


जहां अभी भी एक बॉर्न शेल है, awkवह भी पुराना हो सकता है जो कि नहीं था ARGV
स्टीफन चेजलस

@ स्टीफनचेज़ेलस: सहमत। कम से कम इसने ब्रायन कर्निघन के स्वयं के संस्करण के साथ काम किया। क्या आपके पास कोई आदर्श है?
कोउन्ग्लम

यह तर्कों को मिटा देता है। उन्हें कैसे रखा जाए ?.

@BinaryZebra: awkदृष्टिकोण का उपयोग करें , या अन्य सादे forलूप का उपयोग करें जैसे अन्य ने किया, या उन्हें एक फ़ंक्शन में पास किया।
cuonglm

2

यह किसी भी POSIX अनुरूप शेल के साथ काम करना चाहिए और पूर्व POSIX विरासत सोलारिस बॉर्न शेल के साथ भी काम करेगा:

do=;for do do :;done;printf "%s\n" "$do"

और यहाँ एक ही दृष्टिकोण पर आधारित एक समारोह है:

print_last_arg()
  if [ "$*" ]; then
    for do do :;done
    printf "%s\n" "$do"
  else
    echo
  fi

पुनश्च: मुझे मत बताना मैं समारोह शरीर के चारों ओर घुंघराले ब्रेसिज़ भूल गया ;-)


2
आप फ़ंक्शन बॉडी के चारों ओर घुंघराले ब्रेसिज़ भूल गए ;-)।

@ BinaryZebra मैंने आपको चेतावनी दी; ;-) मैं उन्हें भूल नहीं पाया। ब्रेसिज़ आश्चर्यजनक रूप से यहां वैकल्पिक हैं।
1

1
@jlliagre I, वास्तव में, चेतावनी दी गई थी; ;-): P ...... और: निश्चित रूप से!

वाक्य रचना युक्ति का कौन सा भाग इसकी अनुमति देता है?
टॉम हेल

@TomHale शेल व्याकरण नियम दोनों in ...को एक forलूप में लापता होने और ifफ़ंक्शन बॉडी के रूप में उपयोग किए जाने वाले स्टेटमेंट के चारों ओर कोई घुंघराले ब्रेसिज़ की अनुमति नहीं देता है। देखें for_clauseऔर pubs.opengroup.org/onlinepubs/9699919799compound_command में देखें ।
जूलियाग्रे

2

से "यूनिक्स - अक्सर पूछे जाने वाले प्रश्न"

(1)

unset last
if    [ $# -gt 0 ]
then  eval last=\${$#}
fi
echo  "$last"

यदि तर्कों की संख्या शून्य हो सकती है, तो तर्क शून्य $0(आमतौर पर स्क्रिप्ट का नाम) को सौंपा जाएगा $last। यही कारण है कि अगर।

(2)

unset last
for   last
do    :
done
echo  "$last"

(3)

for     i
do
        third_last=$second_last
        second_last=$last
        last=$i
done
echo    "$last"

जब कोई तर्क न हो, तो एक खाली लाइन को प्रिंट करने से बचने के echo "$last"लिए:

${last+false} || echo "${last}"

शून्य तर्क गणना से बचा जाता है if [ $# -gt 0 ]

यह पृष्ठ में लिंक की गई चीज़ों की एक सटीक प्रतिलिपि नहीं है, कुछ सुधार जोड़े गए थे।


0

यह सभी प्रणालियों पर काम करना चाहिए perl(इसलिए अधिकांश यूनिसेन्स):

print_last_arg () {
    printf '%s\0' "$@" | perl -0ne 's/\0//;$l=$_;}{print "$l\n"'
}

चाल का उपयोग करने के लिए है printfएक जोड़ने के लिए \0प्रत्येक खोल तर्क और फिर बाद perlके -0स्विच जो शून्य करने के लिए अपने रिकॉर्ड विभाजक सेट। फिर हम इनपुट पर पुनरावृति करते हैं, \0प्रत्येक NULL-terminated पंक्ति को हटाने और सहेजने के रूप में $lENDब्लॉक (कि क्या के }{पिछले खोल तर्क: है) के बाद सभी इनपुट इतना पढ़ा जा चुका है पिछले "पंक्ति" प्रिंट होगा निष्पादित किया जाएगा।


@ mikeserv आह, सच है, मैं किसी भी अंतिम तर्क में एक newline के साथ परीक्षण नहीं किया था। संपादित संस्करण बहुत कुछ के लिए काम करना चाहिए, लेकिन शायद इस तरह के एक सरल कार्य के लिए बहुत जटिल है।
terdon

हाँ, एक बाहर निष्पादन योग्य (अगर यह पहले से ही तर्क का हिस्सा नहीं है) , मेरे लिए, थोड़ा भारी हाथ है। लेकिन अगर बात करने के लिए पर है कि तर्क पारित करने के लिए है sed, awkया perlतो यह बहुमूल्य जानकारी वैसे भी हो सकता है। फिर भी आप sed -zअप-टू-डेट GNU संस्करणों के साथ भी ऐसा ही कर सकते हैं ।
मोकेसर

आप नीचे क्यों गए? यह अच्छा था ... मुझे लगा?
मिकसेर्व

0

यहाँ एक संस्करण का उपयोग किया जा रहा है। कोई विचार नहीं है, हालांकि पॉसिक्स का अनुपालन कैसे होता है ...

print_last_arg()
{
    if [ $# -gt 1 ] ; then
        shift
        echo $( print_last_arg "$@" )
    else
        echo "$1"
    fi
}

0

एक सरल अवधारणा, कोई अंकगणित, कोई लूप, कोई eval , बस कार्य करता है।
याद रखें कि बॉर्न शेल में कोई अंकगणित (बाहरी रूप से आवश्यक expr) नहीं था । यदि एक अंकगणित मुक्त, evalमुफ्त विकल्प प्राप्त करना चाहते हैं , तो यह एक विकल्प है। कार्यों की आवश्यकता का अर्थ है SVR3 या उससे ऊपर (मापदंडों का कोई अधिलेखित नहीं)।
प्रिंटफ के साथ अधिक मजबूत संस्करण के लिए नीचे देखें।

printlast(){
    shift "$1"
    echo "$1"
}

printlast "$#" "$@"          ### you may use ${1+"$@"} here to allow
                             ### a Bourne empty list of arguments,
                             ### but wait for a correct solution below.

कॉल करने के लिए यह संरचना printlastहै तय , तर्क खोल तर्कों की सूची में तय करने की आवश्यकता $1, $2आदि (तर्क ढेर) और कॉल किया के रूप में दिया।

यदि तर्कों की सूची को बदलना है, तो उन्हें निर्धारित करें:

set -- o1e t2o t3r f4u
printlast "$#" "$@"

या फ़ंक्शन ( getlast) का उपयोग करने के लिए एक आसान बनाएं जो सामान्य तर्क दे सकता है (लेकिन उतना तेज़ नहीं है, तर्क दो बार पारित किए जाते हैं)।

getlast(){ printlast "$#" "$@"; }
getlast o1e t2o t3r f4u

कृपया ध्यान दें कि तर्क (में getlast, या सभी $@को प्रिंटआउट के लिए शामिल किया गया है) में रिक्त स्थान, नई सूची आदि हो सकते हैं, लेकिन एनयूएल नहीं।

बेहतर

0तर्कों की सूची खाली होने पर यह संस्करण प्रिंट नहीं होता है, और अधिक मजबूत प्रिंटफ का उपयोग करें ( पुराने गोले के लिए echoबाहरी printfउपलब्ध नहीं होने पर वापस आ जाएं )।

printlast(){ shift  "$1"; printf '%s' "$1"; }
getlast  ()  if     [ $# -gt 0 ]
             then   printlast "$#" "$@"
                    echo    ### optional if a trailing newline is wanted.
             fi
### The {} braces were removed on purpose.

getlast 1 2 3 4    # will print 4

EVAL का उपयोग करना।

यदि बॉर्न शेल अधिक पुराना है और कोई कार्य नहीं हैं, या यदि किसी कारण से eval का उपयोग करना एकमात्र विकल्प है:

मान मुद्रित करने के लिए:

if    [ $# -gt 0 ]
then  eval printf "'1%s\n'" \"\$\{$#\}\"
fi

किसी चर में मान सेट करने के लिए:

if    [ $# -gt 0 ]
then  eval 'last_arg='\"\$\{$#\}\"
fi

यदि किसी फ़ंक्शन में करने की आवश्यकता है, तो तर्क को फ़ंक्शन में कॉपी करने की आवश्यकता है:

print_last_arg () {
    local last_arg                ### local only works on more modern shells.
    eval 'last_arg='\"\$\{$#\}\"
    echo "last_arg3=$last_arg"
}

print_last_arg "$@"               ### Shell arguments are sent to the function.

यह बेहतर है, लेकिन यह कहता है कि यह लूप का उपयोग नहीं करता है - लेकिन यह करता है। एक सरणी प्रतिलिपि एक लूप है । और दो कार्यों के साथ यह दो छोरों का उपयोग करता है। भी - वहाँ कुछ कारण पूरे सरणी iterating के साथ यह अनुक्रमण करने के लिए पसंद किया जाना चाहिए है eval?
मोकेसर

1
आपको कोई दिख रहा है for loopया while loop?


1
आपके मानक से, मुझे लगता है कि सभी कोड में लूप हैं, यहां तक ​​कि लूप भी है echo "Hello"क्योंकि सीपीयू को लूप में मेमोरी लोकेशन पढ़ना होता है। फिर से: मेरे कोड में कोई जोड़ा लूप नहीं है।

1
मुझे वास्तव में अच्छे या बुरे के बारे में आपकी राय की परवाह नहीं है, यह पहले से ही कई बार गलत साबित हो चुका है। फिर: मेरे कोड नहीं है for, whileया untilछोरों। क्या आपको कोड में लिखा कोई लूप for whileया untilलूप दिखाई देता है ?. नहीं ?, तो बस तथ्य को स्वीकार करो, उसे गले लगाओ और सीखो।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.