创建一个带参数的Bash别名?

时间:2011-08-20 12:11:41

标签: bash alias

我以前使用CShell(),它允许你创建一个带参数的别名。符号类似于

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

在Bash中,这似乎不起作用。鉴于Bash有许多有用的功能,我会假设这个功能已经实现,但我想知道如何。

22 个答案:

答案 0 :(得分:1777)

Bash别名不直接接受参数。你必须创建一个函数。

alias不接受参数,但可以像控制别名一样调用函数。例如:

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


myfunction old.conf new.conf #calls `myfunction`

顺便说一句,在.bashrc和其他文件中定义的Bash函数可作为shell中的命令使用。因此,例如,您可以像这样调用早期的函数

$ myfunction original.conf my.conf

答案 1 :(得分:173)

完善上面的答案,您可以像使用别名一样获得单行语法,这对于shell或.bashrc文件中的临时定义更方便:

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

bash$ myfunction original.conf my.conf

不要忘记关闭右括号前的分号。同样,对于实际问题:

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

bash$ junk() { mv "$@" ~/.Trash/; }

或者:

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

答案 2 :(得分:95)

这个问题简直被问到了错误。您不会创建带参数的别名,因为alias只为已存在的内容添加第二个名称。 OP想要的功能是function命令来创建新功能。您不需要为函数设置别名,因为函数已有名称。

我想你想要这样的东西:

function trash() { mv "$@" ~/.Trash; }

那就是它!你可以使用参数$ 1,$ 2,$ 3等,或者只用$ @

填充它们

答案 3 :(得分:84)

TL; DR:改为

使用函数比使用别名将参数放在命令中间更容易,更易读。

$ wrap_args() { echo "before $@ after"; }
$ wrap_args 1 2 3
before 1 2 3 after

如果您继续阅读,您将学习关于shell参数处理不需要了解的内容。知识很危险。在黑暗的一面永远控制你的命运之前,只需得到你想要的结果。

澄清

bash别名执行接受参数,但仅限于结束

$ alias speak=echo
$ speak hello world
hello world

通过alias将参数放入命令的中间确实是可能的,但它很难看。

不要在家里试试,小孩!

如果您喜欢规避限制并做其他人认为不可能的事情,那么这就是食谱。如果你的头发被弄得疲惫不堪,你的脸最终会被烟灰色的科学家风格所覆盖,请不要怪我。

解决方法是将alias仅在末尾接受的参数传递给将在中间插入它们然后执行命令的包装器。

解决方案1 ​​

如果你真的反对使用函数本身,你可以使用:

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

如果您只想要第一个参数,则可以将$@替换为$1

解释1

这会创建一个临时函数f,它会传递参数(注意最后调用f)。 unset -f在执行别名时删除了函数定义,因此它不会在之后挂起。

解决方案2

您还可以使用子shell:

$ alias wrap_args='sh -c '\''echo before "$@" after'\'' _'

解释2

别名构建如下命令:

sh -c 'echo before "$@" after' _

评论:

  • 占位符_是必需的,但它可以是任何内容。它被设置为sh的{​​{1}},并且是必需的,以便第一个用户给定的参数不被消耗。示范:

    $0
  • 单引号内的单引号是必需的。这是一个不使用双引号的例子:

    sh -c 'echo Consumed: "$0" Printing: "$@"' alcohol drunken babble
    Consumed: alcohol Printing: drunken babble
    

    此处交互式shell $ sh -c "echo Consumed: $0 Printing: $@" alcohol drunken babble Consumed: -bash Printing: $0的值在传递给$@之前被替换为双引号。这是证据:

    sh

    单引号确保交互式shell不解释这些变量,并按字面意思传递给echo "Consumed: $0 Printing: $@" Consumed: -bash Printing:

    您可以使用双引号和sh -c,但最佳做法是引用您的参数(因为它们可能包含空格),\$@看起来更加丑陋,但可能会帮助您赢得混淆比赛那些疲惫的头发是入门的先决条件。

