bash - एक बैश उपनाम बनाओ जो पैरामीटर लेता है?




alias (8)

टीएल; डीआर: इसके बजाय ऐसा करें

कमांड के मध्य में तर्क डालने के लिए उपनाम की तुलना में फ़ंक्शन का उपयोग करने के लिए यह कहीं अधिक आसान और अधिक पठनीय है।

$ wrap_args() { echo "before [email protected] after"; }
$ wrap_args 1 2 3
before 1 2 3 after

यदि आप पढ़ते हैं, तो आप उन चीजों को सीखेंगे जिन्हें आपको शैल तर्क प्रसंस्करण के बारे में जानने की आवश्यकता नहीं है। ज्ञान खतरनाक है। अंधेरे पक्ष हमेशा के लिए अपने भाग्य को नियंत्रित करने से पहले, जो परिणाम आप चाहते हैं उसे प्राप्त करें।

स्पष्टीकरण

bash उपनाम तर्क स्वीकार करते हैं, लेकिन केवल अंत में :

$ alias speak=echo
$ speak hello world
hello world

alias माध्यम से कमांड के मध्य में तर्क डालना वास्तव में संभव है लेकिन यह बदसूरत हो जाता है।

घर पर कोशिश मत करो, kiddies!

यदि आप सीमाओं को बाधित करना चाहते हैं और जो कुछ भी कहते हैं वह असंभव है, तो यहां नुस्खा है। अगर आपके बाल झटकेदार हो जाते हैं तो बस मुझे दोष न दें और आपका चेहरा सूट पागल-वैज्ञानिक शैली में ढंका हुआ है।

वर्कअराउंड उन तर्कों को पारित करना है जो alias केवल एक रैपर के अंत में स्वीकार करते हैं जो उन्हें बीच में डालेगा और फिर आपके आदेश को निष्पादित करेगा।

समाधान 1

यदि आप वास्तव में एक फ़ंक्शन का उपयोग करने के खिलाफ हैं, तो आप इसका उपयोग कर सकते हैं:

$ alias wrap_args='f(){ echo before "[email protected]" after;  unset -f f; }; f'
$ wrap_args x y z
before x y z after

यदि आप केवल पहले तर्क चाहते हैं तो आप $1 साथ [email protected] को प्रतिस्थापित कर सकते हैं।

स्पष्टीकरण 1

यह एक अस्थायी फ़ंक्शन f बनाता है, जो तर्क पारित किया जाता है (ध्यान दें कि f को बहुत अंत में बुलाया जाता है)। unset -f फ़ंक्शन परिभाषा को हटा देता है क्योंकि उपनाम निष्पादित होता है, इसलिए यह बाद में लटका नहीं जाता है।

समाधान 2

आप सबहेल का भी उपयोग कर सकते हैं:

$ alias wrap_args='sh -c '\''echo before "[email protected]" after'\'' _'

स्पष्टीकरण 2

उपनाम एक कमांड बनाता है जैसे:

sh -c 'echo before "[email protected]" after' _

टिप्पणियाँ:

  • प्लेसहोल्डर _ की आवश्यकता है, लेकिन यह कुछ भी हो सकता है। यह sh $0 s पर सेट हो जाता है, और इसकी आवश्यकता होती है ताकि उपयोगकर्ता द्वारा दिए गए तर्कों में से पहला उपभोग न हो। प्रदर्शन:

    sh -c 'echo Consumed: "$0" Printing: "[email protected]"' alcohol drunken babble
    Consumed: alcohol Printing: drunken babble
    
  • सिंगल-कोट्स के अंदर सिंगल-कोट्स आवश्यक हैं। यहां डबल उदाहरण के साथ काम नहीं करने का एक उदाहरण दिया गया है:

    $ sh -c "echo Consumed: $0 Printing: [email protected]" alcohol drunken babble
    Consumed: -bash Printing:
    

    इंटरएक्टिव शैल के $0 और [email protected] के मूल्यों को sh को पारित होने से पहले दोहरा उद्धृत किया गया sh । यहां सबूत है:

    echo "Consumed: $0 Printing: [email protected]"
    Consumed: -bash Printing:
    

    एकल उद्धरण सुनिश्चित करते हैं कि इन चरों को इंटरैक्टिव शैल द्वारा व्याख्या नहीं किया गया है, और शाब्दिक रूप से sh -c को पारित किया जाता है।

    आप डबल-कोट्स और \[email protected] उपयोग कर सकते हैं, लेकिन सबसे अच्छा अभ्यास है कि आप अपने तर्कों को उद्धृत करें (क्योंकि उनमें रिक्त स्थान हो सकते हैं), और \"\[email protected]\" भी उलझन में दिखता है, लेकिन आप एक अपवित्रता प्रतियोगिता जीतने में मदद कर सकते हैं जहां चमड़े के बाल प्रवेश के लिए एक शर्त है।

