ChatGPT解决这个技术问题 Extra ChatGPT

制作一个带参数的 Bash 别名?

我曾经使用 CShell (),它可以让您创建一个带参数的别名。符号类似于

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

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

确保在 args "$1" 周围使用引号
这无助于将变量插入到字符串的中间
这是我用来发现这个谬误的 lil 测试别名... alias test_args="echo PREFIX --$1-- SUFFIX",当使用 test_args ABCD 调用时会产生以下控制台输出 PREFIX ---- SUFFIX ABCD
请注意,至少从 1996 年开始,bash 文档就包含以下内容:“对于几乎所有目的,别名都被 shell 函数所取代”,我认为这是轻描淡写的。绝对没有理由使用别名而不是函数。
@WilliamPursell:“绝对没有理由”?首先,在某些情况下,某个函数无法发挥别名的作用,例如。以开放引号结尾的别名不能作为函数完成(尽管您可能无论如何都不想这样做)。更实际地,以 do 结尾的别名将不可能或不切实际地编写为函数。其次,对于 all 别名,您可以方便地使用 ZSH 中的 A-a 或 Bash 中的 C-A-e (或您配置的任何内容)扩展别名,而函数没有这样的功能。

A
Aserre

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 括起来吗?
您甚至不必声明别名。只需定义函数即可。
如果您要将别名更改为函数,source .bashrc 将添加该函数,但不会取消旧别名的别名。由于别名的优先级高于函数,因此它将尝试使用别名。您需要关闭并重新打开 shell,或者调用 unalias <name>。也许我会节省我刚刚浪费的 5 分钟。
@MartinNeal:我在 Sun 学到的一个节省时间的技巧是只做一个 exec bash:它将启动一个新的 shell,让您清楚地阅读您的配置,就像您关闭并重新打开一样,但保留该会话的环境变量设置也一样。此外,当您想要处理像堆栈一样的思考时,不带 exec 执行 bash 会很有用。
@mich - 是的,总是将 bash 变量放在引号中。例如mv "$1" "$1.bak"。如果没有引号,如果 $1 是“hello world”,您将执行 mv hello world hello world.bak 而不是 mv "hello world" "hello world.bak"
M
Mike Gleason

完善上面的答案,您可以获得像别名一样的 1 行语法,这对于 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; }

我更喜欢这个答案,因为它显示了遍历可能出现的参数数组。 $1 和 $2 只是特殊情况,这个答案也说明了这一点。
mv "$1" "$1.bak"; cp "$2" "$1" 更改为 mv "$1" "$1.bak" && cp "$2" "$1" 以在 mv 遇到问题(例如,文件系统已满)时不会丢失您的数据。
“不要忘记右括号前的分号。”很多次这样!谢谢 :)
第一个括号之后和最后一个括号之前的空格也是必需的。
@HenkLangeveld虽然您的评论完全正确,但它表明您已经存在了一段时间-我不知道我上次遇到“设备上没有剩余空间”错误是什么时候;-)。 (虽然当我在厨房柜台上找不到空间时,我经常使用它!)这些天似乎不经常发生。
E
Evan Langlois

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

我想你想要这样的东西:

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

而已!您可以使用参数 $1、$2、$3 等,或者只使用 $@ 填充它们


这个答案说明了一切。如果我从本页底部阅读,我会节省一些时间。
-1 别名和函数不等价... echo -e '#!/bin/bash\nshopt -s expand_aliases\nalias asrc='\''echo "${BASH_SOURCE[0]}"'\'' # note the '\''s\nfunction fsrc(){ echo "${BASH_SOURCE[0]}";}'>>file2&&echo -e '#!/bin/bash\n. file2\nalias rl='\''readlink -f'\''\nrl $(asrc)\nrl $(fsrc)'>>file1&&chmod +x file1&&./file1;rm -f file1 file2
您确实应该引用 $@ 以支持带有空格等的文件名。
@FuzzyLogic - 我花了几分钟试图解压你评论中的代码。将一些代码打包到无法正确格式化的注释中需要付出很多(有趣的)努力。我仍然没有弄清楚它到底做了什么,或者更重要的是,为什么它支持你的(正确的)断言。
“问错了”?假设 OP 确切地知道要问什么,如果他/她这样做了,他们很可能一开始就不会问。这是一个提问的论坛,希望能解决好的问题,但我们不要在这里过分评判。
T
Tom Hale

