生成一个采用参数的bash别名?

Make a Bash alias that takes a parameter?

我曾经使用CShell(CSH),它允许您创建一个接受参数的别名。符号是这样的

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

在bash中,这似乎不起作用。考虑到bash有许多有用的特性,我假设这个特性已经实现,但是我想知道如何实现。

  • shell脚本可能重复:如何将命令行参数传递给UNIX别名?
  • 确保在args "$1"周围使用引号。
  • 这个问题不适合这样讨论。它在unix.se上得到了回答,答案是您甚至不需要麻烦:"例如,如果您将ls别名为ls -la,那么键入ls foo bar实际上会在命令行上执行ls -la foo bar"。
  • 这对将变量插入到字符串中间没有帮助


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

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

1
2
3
4
5
6
7
8
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中的命令使用。例如,您可以调用前面的函数

1
$ myfunction original.conf my.conf

  • 谢谢!我喜欢它。这允许我在Mac上创建一个别名来转换dos2unix for perl。my_d2u() { tr -d '
    ' < $1 > $2 } alias dos2unix=my_d2u; alias d2u=my_d2u;
  • 我应该用引号把EDOCX1[1]包起来吗?
  • 您甚至不必声明别名。只要定义函数就行了。
  • 如果要将别名更改为函数,则使用.bashrc的source将添加该函数,但不会取消对旧别名的引用。由于别名的先例高于函数,因此它将尝试使用别名。您需要关闭并重新打开shell,或者致电unalias 。也许我能省下我浪费的5分钟。
  • @马丁尼尔:我在Sun学到的一个省时的技巧是只做一个exec bash:它将启动一个新的shell,让您清楚地了解您的配置,就像您关闭并重新打开一样,但也要保留该会话的环境变量设置。另外,如果您想处理像堆栈一样的思考,那么在不使用exec的情况下执行bash是很有用的。
  • 函数是持久化的还是只对shell会话的作用域可用?如果是后者,你怎么能坚持下去呢?
  • @Raven你用"~/.bashrc"来写它们,使它们持久。
  • 谢谢!顺便说一句,仅仅定义函数就行了。不需要单独定义别名
  • 多酷啊!函数允许通过输入路径作为参数来获取任何位置的最大文件夹和路径:duBiggests() { du -am"$1" | sort -nr | more }
  • @是的,总是把bash变量放在引号中。如mv"$1""$1.bak"。如果没有报价,如果1美元是"你好世界",你将执行mv hello world hello world.bak,而不是mv"hello world""hello world.bak"
  • 我们在贝壳里的工作都不一样。我发现对函数进行别名很有用,比如函数fxx find。| xargs grep$*2>/dev/null;,然后别名fx='fxx'。然后,我可以使用"which"和"alias"来查找自定义命令,以防忘记。比散射更容易。bashrc(是的,我可以为它做个别名…)
  • mynamemyfunction应该总是不同的?
  • A例如,我为git命令创建了一些东西。GIT结账。
  • 你怎么能不在函数前面加声明函数(例如,函数myfunction()在这里做shell的事情)就离开呢?
  • 如果你把它放在~/.bashrc或类似的地方,那么函数名必须用function作为前缀。例如function myfunc{echo $1}
  • 把它放在一行:myfunction()mv"$1""$1.bak";cp"$2""$1";
  • 使用函数docs/examples可以在bash.cyberciti.biz/guide/passu argumentsu中找到。
  • 这与在zsh上采购.aliases相结合,bash是有史以来最干净的答案!
  • 为什么在第一段代码中调用MyFunction,而在第二段代码中调用MyFunction,即使您似乎引用了相同的函数?


优化上述答案后,您可以像处理别名那样得到一行语法,这对于shell或.bashrc文件中的即席定义更为方便:

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

bash$ myfunction original.conf my.conf

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

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

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

或:

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

  • 我更喜欢这个答案,因为它显示了对可能出现的参数数组的迭代。1美元和2美元只是特殊情况,在这个答案中也有说明。
  • mv"$1""$1.bak"; cp"$2""$1"改为mv"$1""$1.bak" && cp"$2""$1",以免在mv遇到问题(如文件系统已满)时丢失数据。
  • "别忘了右括号前的分号。"很多次这样!谢谢)
  • 第一个括号后和最后一个括号前也需要空格。
  • @亨克兰杰维德,虽然你的话是完全正确的,它表明你已经在附近一段时间了——我不知道什么时候我最后一次遇到一个"没有空间留在设备上"的错误;-)。(不过,当我再也找不到厨房柜台的空间时,我还是经常使用它!)这些天似乎不经常发生。
  • @Petera.Schneider Well、mvcp返回状态代码。为什么有状态代码而不使用它们?谁知道还会出什么问题?shell编程仍然是系统编程。故障安全。
  • @你说得对!我不是想暗示人们应该忽略存在状态。"没有空间"的东西只是唤起记忆。(这些天我面临的问题更加复杂,比如"当你在Cygwin的fstab中启用acl处理时,它会把acl搞砸,这样Windows程序就无法访问整个目录树……"。


这个问题只是问错了。您不需要使用参数来创建别名,因为alias只是为已经存在的对象添加了第二个名称。OP想要的功能是创建新功能的function命令。您不需要为函数加别名,因为函数已经有了名称。

我想你想要这样的东西:

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

就是这样!您可以使用参数$1、$2、$3等,或者只是将它们全部填充。$@

  • 这个答案说明了一切。如果我从这一页的底部阅读,我会节省一些时间。
  • -1别名和函数不是等价的…echo -e '#!/bin/bash
    shopt -s expand_aliases
    alias asrc='\''echo"${BASH_SOURCE[0]}"'\'' # note the '\''s
    function fsrc(){ echo"${BASH_SOURCE[0]}";}'>>file2&&echo -e '#!/bin/bash
    . file2
    alias rl='\''readlink -f'\''
    rl $(asrc)
    rl $(fsrc)'>>file1&&chmod +x file1&&./file1;rm -f file1 file2
  • 您真的应该引用$@来支持带有空格的文件名等。
  • 这应该是一个一般性的解释,即您没有别名参数,而是使用一个函数。给定的函数只是一个与原始示例相反的示例。我不认为那个人在寻找一个通用的垃圾处理功能。但是,为了提供更好的代码,我更新了答案。
  • @fuzzylogic-我花了几分钟的时间试图在您的评论中解包代码。要将一些代码打包到一个注释中,而您无法正确格式化它,这是一项非常(有趣的)工作。我还没有弄清楚它到底做了什么,更重要的是,它为什么支持你的(正确的)断言。
  • @乔除了让它作为一行代码运行并删除测试文件之外,没有做任何工作。如果你想看看到底发生了什么,只需在最后评论一下rm -f ..部分,它就不会删除测试文件。它所做的是file2定义一个别名和一个函数,该函数读取脚本路径的名称,然后file2从file1中包含,从中调用别名和函数。从输出中可以看出,尽管它们都在file2中定义并且具有相同的代码,但它们都会产生不同的结果。原因是别名存储为字符串。


tl;dr:改为这样做

使用函数比使用别名将参数放在命令中间要容易得多,可读性也更高。

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

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

澄清

bash别名确实接受参数,但仅在结尾:

1
2
3
$ alias speak=echo
$ speak hello world
hello world

通过alias将论点置于命令的中间确实是可能的,但它变得很难看。

别在家里试这个,孩子们!

如果你喜欢规避限制,做别人说的不可能的事,那么这就是秘诀。别怪我,如果你的头发被吹得花枝招展,你的脸上也沾满了科学家的烟灰。

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

解决方案1

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

1
2
3
$ 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

还可以使用子外壳:

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

说明2

别名生成如下命令:

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

评论:

  • 占位符_是必需的,但它可以是任何内容。它被设置为sh$0,并且是必需的,这样用户给定的第一个参数就不会被使用。论证:

    1
    2
    sh -c 'echo Consumed:"$0" Printing:"$@"' alcohol drunken babble
    Consumed: alcohol Printing: drunken babble
  • 单引号内的单引号是必需的。下面是一个不使用双引号的示例:

    1
    2
    $ sh -c"echo Consumed: $0 Printing: $@" alcohol drunken babble
    Consumed: -bash Printing:

    在这里,交互shell的$0$@的值在传递给sh之前被替换为双引号。以下是证据:

    1
    2
    echo"Consumed: $0 Printing: $@"
    Consumed: -bash Printing:

    单引号确保这些变量不会被交互shell解释,并从字面上传递给sh -c

    你可以使用双引号和\$@,但最好的做法是引用你的论点(因为它们可能包含空格),\"\$@\"看起来更丑,但可能有助于你赢得一场模糊比赛,在比赛中,头发是进入的先决条件。

  • 对于解决方案1,请确保使用单引号,并避免"in around the$@"。如果你需要的话,这是一个非常有用的技巧。TY。
  • 解决方案1为我工作,用我想要的每台服务器的参数包装RDESKTopVRDP。
  • 在末尾接受参数正是我将"git checkout branchname"替换为简单的"gc branchname"所需要的。在.bash配置文件中,我只需要添加alias gc='git checkout'
  • @SGTZ为什么要删除$@的报价?如果您有文件(如其中有空格的文件),它们是必需的。


另一种解决方案是使用marker,这是我最近创建的一种工具,它允许您"为命令模板加书签",并轻松地将光标放在命令位置固定器上:

commandline marker

我发现大多数时候,我使用的是shell函数,所以我不必在命令行中反复编写常用的命令。在这个用例中使用函数的问题是,在我的命令词汇表中添加新的术语,并且必须记住实际命令中的函数参数是指什么。标记的目标是消除这种心理负担。


下面是我在~/.bashrc中使用的三个函数示例,它们本质上是接受参数的别名:

1
2
3
#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'"

.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
:<<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
"

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
}