मैं CShell ( csh ) का उपयोग करता था, जो आपको एक उपनाम बनाने देता है जो पैरामीटर लेता है। नोटेशन कुछ ऐसा था

alias junk="mv \\!* ~/.Trash"

बैश में, यह काम नहीं लग रहा है। यह देखते हुए कि बैश में कई उपयोगी विशेषताएं हैं, मुझे लगता है कि यह लागू किया गया है लेकिन मैं सोच रहा हूं कि कैसे।


ऊपर दिए गए उत्तर को परिशोधित करते हुए, आप 1-लाइन सिंटैक्स प्राप्त कर सकते हैं जैसे आप उपनाम के लिए कर सकते हैं, जो शेल या .bashrc फ़ाइलों में विज्ञापन-प्रसार परिभाषाओं के लिए अधिक सुविधाजनक है:

bash$ myfunction() { mv "$1" "$1.bak"; cp "$2" "$1"; }

bash$ myfunction original.conf my.conf

समापन दाएं ब्रैकेट से पहले सेमी-कोलन को न भूलें। इसी प्रकार, वास्तविक प्रश्न के लिए:

csh% alias junk="mv \\!* ~/.Trash"

bash$ junk() { mv "[email protected]" ~/.Trash/; }

या:

bash$ junk() { for item in "[email protected]" ; do echo "Trashing: $item" ; mv "$item" ~/.Trash/; done; }

एनबी: यदि विचार स्पष्ट नहीं है, तो उपनामों के अलावा उपनामों का उपयोग करना एक बुरा विचार है, पहला 'उपनाम में कार्य' और दूसरा 'रीडायरेक्ट / स्रोत पढ़ने के लिए कठिन' है। इसके अलावा, त्रुटियां हैं (जो मैंने सोचा था कि स्पष्ट होगा, लेकिन अगर आप उलझन में हैं: मेरा मतलब यह नहीं है कि वास्तव में उनका उपयोग किया जाए ... कहीं भी!)

.................................................. .................................................. ............................................

मैंने पहले इसका उत्तर दिया है, और यह हमेशा अतीत में ऐसा ही रहा है:

alias foo='__foo() { unset -f $0; echo "arg1 for foo=$1"; }; __foo()'

जो ठीक और अच्छा है, जब तक आप सभी कार्यों के उपयोग से परहेज नहीं कर रहे हैं। इस मामले में आप पाठ को पुनर्निर्देशित करने के लिए बैश की विशाल क्षमता का लाभ उठा सकते हैं:

alias bar='cat <<< '\''echo arg1 for bar=$1'\'' | source /dev/stdin'

वे दोनों एक ही लंबाई के बारे में हैं या कुछ पात्र लेते हैं।

वास्तविक किकर समय अंतर है, शीर्ष 'फ़ंक्शन विधि' है और नीचे 'रीडायरेक्ट-स्रोत' विधि है। इस सिद्धांत को साबित करने के लिए, समय स्वयं ही बोलता है:

arg1 for foo=FOOVALUE
 real 0m0.011s user 0m0.004s sys 0m0.008s  # <--time spent in foo
 real 0m0.000s user 0m0.000s sys 0m0.000s  # <--time spent in bar
arg1 for bar=BARVALUE
[email protected] /usr/bin# time foo FOOVALUE; time bar BARVALUE
arg1 for foo=FOOVALUE
 real 0m0.010s user 0m0.004s sys 0m0.004s
 real 0m0.000s user 0m0.000s sys 0m0.000s