TL;DR:改为这样做

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

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

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

澄清

bash 别名 do 接受参数,但仅在 结束

$ alias speak=echo
$ speak hello world
hello world

通过 alias 将参数放入命令的 middle 确实是可能的,但它会变得丑陋。

不要在家里尝试这个,孩子们!

如果你喜欢规避限制并做别人说不可能的事,这里就是秘诀。如果你的头发变得蓬乱并且你的脸最终被煤烟疯狂科学家式覆盖,请不要责怪我。

解决方法是将 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 的 $0,并且是必需的,这样用户给定的参数中的第一个就不会被消耗。演示:sh -c 'echo 已使用:“$0” 打印:“$@”' 酒精醉酒 已使用:酒精 打印:醉酒 babble

单引号内的单引号是必需的。下面是一个不使用双引号的示例: $ sh -c "echo Consumed: $0 Printing: $@" Alcoholdrinken babble Consumed: -bash Printing: 这里交互式 shell 的 $0 和 $@ 的值被替换为双引号在传递给 sh 之前引用。证明如下: echo "Consumed: $0 Printing: $@" Consumed: -bash Printing: 单引号确保这些变量不会被交互式 shell 解释,而是按字面意思传递给 sh -c。您可以使用双引号和 \$@,但最佳做法是引用您的论点(因为它们可能包含空格),并且 \"\$@\" 看起来更丑陋,但可能会帮助您赢得一场头发蓬乱的混淆竞赛是进入的先决条件。


对于解决方案 1,请确保使用单引号,并避免在 $@ 周围出现 \"。如果需要,这是一种非常有用的技术。ty。
解决方案 1 对我有用,用我想要的每个服务器的参数包装 rdesktop-vrdp。
最后接受参数正是我需要用简单的“gc {branchname}”替换“git checkout {branchname}”。在 .bash_profile 我只需要添加 alias gc='git checkout'
@sgtz 为什么要删除 $@ 周围的引号?例如,如果您有包含空格的文件,则它们是必需的。
@AlexisWilke 准确。甚至更简单,您只需使用 \df
K
Ken Tran

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

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

您必须在 "$1" 周围加上双引号,因为单引号不起作用。这是因为在标有箭头的地方与引号发生冲突会使系统感到困惑。此外,该功能需要在标有星号的位置留出空间。


这很聪明,但除了问题确实指定创建别名之外,是否有任何理由不只是使函数与最初的别名具有相同的名称?
@xaxxon 不是真的,但这是我知道使用别名而不是函数的最简单方法。
这不起作用。在 Ubuntu 18 上,我收到错误 bash: syntax error near unexpected token '{mkdir'
我已经修复了语法问题。回复:删除 function 前缀,请参阅 wiki.bash-hackers.org/scripting/obsolete 中的讨论。也就是说,我不知道为什么有人会这样做而不是只定义一个函数而不使用任何别名。
@faramir 你可以使用 command。您甚至可以使用 alias c = 'command' 之类的别名,然后只需使用 c ls
A
Amine Hajyoussef

另一种解决方案是使用 marker,这是我最近创建的一个工具,它允许您“标记”命令模板并轻松地将光标放在命令占位符上:

https://i.stack.imgur.com/0fyiS.gif

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


I
Ivan

一旦我做了一些有趣的project,我仍然使用它。当我通过 cp 命令复制文件时,它会显示一些动画,因为 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

它看起来像这样

https://i.stack.imgur.com/e3DAc.gif

循环动画)

https://i.stack.imgur.com/kTUMZ.gif

这是上述 color 脚本的链接。和新的动画周期)

https://i.stack.imgur.com/ifc5n.gif


这是无价的。 🤣
但是,这根本不能回答这个问题。
不,但实际上这是另一回事,我同意@ctf0 AND Triplee - 尽管它没有回答问题,但这 - 仍然*是其他人每天都看不到的东西,并且投票集是一个先例 xD
ס
סטנלי גרונן

句法:

alias shortName="your custom command here"

例子:

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