答案 4 :(得分:32)

另一种解决方案是使用marker,这是我最近创建的一个工具,可以让您进行书签"命令模板,并轻松将光标放在命令占位符:

commandline marker

我发现大部分时间,我都使用shell函数,因此我不必在命令行中反复编写常用命令。在这个用例中使用函数的问题是在命令词汇表中添加新术语,并且必须记住在real-command中引用的函数参数。标记的目标是消除这种心理负担。

答案 5 :(得分:14)

Bash别名绝对接受参数。我刚刚添加了一个别名来创建一个新的react应用程序,该应用程序接受应用程序名称作为参数。这是我的过程:

打开bash_profile以便在nano中进行编辑

nano /.bash_profile

添加别名,每行一个:

alias gita='git add .'
alias gitc='git commit -m "$@"'
alias gitpom='git push origin master'
alias creact='npx create-react-app "$@"'

注意:“ $ @”接受传递的参数,例如“ creact my-new-app”

保存并退出Nano编辑器

ctrl + o来写(按回车); ctrl + x退出

告诉终端使用.bash_profile中的新别名

source /.bash_profile

就是这样!您现在可以使用新的别名

答案 6 :(得分:10)

一旦我做了一些有趣的事情project,我仍然会使用它。当我通过cp命令coz cp复制文件时,它显示了一些动画,但没有显示任何内容,这令人沮丧。所以我做了这个别名

alias cp="~/SCR/spiner cp"

这是微调脚本

#!/bin/bash

#Set timer
T=$(date +%s)

#Add some color
. ~/SCR/color

#Animation sprites
sprite=( "(* )  ( *)" " (* )( *) " " ( *)(* ) " "( *)  (* )" "(* )  ( *)" )

#Print empty line and hide cursor
printf "\n${COF}"

#Exit function
function bye { printf "${CON}"; [ -e /proc/$pid ] && kill -9 $pid; exit; }; trap bye INT

#Run our command and get its pid
"$@" & pid=$!

#Waiting animation
i=0; while [ -e /proc/$pid ]; do sleep 0.1

    printf "\r${GRN}Please wait... ${YLW}${sprite[$i]}${DEF}"
    ((i++)); [[ $i = ${#sprite[@]} ]] && i=0

done

#Print time and exit
T=$(($(date +%s)-$T))
printf "\n\nTime taken: $(date -u -d @${T} +'%T')\n"

bye

看起来像这样

enter image description here

循环动画)

enter image description here

以下是指向上述color脚本的链接。 和新的动画周期)

enter image description here

答案 7 :(得分:9)

以下是我在~/.bashrc中的三个函数示例,其中基本上是别名,它们接受一个参数:

#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
}

参考文献:

答案 8 :(得分:9)

对于所有那些说您不能在别名中间插入参数的人,我刚刚进行了测试,发现它确实有效。

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

当我随后运行mycommand foobar时,它的工作原理就像我直接键入命令一样。

答案 9 :(得分:6)

注意:如果这个想法不明显,那么除了别名之外的任何东西都使用别名是个坏主意,第一个是别名&#39;中的&#39;函数。第二个是难以阅读的重定向/来源&#39;。此外,还有一些缺陷(我认为会很明显,但万一你会感到困惑:我并不是说它们实际上被用到了......)

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

我之前已经回答了这个问题,而且过去一直如此:

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

这是好的和好的,除非你一直避免使用功能。在这种情况下,您可以利用bash的重定向文本的巨大能力:

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

它们的长度大致相同或者取一些字符。

真正的踢球者是时差,顶部是&#39;函数方法&#39;而底部是“重定向源”#39;方法。为证明这一理论,时机不言自明:

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

这是约200个结果的底部,以随机间隔完成。似乎函数创建/销毁比重定向花费更多时间。希望这将有助于未来的访问者提出这个问题(不想让自己保留)。