.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
:<<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
"

hx()  {
    history -d"$1"
}

.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
:<<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
"

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
}

参考文献:

  • 从字符串中删除空白:如何从bash变量中删除空白?
  • 实际换行符:https://stackoverflow.com/a/4296147/2736496
  • 别名w/param:https://stackoverflow.com/a/7131683/2736496(此问题的另一个答案)
  • histignore:https://superuser.com/questions/232885/can-you-share-withom-on-using-histignore-in-bash
  • 是/否提示:https://stackoverflow.com/a/3232082/2736496
  • 从历史记录中删除所有匹配项:https://unix.stackexchange.com/questions/57924/how-to-delete-commands-in-history-matching-a-given-string
  • 字符串是否为空:http://tldp.org/ldp/abs/html/comparison-ops.html

  • 呵呵?这是一个函数,而不是别名。你的一个参考就是这个问题。
  • 它的工作原理和我的化名一模一样。在这种情况下,不需要显式别名声明。
  • 不,它的工作原理和函数完全一样。正如这里的几个答案所解释的,您不能创建一个接受参数的别名。你能做的就是写一个函数,这就是你所做的。
  • @从我的bash新手的角度来看,在阅读你的评论之前,我认为这是一个别名(创建别名的另一种方法)。据我所知,它的功能和一个一模一样。它本质上是一个接受参数的别名,即使我不了解这个术语。我看不出问题所在。我已经澄清了这个问题中另一个答案的链接。它帮助我创造了这个。
  • 也许这并没有具体回答"一个接受参数的bash别名"的问题,因为我现在知道这是不可能的,但它确实有效地回答了这个问题。我这里缺什么?
  • 这里有一些非常好的例子,以及注释很好的代码。对来到这一页的人有很大帮助。