真是聪明的解决方案!如果您尝试此操作并仅收到 > 提示,请确保您在 } 之前获得了 ;。另请注意,每次使用别名时都会重新定义函数,因此如果函数定义很昂贵,最好将其移出 alias
为什么要这样做而不是仅仅定义一个函数而根本没有任何别名?
我为这个别名使用了这个想法来为 kubectl 的 json 输出着色。谢谢! alias kj='_kc_jq() { kc get -o json $@ |jq; }; _kc_jq'
B
Billeh

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

在 nano 中打开 bash_profile 进行编辑

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

而已!您现在可以使用新别名


如果参数位于命令中间,这似乎不起作用。我怀疑正在发生的事情是你调用别名后跟“参数”,它恰好在你的命令末尾,所以它被解释为你最后的参数在最后。例如 gitc foo 运行 git commit -m foo 不是因为您有 $@ 而是因为 foo 出现在最后——它正在运行 git commit -m 并且您碰巧有一个 foo 紧随其后并被附加,这使您的命令有效。
综上所述,别名接受参数,但仅在放在别名末尾时。这是一种有限的参数。而且您不需要别名 def 中的 $@
您可以从别名中删除所有位置变量引用,它仍然可以工作 - 所有这些占位符都替换为空字符串,并且给别名调用的参数只是简单地附加到别名的扩展中,在其末尾。因此,您可能认为它按您描述的方式工作,但实际上并非如此。您将了解到,只有将占位符放在别名的中间或开头。
如果您在调用别名之前运行 set -- "first argument" "second argument",则所有 "$@" 都将变成 "first argument" "second argument"。它们不反映传递给别名的参数;它们只反映在调用别名的上下文中处于活动状态的参数
C
Community

以下是我在 ~/.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
}

参考:

从字符串中修剪空格:如何从 Bash 变量中修剪空格?

实际换行符:https://stackoverflow.com/a/4296147/2736496

带参数的别名:https://stackoverflow.com/a/7131683/2736496(这个问题的另一个答案)

历史记录:https://superuser.com/questions/232885/can-you-share-wisdom-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


嗯?这是一个函数,而不是别名。您的参考资料之一就是这个问题。
不,它的工作原理与函数完全一样。就像这里解释的几个答案一样,您不能制作带有参数的别名。你能做的就是写一个函数,这就是你所做的。
@tripleee 从我的 bash 新手的角度来看,在阅读您的评论之前,我认为这是一个别名(创建别名的另一种方法)。据我所知,它的功能完全一样。它本质上是一个接受参数的别名,即使我不使用术语。我没有看到问题。我已经澄清了这个问题中另一个答案的链接。它帮助我创造了这个。
也许这并没有具体回答“接受参数的 bash 别名”的问题,因为我现在明白这是不可能的,但它确实有效地回答了它。我在这里想念什么?
这里有一些非常好的例子,以及很好的注释代码。对访问此页面的人有很大帮助。
c
ctf0

尊重所有那些说你不能在别名中间插入参数的人,我刚刚测试了它,发现它确实有效。

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

然后,当我运行 mycommand foobar 时,它的工作方式就像我以手写方式输入命令一样。


绝对, alias 可以获取参数,我已经使用这个参数了,例如: alias commit="git commit -m $1"
对我不起作用:alias myalias="echo 1 "$1" 3"; myalias 2 给我:1 3 2
“没有在替换文本中使用参数的机制,如在 csh 中”gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Aliases 您的命令可能有效,因为 appending 无论您在该 cmd 行末尾放入 $1 中的任何内容仍然适用于您的任何内容正在做。
将与以下内容相同:alias mycommand = "python3 script.py --folderoutput RESULTS/"(省略 "$1")。相同,因为 alias 在 bash 中没有设置位置参数。在交互式 shell 中,它们的值是 "" 一个空字符串,除非你在里面放了一些东西。为了进一步测试,在定义别名之前尝试真正添加一些东西到 $1: set -- something 。现在您的别名很可能已损坏,请查看该别名包含的内容:alias mycommand
@agapitocandemor 如果您的评论不是开玩笑(不是那么明显......),那么您的别名被破坏并且它“有效”只是因为 $1 被空字符串替换并且您的评论被附加。您可以安全地将您的别名重新定义为 git commit -m,它仍然可以工作。
M
Micah

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