arg1 for bar=BARVALUE
[email protected] /usr/bin# time foo FOOVALUE; time bar BARVALUE
arg1 for foo=FOOVALUE
 real 0m0.011s user 0m0.000s sys 0m0.012s
 real 0m0.000s user 0m0.000s sys 0m0.000s
arg1 for bar=BARVALUE
[email protected] /usr/bin# time foo FOOVALUE; time bar BARVALUE
arg1 for foo=FOOVALUE
 real 0m0.012s user 0m0.004s sys 0m0.004s
 real 0m0.000s user 0m0.000s sys 0m0.000s
arg1 for bar=BARVALUE
[email protected] /usr/bin# time foo FOOVALUE; time bar BARVALUE
arg1 for foo=FOOVALUE
 real 0m0.010s user 0m0.008s sys 0m0.004s
 real 0m0.000s user 0m0.000s sys 0m0.000s
arg1 for bar=BARVALUE

यादृच्छिक अंतराल पर किए गए लगभग 200 परिणामों का यह नीचे हिस्सा है। ऐसा लगता है कि कार्य निर्माण / विनाश पुनर्निर्देशन से अधिक समय लेता है। उम्मीद है कि यह इस प्रश्न के लिए भविष्य के आगंतुकों की मदद करेगा (इसे खुद को नहीं रखना चाहता था)।


कार्य वास्तव में हमेशा उत्तर हैं क्योंकि पहले से ही योगदान दिया गया है और मैन पेज से इस उद्धरण द्वारा पुष्टि की गई है: "लगभग हर उद्देश्य के लिए, उपनामों को शैल फ़ंक्शंस द्वारा हटा दिया जाता है।"

पूर्णता के लिए और क्योंकि यह उपयोगी हो सकता है (मामूली रूप से अधिक हल्के वाक्यविन्यास) यह ध्यान दिया जा सकता है कि जब पैरामीटर उपनाम का पालन करता है, तब भी उनका उपयोग किया जा सकता है (हालांकि यह ओपी की आवश्यकता को संबोधित नहीं करेगा)। उदाहरण के साथ प्रदर्शित करना शायद सबसे आसान है:

alias ssh_disc='ssh -O stop'

मुझे ssh_disc myhost तरह ssh_disc myhost टाइप करने की अनुमति देता है, जो उम्मीद के रूप में विस्तारित हो जाता है: ssh -O stop myhost

यह उन आदेशों के लिए उपयोगी हो सकता है जो जटिल तर्क लेते हैं (मेरी याददाश्त वह नहीं है जो अब टी का उपयोग करती है ...)


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

हाल ही में मुझे इस समाधान का प्रयास करने के लिए मजबूर किया गया है कि मैं चर और कार्यों की परिभाषाओं को मुद्रित करने के लिए कुछ संक्षिप्त आदेश बनाना चाहता हूं। तो मैंने उस उद्देश्य के लिए कुछ कार्यों को लिखा। हालांकि, कुछ चर हैं जो फ़ंक्शन कॉल द्वारा स्वयं (या हो सकते हैं) बदल जाते हैं। उनमें से हैं:

FUNCNAME BASH_SOURCE BASH_LINENO BASH_ARGC BASH_ARGV

परिवर्तनीय defns मुद्रित करने के लिए मैं मूल आदेश (एक समारोह में) का उपयोग कर रहा था। सेट कमांड द्वारा फॉर्म आउटपुट में था:

sv () { set | grep --color=never -- "^$1=.*"; }

उदाहरण के लिए:

> V=voodoo
sv V
V=voodoo

समस्या: यह ऊपर वर्णित चर के परिभाषाओं को मुद्रित नहीं करेगा क्योंकि वे वर्तमान संदर्भ में हैं , उदाहरण के लिए, यदि एक इंटरैक्टिव शेल प्रॉम्प्ट (या किसी फ़ंक्शन कॉल में नहीं) में, FUNCNAME परिभाषित नहीं किया गया है। लेकिन मेरा काम मुझे गलत जानकारी बताता है:

> sv FUNCNAME
FUNCNAME=([0]="sv")

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

alias asv='(grep -- "^$(cat -)=.*" <(set)) <<<'

जो सही आउटपुट (कोई नहीं) देता है, और परिणाम स्थिति (झूठी):

