Make a Bash alias that takes a parameter?

后端 未结 20 1931
长发绾君心
长发绾君心 2020-11-21 06:53

I used to use CShell (csh), which lets you make an alias that takes a parameter. The notation was something like

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

相关标签:
20条回答
  • 2020-11-21 07:18

    Bash alias does not directly accept parameters. You will have to create a function.

    alias does not accept parameters but a function can be called just like an alias. For example:

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

    By the way, Bash functions defined in your .bashrc and other files are available as commands within your shell. So for instance you can call the earlier function like this

    $ myfunction original.conf my.conf
    
    0 讨论(0)
  • 2020-11-21 07:18

    NB: In case the idea isn't obvious, it is a bad idea to use aliases for anything but aliases, the first one being the 'function in an alias' and the second one being the 'hard to read redirect/source'. Also, there are flaws (which i thought would be obvious, but just in case you are confused: I do not mean them to actually be used... anywhere!)

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

    I've answered this before, and it has always been like this in the past:

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

    which is fine and good, unless you are avoiding the use of functions all together. in which case you can take advantage of bash's vast ability to redirect text:

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

    They are both about the same length give or take a few characters.

    The real kicker is the time difference, the top being the 'function method' and the bottom being the 'redirect-source' method. To prove this theory, the timing speaks for itself:

    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
    ubuntu@localhost /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
    ubuntu@localhost /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
    ubuntu@localhost /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
    ubuntu@localhost /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
    

    This is the bottom part of about 200 results, done at random intervals. It seems that function creation/destruction takes more time than redirection. Hopefully this will help future visitors to this question (didn't want to keep it to myself).

    0 讨论(0)
  • 2020-11-21 07:20

    Here's are three examples of functions I have in my ~/.bashrc, that are essentially aliases that accept a parameter:

    #Utility required by all below functions.
    #https://stackoverflow.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://stackoverflow.com/a/3232082/2736496
        - Alias w/param: https://stackoverflow.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://stackoverflow.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
    }
    

    References:

    • Trimming whitespace from strings: How to trim whitespace from a Bash variable?
    • Actual line breaks: https://stackoverflow.com/a/4296147/2736496
    • Alias w/param: https://stackoverflow.com/a/7131683/2736496 (another answer in this question)
    • HISTIGNORE: https://superuser.com/questions/232885/can-you-share-wisdom-on-using-histignore-in-bash
    • Y/N prompt: https://stackoverflow.com/a/3232082/2736496
    • Delete all matching items from history: https://unix.stackexchange.com/questions/57924/how-to-delete-commands-in-history-matching-a-given-string
    • Is string null/empty: http://tldp.org/LDP/abs/html/comparison-ops.html
    0 讨论(0)
  • 2020-11-21 07:20

    Respectfully to all those saying you can't insert a parameter in the middle of an alias I just tested it and found that it did work.

    alias mycommand = "python3 "$1" script.py --folderoutput RESULTS/"

    when I then ran mycommand foobar it worked exactly as if I had typed the command out longhand.

    0 讨论(0)
  • 2020-11-21 07:20

    There are legitimate technical reasons to want a generalized solution to the problem of bash alias not having a mechanism to take a reposition arbitrary arguments. One reason is if the command you wish to execute would be adversely affected by the changes to the environment that result from executing a function. In all other cases, functions should be used.

    What recently compelled me to attempt a solution to this is that I wanted to create some abbreviated commands for printing the definitions of variables and functions. So I wrote some functions for that purpose. However, there are certain variables which are (or may be) changed by a function call itself. Among them are:

    FUNCNAME BASH_SOURCE BASH_LINENO BASH_ARGC BASH_ARGV

    The basic command I had been using (in a function) to print variable defns. in the form output by the set command was:

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

    E.g.:

    > V=voodoo
    sv V
    V=voodoo
    

    Problem: This won't print the definitions of the variables mentioned above as they are in the current context, e.g., if in an interactive shell prompt (or not in any function calls), FUNCNAME isn't defined. But my function tells me the wrong information:

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

    One solution I came up with has been mentioned by others in other posts on this topic. For this specific command to print variable defns., and which requires only one argument, I did this:

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

    Which gives the correct output (none), and result status (false):

    > asv FUNCNAME
    > echo $?
    1
    

    However, I still felt compelled to find a solution that works for arbitrary numbers of arguments.

    A General Solution To Passing Arbitrary Arguments To A Bash Aliased Command:

    # (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($@)"; 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=("$@");  # 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".
    

    E.g.:

    > . 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
    

    A nice thing about this solution is that all the special tricks used to handle positional parameters (arguments) to commands will work when composing the trapped command. The only difference is that array syntax must be used.

    E.g.,

    If you want "$@", use "${CMD_ARGV[@]}".

    If you want "$#", use "${#CMD_ARGV[@]}".

    Etc.

    0 讨论(0)
  • 2020-11-21 07:20

    To give specific answer to the Question posed about creating the alias to move the files to Trash folder instead of deleting them:

    alias rm="mv "$1" -t ~/.Trash/"
    

    Offcourse you have to create dir ~/.Trash first.

    Then just give following command:

    $rm <filename>
    $rm <dirname>
    
    0 讨论(0)
提交回复
热议问题