#!/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 而不是将其设为函数的别名?看起来不必要的复杂!
@EvanLanglois 函数在 bash 文件中自动被 alias 编辑(或将事物的属性传递给 alias)?如果是,那只是我不知道
不明白你的意思。别名是另一个名称,无论是人还是函数。因此,您创建了一个函数,然后为该函数创建了第二个名称。别名没有什么特别之处,它只是第二个名字,不是必需的。您在命令行键入的内容可以是内部函数或外部函数,因此“函数”会生成一个新命令,而不是别名。
这是没有意义的。与 alias runjar='java -jar myjar.jar' 相同。别名确实接受参数,但仅在最后。
o
osirisgothra

注意:如果这个想法不明显,那么将别名用于除别名之外的任何东西都是一个坏主意,第一个是“别名中的函数”,第二个是“难以阅读的重定向/源”。此外,也存在缺陷(我认为这很明显,但以防万一你感到困惑:我并不是说它们实际上可以用于......任何地方!)

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

这个我以前回答过,过去一直是这样的:

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

这很好,除非你避免一起使用函数。在这种情况下,您可以利用 bash 重定向文本的强大功能:

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
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 函数所取代。
第二个(酒吧别名)有几个副作用。首先该命令不能使用标准输入。其次,如果在别名之后没有提供任何参数,那么 shell 碰巧有的任何参数都会被传递。使用函数可以避免所有这些副作用。 (也无用的使用猫)。
嘿,我从来没有说过这样做是个好主意,我只是说还有其他方法可以解决它——这里的主要思想是你不应该在别名中使用函数,因为它们甚至比复杂的函数更慢重定向,我认为这很明显,但我想我必须为每个人都拼出来(如果我首先这样做,我也会因为帖子太臃肿而大喊大叫)
由于 bar 的时间都是 0,我怀疑 shell 的时间不正确。请注意,在命令运行之前打印了时间消息?因此,它什么都不计时,然后执行 bar,所以你根本没有测量 bar。做一些更复杂的事情,或者在 bar 中做一个大循环,这样你就可以更明显地表明你没有计时
我现在尝试了这个(尽管我不得不用 __foo 替换最后的 __foo())。 time for i in {1..1000}; do foo FOOVALUE; done → 0m0.028s。但是 time for i in {1..1000}; do bar FOOVALUE; done → 0m2.739s。 Bar 比 foo 慢两个数量级。仅使用普通函数而不是别名将运行时间再缩短 30%:function boo() { echo "arg1 for boo=$1" ; }time for i in {1..1000}; do boo FOOVALUE; done → 0m0.019s。
s
shwz

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

在终端:

$ 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

哦,天哪——对不起;我在格式化代码时遇到了很多问题,如果有人想改进帖子,请做; //最好的祝愿
传递参数时,参数前需要一个空格
这可行,但出于完全不同的原因。它之所以有效,只是因为 $f 在末尾。 $f 在别名定义时可能为空,因此解析为 alias CAT_FILE_="cat "。只需运行 alias 即可查看生成的定义。因此,当您调用 CAT_FILE_ 时,您只需将参数传递给别名,就像 alias ll="ls -l" 中的任何其他别名用法一样。尝试将“参数” $f 放在其他地方而不是最后,这将不起作用。例如:alias ECHO_ARG_$f="echo xxx $f yyy"。然后 ECHO_ARG_ test 通向 xxx yyy test
@HolgerBöhnke你是对的,非常感谢你的观点;out! :竖起大拇指图标:
c
crobc1

有正当的技术原因需要一个通用的解决方案来解决 bash 别名的问题,因为它没有一种机制来重新定位任意参数。一个原因是,如果您希望执行的命令会受到因执行函数而导致的环境变化的不利影响。在所有其他情况下,应使用函数。

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

FUNCNAME BASH_SOURCE BASH_LINENO BASH_ARGC BASH_ARGV

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

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

例如:

> V=voodoo
sv V
V=voodoo

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

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

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

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

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

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

例如:

> . 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[@]}”。

等等。


d
dirdi

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

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

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

请注意,这对流的重定向有副作用。


你认为内引号在做什么? (它们不会成为别名的一部分;如果您想要它们,则需要转义它们:alias junk=xargs -I \"{}\" -- mv \"{}\" \"~/.Trash\" <<<")。
也就是说,您实际上并不想要这些引号,因为 xargs 没有进行文本替换(它对在 shell 解析已经结束后生成的 C 字符串数组进行操作,所以引号不再具有改变 shell 行为的价值) ,并且当引用 ~ 时不起作用。所以这会更好,只是 alias junk='xargs -I {} -- mv {} ~/.Trash <<<'
@CharlesDuffy 随时编辑和改进我的答案。
S
Shital Shah