> asv FUNCNAME
> echo $?
1

हालांकि, मुझे अभी भी एक समाधान खोजने के लिए मजबूर होना पड़ा जो तर्कों की मनमानी संख्याओं के लिए काम करता है।

एक बैश के लिए मनमाने ढंग से तर्क पारित करने के लिए एक सामान्य समाधान अलीश कमांड:

# (I put this code in a file "alias-arg.sh"):

# cmd [arg1 ...] – an experimental command that optionally takes args,
# which are printed as "cmd(arg1 ...)"
#
# Also sets global variable "CMD_DONE" to "true".
#
cmd () { echo "cmd([email protected])"; declare -g CMD_DONE=true; }

# Now set up an alias "ac2" that passes to cmd two arguments placed
# after the alias, but passes them to cmd with their order reversed:
#
# ac2 cmd_arg2 cmd_arg1 – calls "cmd" as: "cmd cmd_arg1 cmd_arg2"
#
alias ac2='
    # Set up cmd to be execed after f() finishes:
    #
    trap '\''cmd "${CMD_ARGV[1]}" "${CMD_ARGV[0]}"'\'' SIGUSR1;
    #        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    #       (^This is the actually execed command^)
    #
    # f [arg0 arg1 ...] – acquires args and sets up trap to run cmd:
    f () {
        declare -ag CMD_ARGV=("[email protected]");  # array to give args to cmd
        kill -SIGUSR1 $$;             # this causes cmd to be run
        trap SIGUSR1;                 # unset the trap for SIGUSR1
        unset CMD_ARGV;               # clean up env...
        unset f;                      # incl. this function!
    };
    f'  # Finally, exec f, which will receive the args following "ac2".

उदाहरण के लिए:

> . alias-arg.sh
> ac2 one two
cmd(two one)
>
> # Check to see that command run via trap affects this environment:
> asv CMD_DONE
CMD_DONE=true

इस समाधान के बारे में एक अच्छी बात ये है कि आदेशों के लिए स्थितित्मक पैरामीटर (तर्क) को संभालने के लिए उपयोग की जाने वाली सभी विशेष चालें फंसे हुए कमांड को लिखते समय काम करेंगे। केवल अंतर यह है कि सरणी वाक्यविन्यास का उपयोग किया जाना चाहिए।

उदाहरण के लिए,

अगर आप "$ @" चाहते हैं, तो "$ {CMD_ARGV [@]}" का उपयोग करें।

अगर आप "$ #" चाहते हैं, तो "$ {# सीएमडी_एआरजीवी [@]}" का उपयोग करें।

आदि।


बैश उपनाम सीधे पैरामीटर स्वीकार नहीं करता है। आपको एक फ़ंक्शन और उपनाम बनाना होगा।

alias पैरामीटर स्वीकार नहीं करता है लेकिन एक फ़ंक्शन को उपनाम की तरह ही कहा जा सकता है। उदाहरण के लिए:

myfunction() {
    #do things with parameters like $1 such as
    mv "$1" "$1.bak"
    cp "$2" "$1"
}


myFunction xyz #calls `myfunction`

वैसे, आपके .bashrc में परिभाषित बैश फ़ंक्शंस और अन्य फ़ाइलें आपके खोल के भीतर कमांड के रूप में उपलब्ध हैं। तो उदाहरण के लिए आप इस तरह के पहले समारोह को कॉल कर सकते हैं

$ myfunction original.conf my.conf

यहां मेरे कार्यों के तीन उदाहरण हैं जो मेरे ~/.bashrc , जो अनिवार्य रूप से उपनाम हैं जो पैरामीटर स्वीकार करते हैं:

#Utility required by all below functions.
#https://.com/questions/369758/how-to-trim-whitespace-from-bash-variable#comment21953456_3232433
alias trim="sed -e 's/^[[:space:]]*//g' -e 's/[[:space:]]*\$//g'"