答案 10 :(得分:5)

语法:

alias shortName="your custom command here"

示例:

alias tlogs='_t_logs() { tail -f ../path/$1/to/project/logs.txt ;}; _t_logs'

答案 11 :(得分:4)

您要做的就是在别名中创建一个函数:

$alias mkcd='function _mkcd(){mkdir "$1"; cd "$1"}; _mkcd'

必须将双引号括在“ $ 1”周围,因为单引号不起作用。

答案 12 :(得分:4)

要对有关创建别名以将文件移动到“垃圾箱”文件夹而不是删除文件的问题给出具体答案:

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

在场外,您必须先创建目录〜/。先删除垃圾。

然后只需给出以下命令:

$rm <filename>
$rm <dirname>

答案 13 :(得分:3)

我会发布我的(希望如此)解决方案 (对于未来的读者,最重要的是,编辑)。
所以 - 请编辑和改进/删除这篇文章中的任何内容。

在终端中:

$ alias <name_of_your_alias>_$argname="<command> $argname"

并使用它:

$<name_of_your_alias>_$argname

例如,一个名为 cat 的文件的别名 hello.txt

  • (别名是 CAT_FILE_
  • $f(是 $argname,在本例中是一个文件)
$ alias CAT_FILE_$f="cat $f"

$ echo " " >> hello.txt
$ echo "hello there!" >> hello.txt
$ echo " " >> hello.txt
$ cat hello.txt

    hello there!

测试:

CAT_FILE_ hello.txt

答案 14 :(得分:3)

如果您正在寻找将所有参数应用于函数的通用方法,而不仅仅是一个或两个或其他一些硬编码量,您可以这样做:

#!/usr/bin/env bash

# you would want to `source` this file, maybe in your .bash_profile?
function runjar_fn(){
    java -jar myjar.jar "$@";
}

alias runjar=runjar_fn;

所以在上面的例子中,我将所有参数从我运行runjar时传递给别名。

例如,如果我runjar hi there,它最终会实际运行java -jar myjar.jar hi there。如果我runjar one two three,它会运行java -jar myjar.jar one two three

我喜欢这种基于$@的解决方案,因为它适用于任意数量的参数。

答案 15 :(得分:2)

这里是例子:

alias gcommit='function _f() { git add -A; git commit -m "$1"; } ; _f'

非常重要:

  1. {之后和}之前有一个空格。
  2. 每个命令后都有一个;。如果您在最后一条命令后忘记了此内容,则会看到>提示!
  3. 该参数用引号括起来"$1"

答案 16 :(得分:2)

有合理的技术理由要求对bash别名问题进行​​通用解决,而不是有一种机制来重新定位任意参数。一个原因是,您希望执行的命令会受到执行函数导致的环境更改的不利影响。在所有其他情况下,应使用函数。

最近强迫我尝试解决这个问题的方法是,我想创建一些缩写命令来打印变量和函数的定义。所以我为此目的写了一些函数。但是,函数调用本身会(或可能)更改某些变量。其中包括:

FUNCNAME BASH_SOURCE BASH_LINENO BASH_ARGC BASH_ARGV

我一直在使用(在函数中)打印变量defns的基本命令。在set命令输出的表单中是:

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

E.g:

> V=voodoo
sv V
V=voodoo

问题:这不会打印上面提到的变量的定义,因为它们位于当前上下文中,例如,如果在交互式shell提示符中(或者不在任何函数调用中) ,FUNCNAME没有定义。但我的功能告诉我错误的信息:

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

我提出的一个解决方案已被其他人在此主题的其他帖子中提及。对于这个打印变量defns的特定命令,并且只需要一个参数,我这样做了:

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

哪个给出了正确的输出(无)和结果状态(false):

> asv FUNCNAME
> echo $?
1

但是,我仍然觉得有必要找到适用于任意数量论点的解决方案。

将任意参数传递给Bash别名命令的一般解决方案:

# (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

这个解决方案的一个好处是,在编写陷阱命令时,用于处理命令的位置参数(参数)的所有特殊技巧都将起作用。唯一的区别是必须使用数组语法。

如,

如果您想要&#34; $ @&#34;,请使用&#34; $ {CMD_ARGV [@]}&#34;。

如果你想&#34; $#&#34;,请使用&#34; $ {#CMD_ARGV [@]}&#34;。

答案 17 :(得分:2)

要获取参数,您应该使用函数!

然而,$ @在创建别名时解释,而不是在执行别名期间解析,并且转义$也不起作用。我该如何解决这个问题?

您需要使用shell函数而不是别名来解决此问题。您可以按如下方式定义foo:

function foo() { /path/to/command "$@" ;}

OR

foo() { /path/to/command "$@" ;}

最后,使用以下语法调用您的foo():

foo arg1 arg2 argN

确保将foo()添加到~/.bash_profile~/.zshrc文件。

在你的情况下,这将起作用

function trash() { mv $@ ~/.Trash; }

答案 18 :(得分:2)

正如其他人已经指出的那样,使用函数应被视为最佳实践。

但是,这是另一种利用xargs的方法:

alias junk="xargs -I "{}" -- mv "{}" "~/.Trash" <<< "

请注意,这会对流重定向产生副作用。

答案 19 :(得分:1)

尽管函数通常是一个不错的选择,就像其他人在这里所说的那样。但我想指出的是,在某些情况下,函数不起作用,而别名却可以,或者在其他情况下,使用包装脚本是更好的选择。

别名有效,功能无效,并且仍然可以传递参数。

例如:我想创建一个快捷方式来激活conda并在一个命令中提供一个conda环境。这样做很容易:

function conda_activate(){
    export PATH="$PATH:/path/to/conda"
    envname=$1
    if [ "x$envname" -ne "x" ]; then
       source activate "$envname"
    fi
}

这无法正常工作。如果有人跑conda_activate myenvsource命令执行源操作,但立即退出,并且正在运行的外壳程序没有环境。

可行的解决方案如下:

function conda_activate(){
    export PATH="$PATH:/path/to/conda"
    # do some other things
}
alias conda_env='conda_activate; source activate'
# usage example
conda_env myenv

包装脚本是一个更好的选择

我经历过好几次,当通过ssh登录或涉及切换用户名或多用户环境时,找不到别名或函数。获取点文件有一些提示和技巧,例如,一个有趣的点:alias sd='sudo '使此alias install='sd apt-get install'能够按预期工作。请注意sd='sudo '中的多余空间。但是当您有疑问时,包装脚本始终是最可靠和可移植的解决方案。

答案 20 :(得分:0)

函数确实几乎总是答案,因为已经充分贡献并通过手册页中的引用证实:“对于几乎所有目的,别名都被shell函数取代。”

为了完整性并且因为这可能是有用的(稍微更轻量级的语法),可以注意到当参数遵循别名时,它们仍然可以使用(尽管这不符合OP的要求)。这可能是最简单的示例:

alias ssh_disc='ssh -O stop'

允许我输入类似于ssh_disc myhost的smth,它按预期扩展为:ssh -O stop myhost

这对于采用复杂参数的命令非常有用(我的记忆不再是它的用途了......)

答案 21 :(得分:0)

这是另一种使用 read 的方法。我正在使用它通过名称片段粗暴搜索文件,忽略“权限被拒绝”消息。

alias loc0='{ IFS= read -r x; find . -iname "*" -print 2>/dev/null | grep $x;} <<<'

一个简单的例子:

$ { IFS= read -r x; echo "1 $x 2 ";} <<< "a b"
1 a b 2 

请注意,这会将参数作为字符串转换为变量。为此,可以在引号内使用多个参数,空格分隔:

$ { read -r x0 x1; echo "1 ${x0} 2 ${x1} 3 ";} <<< "a b"
1 a 2 b 3