这是示例:

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

很重要:

{ 之后和 } 之前有一个空格。有一个;在每个命令之后。如果您在最后一个命令之后忘记了这一点,您将看到 > 提示!参数用引号括起来,如 "$1"


不需要为函数创建别名,直接使用函数即可。
N
Neeraj Jain

要具体回答有关创建别名以将文件移动到垃圾箱文件夹而不是删除它们的问题:

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

当然,您必须先创建 dir ~/.Trash。

然后只需给出以下命令:

$rm <filename>
$rm <dirname>

甚至更好并且结果相同:alias rm="mv "$i_bellieve_this_is_correct_because_it_WORKS" -t ~/.Trash/" 仍然不相信的人可以在之前运行此命令:set -- my_very_important_file 如果您确信 $1 在别名中有效,请按字面意思运行它或使用指向您非常重要文件的路径。 now 声明:建议的 alias rm="mv "$1" -t ~/.Trash/"rm <filename>set -- 将值设置为位置参数 $1。别名命令没有。
正如 papo 上面所说:这只是看起来有效,因为即使 $1 完全为空,mv 仍然尊重尾随位置的参数。 $1 不是由传递给别名的参数填充;它是从运行别名的 shell 中预先存在的 $1 值填充的。
A
Anton Abrosimov

使用子命令的解决方案:

d () {
    if [ $# -eq 0 ] ; then
        docker
        return 0
    fi
    CMD=$1
    shift

    case $CMD in
    p)
        docker ps --all $@
        ;;
    r)
        docker run --interactive --tty $@
        ;;
    rma)
        docker container prune
        docker image prune --filter "dangling=true"
        ;;
    *)
        docker $CMD $@
        ;;
    esac
    return $?
}

使用:

$ d r my_image ...

称为:

docker run --interactive --tty my_image ...

s
sxc731

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

为了完整起见并且因为这可能有用(略微更轻量级的语法),可以注意到当参数跟随别名时,它们仍然可以使用(尽管这不能满足 OP 的要求)。这可能是最容易用一个例子来证明的:

alias ssh_disc='ssh -O stop'

允许我像 ssh_disc myhost 一样键入 smth,它会按预期扩展为:ssh -O stop myhost

这对于需要复杂参数的命令很有用(我的记忆不再是它使用的东西了......)


我没想到它实际上*会这样做,
A
Ahmad Awais

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

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

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

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

或者

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

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

foo arg1 arg2 argN

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

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

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

我不明白你的想法。我认为论点何时被解释并不重要。 Aliase 最后接受尽可能多的参数。
但最好用函数来做到这一点。别名不是为了那个。
b
biocyberman

函数和别名都可以使用其他人在此处显示的参数。此外,我想指出其他几个方面:

1.函数在自己的作用域内运行,别名共享作用域

在您需要隐藏或公开某些内容的情况下,了解这种差异可能会很有用。它还表明函数是封装的更好选择。

function tfunc(){
    GlobalFromFunc="Global From Func" # Function set global variable by default
    local FromFunc="onetwothree from func" # Set a local variable

}

alias talias='local LocalFromAlias="Local from Alias";  GlobalFromAlias="Global From Alias" # Cant hide a variable with local here '
# Test variables set by tfunc
tfunc # call tfunc
echo $GlobalFromFunc # This is visible
echo $LocalFromFunc # This is not visible
# Test variables set by talias
# call talias
talias
echo $GlobalFromAlias # This is invisible
echo $LocalFromAlias # This variable is unset and unusable 

输出:

bash-3.2$     # Test variables set by tfunc
bash-3.2$     tfunc # call tfunc
bash-3.2$     echo $GlobalFromFunc # This is visible
Global From Func
bash-3.2$     echo $LocalFromFunc # This is not visible

bash-3.2$     # Test variables set by talias
bash-3.2$     # call talias
bash-3.2$     talias
bash: local: can only be used in a function
bash-3.2$     echo $GlobalFromAlias # This is invisible
Global From Alias
bash-3.2$ echo $LocalFromAlias # This variable is unset and unusable