:<<COMMENT
    Alias function for recursive deletion, with are-you-sure prompt.

    Example:
        srf /home/myusername/django_files/rest_tutorial/rest_venv/

    Parameter is required, and must be at least one non-whitespace character.

    Short description: Stored in SRF_DESC

    With the following setting, this is *not* added to the history:
        export HISTIGNORE="*rm -r*:srf *"
    - https://superuser.com/questions/232885/can-you-share-wisdom-on-using-histignore-in-bash

    See:
    - y/n prompt: https://.com/a/3232082/2736496
    - Alias w/param: https://.com/a/7131683/2736496
COMMENT
#SRF_DESC: For "aliaf" command (with an 'f'). Must end with a newline.
SRF_DESC="srf [path]: Recursive deletion, with y/n prompt\n"
srf()  {
    #Exit if no parameter is provided (if it's the empty string)
        param=$(echo "$1" | trim)
        echo "$param"
        if [ -z "$param" ]  #http://tldp.org/LDP/abs/html/comparison-ops.html
        then
          echo "Required parameter missing. Cancelled"; return
        fi

    #Actual line-breaks required in order to expand the variable.
    #- https://.com/a/4296147/2736496
    read -r -p "About to
    sudo rm -rf \"$param\"
Are you sure? [y/N] " response
    response=${response,,}    # tolower
    if [[ $response =~ ^(yes|y)$ ]]
    then
        sudo rm -rf "$param"
    else
        echo "Cancelled."
    fi
}

:<<COMMENT
    Delete item from history based on its line number. No prompt.

    Short description: Stored in HX_DESC

    Examples
        hx 112
        hx 3

    See:
    - https://unix.stackexchange.com/questions/57924/how-to-delete-commands-in-history-matching-a-given-string
COMMENT
#HX_DESC: For "aliaf" command (with an 'f'). Must end with a newline.
HX_DESC="hx [linenum]: Delete history item at line number\n"
hx()  {
    history -d "$1"
}

:<<COMMENT
    Deletes all lines from the history that match a search string, with a
    prompt. The history file is then reloaded into memory.

    Short description: Stored in HXF_DESC

    Examples
        hxf "rm -rf"
        hxf ^source

    Parameter is required, and must be at least one non-whitespace character.

    With the following setting, this is *not* added to the history:
        export HISTIGNORE="*hxf *"
    - https://superuser.com/questions/232885/can-you-share-wisdom-on-using-histignore-in-bash

    See:
    - https://unix.stackexchange.com/questions/57924/how-to-delete-commands-in-history-matching-a-given-string
COMMENT
#HXF_DESC: For "aliaf" command (with an 'f'). Must end with a newline.
HXF_DESC="hxf [searchterm]: Delete all history items matching search term, with y/n prompt\n"
hxf()  {
    #Exit if no parameter is provided (if it's the empty string)
        param=$(echo "$1" | trim)
        echo "$param"
        if [ -z "$param" ]  #http://tldp.org/LDP/abs/html/comparison-ops.html
        then
          echo "Required parameter missing. Cancelled"; return
        fi

    read -r -p "About to delete all items from history that match \"$param\". Are you sure? [y/N] " response
    response=${response,,}    # tolower
    if [[ $response =~ ^(yes|y)$ ]]
    then
        #Delete all matched items from the file, and duplicate it to a temp
        #location.
        grep -v "$param" "$HISTFILE" > /tmp/history

        #Clear all items in the current sessions history (in memory). This
        #empties out $HISTFILE.
        history -c

        #Overwrite the actual history file with the temp one.
        mv /tmp/history "$HISTFILE"

        #Now reload it.
        history -r "$HISTFILE"     #Alternative: exec bash
    else
        echo "Cancelled."
    fi
}

संदर्भ:


सवाल सिर्फ गलत पूछा जाता है। आप उपनाम नहीं लेते हैं जो पैरामीटर लेते हैं क्योंकि alias कुछ ऐसा है जो पहले से मौजूद है। ओपी चाहता है function एक नया समारोह बनाने के लिए function कमांड है। फ़ंक्शन को उपनाम करने की आवश्यकता नहीं है क्योंकि फ़ंक्शन के पास पहले से ही एक नाम है।

मुझे लगता है कि आप ऐसा कुछ चाहते हैं:

function trash() { mv "[email protected]" ~/.Trash; }

बस! आप पैरामीटर $ 1, $ 2, $ 3, आदि का उपयोग कर सकते हैं, या बस उन्हें $ @





alias