注意:如果这个想法不明显,那么除了别名以外,其他任何东西都使用别名是一个坏主意,第一个别名是"别名中的函数",第二个别名是"难以读取的重定向/源代码"。另外,还有一些缺陷(我认为是显而易见的,但只是为了防止你感到困惑:我不是指它们被实际使用…到处都是!)

…………………

我以前回答过这个问题,过去总是这样:

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

这是很好的,除非您避免同时使用函数。在这种情况下,您可以利用bash的巨大能力来重定向文本:

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

它们的长度大致相同,可以用几个字符表示。

真正的推动因素是时间差,顶部是"函数方法",底部是"重定向源"方法。为了证明这一理论,时机本身就说明了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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个结果的底部部分,以随机间隔完成。似乎函数创建/销毁比重定向花费的时间更多。希望这将有助于未来的访问者对这个问题(不想让自己知道)。

  • 有一个别名来定义一个函数,然后运行那个函数是很愚蠢的。只需编写一个函数。对于几乎所有的用途,别名都被shell函数取代。
  • 如果你没有读完整本书,你可以试试看,我解释了为什么使用函数方法一开始就没有那么好,而且,这一点也不傻。不要仅仅因为你个人不喜欢就发帖子和投反对票…或者至少给出一个合理的解释。叫名字是第一个标志,心胸狭窄…
  • 第二个(条形别名)有几个副作用。首先,命令不能使用stdin。其次,如果在别名后面没有提供参数,那么不管shell碰巧传递了什么参数。使用函数可以避免所有这些副作用。(也不能用猫)。
  • 嘿,我从来没有说过这样做是个好主意,我只是说还有其他方法可以解决这个问题——这里的主要想法是不要在别名中使用函数,因为它们比复杂的重定向速度还要慢,我认为这是显而易见的,但我想我必须为每个人把它拼出来(我也会得到叫喊)如果我一开始就这么做的话,我会觉得这个职位太臃肿)
  • 因为酒吧的时间都是0,我怀疑壳牌没有正确的时间安排。注意到在命令运行之前打印消息的时间吗?因此,它不乘以任何值,然后执行BAR,所以你根本就不测量BAR。做一些更复杂的事情,或者在酒吧里做一个大的循环,这样你就可以更明显地发现你什么都不定时。
  • 我现在尝试了这个方法(尽管我必须把最后的foo()替换为foo)。time for i in {1..1000}; do foo FOOVALUE; done~0.028s,但time for i in {1..1000}; do bar FOOVALUE; done~0.739s,bar比foo慢两个数量级。仅仅使用一个普通函数而不是别名就可以将运行时减少30%:function boo() { echo"arg1 for boo=$1" ; }time for i in {1..1000}; do boo FOOVALUE; done→0.019秒。


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