2.包装脚本是更好的选择

我遇到过好几次在 logging in via ssh 或涉及切换用户名或多用户环境时找不到别名或函数的情况。获取点文件有一些技巧和窍门,或者这个有趣的别名:alias sd='sudo ' 让这个后续的别名 alias install='sd apt-get install' 按预期工作(注意 sd='sudo ' 中的额外空间)。但是,在这种情况下,包装脚本比函数或别名更有效。包装脚本的主要优点是它在预期路径(即/usr/loca/bin/)下是可见/可执行的,其中作为函数/别名需要在它可用之前获取源。例如,您在 ~/.bash_profile 或 ~/.bashrc 中为 bash 放置了一个函数,但稍后切换到另一个 shell(即 zsh),则该函数不再可见。因此,当您有疑问时,包装脚本始终是最可靠和可移植的解决方案。


为什么你认为功能版本不起作用?
@tripleee。我已经编辑了帖子以使其更具可读性。在标题“1. alias works, function ...”下,我说明了为什么函数版本不起作用。也许为了直接回答这个问题,函数引入了一个别名没有的新范围。
你说这行不通,但我不相信你。 source 在函数中工作得很好。
f () { source /tmp/nst; } 完全符合我的预期。也许您的 PATH 是错误的,所以它运行错误的 activate 或其他东西;但从函数运行 source 工作正常。
顺便说一句,重新:在您的定义中使用 function 关键字,请参阅 wiki.bash-hackers.org/scripting/obsolete - function funcname { 是古老的 ksh 语法 bash 支持向后兼容 pre-POSIX shell,而 funcname() { 是官方 POSIX 标准化语法。 function funcname() { 是与古代 ksh 与 POSIX sh 不兼容的两者的混搭,因此最好避免。
e
elomage

这是使用 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 

d
d9k
alias junk="delay-arguments mv _ ~/.Trash"

delay-arguments 脚本:

#!/bin/bash

# Example:
# > delay-arguments echo 1 _ 3 4 2
# 1 2 3 4
# > delay-arguments echo "| o n e" _ "| t h r e e" "| f o u r" "| t w o"
# | o n e | t w o | t h r e e | f o u r

RAW_ARGS=("$@")

ARGS=()

ARG_DELAY_MARKER="_"
SKIPPED_ARGS=0
SKIPPED_ARG_NUM=0
RAW_ARGS_COUNT="$#"

for ARG in "$@"; do
  #echo $ARG
  if [[ "$ARG" == "$ARG_DELAY_MARKER" ]]; then
    SKIPPED_ARGS=$((SKIPPED_ARGS+1))
  fi
done

for ((I=0; I<$RAW_ARGS_COUNT-$SKIPPED_ARGS; I++)); do
  ARG="${RAW_ARGS[$I]}"
  if [[ "$ARG" == "$ARG_DELAY_MARKER" ]]; then
    MOVE_SOURCE_ARG_NUM=$(($RAW_ARGS_COUNT-$SKIPPED_ARGS+$SKIPPED_ARG_NUM))
    MOVING_ARG="${RAW_ARGS[$MOVE_SOURCE_ARG_NUM]}"
    if [[ "$MOVING_ARG" == "$ARG_DELAY_MARKER" ]]; then
      echo "Error: Not enough arguments!"
      exit 1;
    fi
    #echo "Moving arg: $MOVING_ARG"
    ARGS+=("$MOVING_ARG")
    SKIPPED_ARG_NUM=$(($SKIPPED_ARG_NUM+1))
  else
    ARGS+=("$ARG")
  fi
done

#for ARG in "${ARGS[@]}"; do
  #echo "ARGN: $ARG"
#done

#echo "RAW_ARGS_COUNT: $RAW_ARGS_COUNT"
#echo "SKIPPED_ARGS: $SKIPPED_ARGS"

#echo "${ARGS[@]}"
QUOTED_ARGS=$(printf ' %q' "${ARGS[@]}")
eval "${QUOTED_ARGS[@]}"


这只是针对非问题的复杂而脆弱的解决方案。正确的解决方案是不使用别名。 (也许切切地注意到 GNU mv 有一个 -t 选项,它以不同的方式解决该特定命令的问题。)