1
2
3
4
5
6
7
8
#!/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

我喜欢这个基于$@的解决方案,因为它可以处理任意数量的参数。

  • 为什么不将函数命名为runjar,而不将其作为函数的别名?似乎不必要的复杂!
  • @在bash文件中,evanlanglois函数是自动的aliased(或者具有传递给alias的事物的属性)?如果是,那只是我不知道
  • 不明白你的意思。别名是另一个名称,可以是人名,也可以是函数。所以,您创建了一个函数,然后为该函数创建了第二个名称。别名没有什么特别之处,它只是第二个名称,不需要。在命令行中键入的内容可以是内部函数或外部函数,因此"函数"将生成一个新命令,而不是别名。
  • 这是毫无意义的。与alias runjar='java -jar myjar.jar'相同。别名确实接受参数,但只在末尾接受。


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

最近迫使我尝试解决这个问题的是,我想创建一些用于打印变量和函数定义的缩写命令。为此我编写了一些函数。但是,有一些变量是(或可能)由函数调用本身更改的。其中有:

函数名巴什源巴什利林诺巴什尔加尔克巴什亚尔夫

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

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

例如。:

1
2
3
> V=voodoo
sv V
V=voodoo

问题:这不会像在当前上下文中那样打印上述变量的定义,例如,如果在交互式shell提示(或不在任何函数调用中)中没有定义funcname。但是我的函数告诉我错误的信息:

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

我提出的一个解决方案已经被其他人在这个主题的其他文章中提到。对于这个打印变量defns.的特定命令,它只需要一个参数,我这样做了:

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

给出正确的输出(无)和结果状态(假):

1
2
3
> asv FUNCNAME
> echo $?
1

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

向bash别名命令传递任意参数的一般解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# (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".

例如。:

1
2
3
4
5
6
7
> . 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[@]"。

如果您想要"$",请使用"$cmd_argv[@]"。

等。


对于获取参数,应该使用函数!

但是,在创建别名而不是在执行别名和转义$的过程中,$@get也不起作用。我该如何解决这个问题?

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

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

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

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

1
foo arg1 arg2 argN

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

在你的情况下,这是可行的

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

  • 我不明白你的想法。我认为解释这些论点并不重要。别名在末尾接受尽可能多的参数。
  • 但最好用函数来实现。别名并不是为了这个。


虽然函数通常是一个很好的选择,就像其他人在这里所说的。但我想指出的是,有些情况下函数不起作用,而别名起作用,或者其他情况下,包装脚本是更好的选择。

别名有效,但函数无效,仍可以传递参数。

例如:我想创建一个快捷方式来激活conda,并在一个命令中源代码一个conda环境。这样做是很有诱惑力的:

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

这不符合预期。如果一个运行conda_activate myenvsource命令执行寻源,但立即退出,运行的shell没有环境。

工作解决方案如下:

1
2
3
4
5
6
7
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 '中的额外空间。但是,当您有疑问时,包装脚本总是最可靠和可移植的解决方案。


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

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

您必须在"$1"周围加上双引号,因为单引号不起作用。


实际上,函数几乎总是一个答案,正如在手册页中引用并确认的那样:"几乎每一个目的,别名都被shell函数所取代。"

为了完整性,并且因为这是有用的(稍微轻一些的语法),可以注意到,当参数跟在别名后面时,它们仍然可以使用(尽管这不能满足OP的要求)。这可能最容易用一个例子来演示:

1
alias ssh_disc='ssh -O stop'

允许我键入smth,如ssh_disc myhost,按预期扩展为:ssh -O stop myhost

这对于接受复杂参数的命令很有用(我的内存不再是它所用的了…)