问答中心分类: BASH制作一个带参数的 Bash 别名?
0
匿名用户 提问 3天 前

我曾经使用 CShell (csh),它允许您创建一个带有参数的别名。符号类似于

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

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

Christophe Roussy 回复 3天 前

确保在 args 周围使用引号"$1"

Christophe Roussy 回复 3天 前

这个问题对于SO来说是题外话。它是在 UNIX.SE 上回答,答案是你甚至不需要费心:“例如,如果你要别名lsls -la,然后输入ls foo bar真的执行ls -la foo bar在命令行上。”

Christophe Roussy 回复 3天 前

这无助于将变量插入到字符串的中间

Christophe Roussy 回复 3天 前

这是我用来发现这个谬误的 lil 测试别名……alias test_args="echo PREFIX --$1-- SUFFIX", 当调用test_args ABCD产生以下控制台输出PREFIX ---- SUFFIX ABCD

Christophe Roussy 回复 3天 前

请注意,至少从 1996 年开始,bash 文档就包含以下内容:“对于几乎所有目的,别名都被 shell 函数所取代”,我认为这是轻描淡写的。绝对没有理由使用别名而不是函数。

Christophe Roussy 回复 3天 前

@WilliamPursell:“绝对没有理由”?首先,在某些情况下,某个函数无法发挥别名的作用,例如。以开放引号结尾的别名不能作为函数完成(尽管您可能无论如何都不想这样做)。更现实地说,别名以do写成函数是不可能或不切实际的。其次,对于全部别名,您可以方便地扩展别名A-a在 ZSH 或C-A-e在 Bash(或您配置的任何内容)中,而没有可用于函数的此类功能。

Christophe Roussy 回复 3天 前

@pyrocrasty 在 bash 中,您可以使用制表符扩展来扩展函数名称。 (不确定zsh)。

Christophe Roussy 回复 3天 前

@WilliamPursell:我不是指名称完成,我的意思是您可以将别名扩展为内联定义。 (尝试输入别名,然后C-A-e将光标直接放在它后面)当您不记得命令的选项但有一个接近您需要的别名时,它特别方便。

Christophe Roussy 回复 3天 前

@pyrocrasty 这是一个方便的功能(如果我使用别名!)。对于未来的读者,CAe 是 alias-expand-line 函数的默认 readline 绑定,在 vi 模式下默认不绑定。

Christophe Roussy 回复 3天 前

我广泛使用了不能作为函数的别名,这在我的所有脚本中都是必不可少的。例如,这是一段代码,除了体面的异常处理外,它还添加了自动跟踪机制:alias begin_function='trace=$((trace-1)); [ $trace -lt $bash_trace ] && set +x || set -x; local return_value=0; while true; do trace' alias end_function='break; done; [ $trace -ge $bash_trace ] && set -x; trace=$((trace+1))'

16 Answers
0
Mike Gleason 回答 3天 前

完善上面的答案,您可以获得像别名一样的 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; }
philo vivero 回复 3天 前

我更喜欢这个答案,因为它显示了遍历可能出现的参数数组。 $1 和 $2 只是特殊情况,这个答案也说明了这一点。

Henk Langeveld 回复 3天 前

改变mv "$1" "$1.bak"; cp "$2" "$1"进入mv "$1" "$1.bak" && cp "$2" "$1"不要丢失您的数据mv遇到麻烦(例如,文件系统已满)。

Kaushal Modi 回复 3天 前

“不要忘记右括号前的分号。”很多次这样!谢谢 :)

Bryan Chapel 回复 3天 前

第一个括号之后和最后一个括号之前的空格也是必需的。

Peter - Reinstate Monica 回复 3天 前

@HenkLangeveld虽然您的评论完全正确,但它表明您已经存在了一段时间-我不知道我上次遇到“设备上没有剩余空间”错误是什么时候;-)。 (虽然当我在厨房柜台上找不到空间时,我经常使用它!)这些天似乎不经常发生。

Henk Langeveld 回复 3天 前

@PeterA.Schneider 好吧,mvcp返回状态码。为什么有状态码而不使用它们?谁知道还有什么可能出错? Shell 编程仍然是系统编程。万无一失。

Peter - Reinstate Monica 回复 3天 前

@HenkLangeveld 哦,你说得对!我并不是要暗示人们应该忽略存在状态。 “没有空间”的东西只是唤起了记忆。 (这些天我面临的问题更加复杂,比如“当你在 cygwin 的 fstab 中启用 acl 处理时,它会搞砸 acls,以至于整个目录树变得无法被 Windows 程序访问……”。)

zyy 回复 3天 前

我在用tcsh我试过了csh脚本,我得到了mv: No match在采购时.cshrc文件。

0
Evan Langlois 回答 3天 前

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

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

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

joe 回复 3天 前

这个答案说明了一切。如果我从本页底部阅读,我会节省一些时间。

Fuzzy Logic 回复 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

Tom Hale 回复 3天 前

你真的应该引用$@支持带空格的文件名等。

Evan Langlois 回复 3天 前

这应该是一个一般性的解释,即您没有别名参数,而是使用函数。给定的函数只是对抗原始示例的示例。我不认为该人正在寻找通用垃圾功能。但是,为了提供更好的代码,我更新了答案。

Joe 回复 3天 前

@FuzzyLogic – 我花了几分钟试图解压你评论中的代码。将一些代码打包到无法正确格式化的注释中需要付出很多(有趣的)努力。我仍然没有弄清楚它到底做了什么,或者更重要的是,为什么它支持你的(正确的)断言。

Fuzzy Logic 回复 3天 前

@Joe除了让它作为单行代码运行并删除测试文件之外没有任何努力。如果你想看看发生了什么,只需注释掉rm -f ..最后的部分,它不会删除测试文件。它所做的是 file2 定义了一个别名和一个函数,它们都读取脚本路径的名称,然后从 file1 中包含 file2 并从那里调用别名和函数。您可以从输出中看到它们各自产生不同的结果,即使它们都在 file2 中定义并且具有相同的代码。原因是因为别名存储为字符串。

Dan L 回复 3天 前

“问错了”?假设OP知道确切地问什么,如果他/她问了,他们很可能一开始就不会问。这是一个提问的论坛,希望能解决好的问题,但我们不要在这里过分评判。

0
Tom Hale 回答 3天 前

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$0, 并且是必需的,这样用户给定的参数中的第一个就不会被消耗。示范:
    sh -c 'echo Consumed: "$0" Printing: "$@"' alcohol drunken babble
    Consumed: alcohol Printing: drunken babble
  • 单引号内的单引号是必需的。这是一个不使用双引号的示例:
    $ sh -c "echo Consumed: $0 Printing: $@" alcohol drunken babble
    Consumed: -bash Printing:

    这里是交互式 shell 的值$0$@被替换成双引号它被传递给sh.这是证据:

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

    单引号确保这些变量不会被交互式 shell 解释,而是按字面意思传递给sh -c.
    你可以使用双引号和\$@,但最佳做法是引用您的论点(因为它们可能包含空格),并且\"\$@\"看起来更丑,但可能会帮助您赢得一场混淆比赛,其中疲惫的头发是进入的先决条件。

sgtz 回复 3天 前

对于解决方案 1,请确保使用单引号,并避免在 $@ 周围出现 \”。如果需要,这是一种非常有用的技术。ty。

Hsehdar 回复 3天 前

解决方案 1 对我有用,用我想要的每个服务器的参数包装 rdesktop-vrdp。

AbstractVoid 回复 3天 前

最后接受参数正是我需要用简单的“gc {branchname}”替换“git checkout {branchname}”。在 .bash_profile 我只需要添加alias gc='git checkout'

Tom Hale 回复 3天 前

@sgtz 为什么要删除周围的引号$@?例如,如果您有包含空格的文件,则它们是必需的。

BUFU 回复 3天 前

@TomHale这有点语言学,但是如果有人真的反对使用函数(无论可能有什么原因),解决方案1并不是真的有用,因为您直接承认自己只是将函数的定义转移到执行时间别名。其次,“bash 别名确实接受参数,但仅在末尾”更具语义:别名是按定义用字符串替换单词。他们不需要接受参数,但是通过替换执行的命令可以。 alias 是字符替换,仅此而已。不?

Alexis Wilke 回复 3天 前

完美的。我有一个屁股负荷/dev/loop出现在我的标准中df命令。我现在的别名是:alias df='f() { df "$@" | grep -v loop; }; f'它会从我的默认输出中自动隐藏这些循环设备。拥有与其他解决方案一样的功能将使我使用与默认 Unix 命令不同的名称…

BUFU 回复 3天 前

@alexis-wilke 为什么需要使用不同的名称?如果这是您希望所有 df 使用的行为,只需调用您的函数 df…

Alexis Wilke 回复 3天 前

@BUFU,有几次我想看看循环。但现在我知道我可以简单地使用完整路径(/usr/bin/df) 并且完全忽略了函数/别名。所以无论哪种方式都有效。

BUFU 回复 3天 前

@AlexisWilke 准确。甚至更简单,您可以简单地使用\df

Timo 回复 3天 前

汤姆,我喜欢你:“知识是危险的……”

0
Ken Tran 回答 3天 前

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

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

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

xaxxon 回复 3天 前

这很聪明,但除了问题确实指定创建别名之外,是否有任何理由不只是使函数与最初的别名具有相同的名称?

Ken Tran 回复 3天 前

@xaxxon 不是真的,但这是我知道使用别名而不是函数的最简单方法。

Shital Shah 回复 3天 前

这不起作用。在 Ubuntu 18 上,我收到错误bash: syntax error near unexpected token '{mkdir'.

Jetchisel 回复 3天 前

缺少尾随;函数内部_mkcd很可能错误来自于此。

hesham_EE 回复 3天 前

前后留空间{}

Charles Duffy 回复 3天 前

我已经修复了语法问题。回复:删除function前缀,见讨论wiki.bash-hackers.org/scripting/obsolete.也就是说,我不知道为什么有人会这样做而不是只定义一个函数而不使用任何别名。

faramir 回复 3天 前

您可以通过以下方式转义/禁用别名`, eg.\ls, but if you changels` 成为一个函数(例如。ls() { /bin/ls -l; }) 然后\ls仍在使用函数代替 justls.

BUFU 回复 3天 前

@faramir 你可以使用command.你甚至可以使用别名alias c = 'command'然后简单地使用c ls.

Ken Tran 回复 3天 前

@ShitalShah,您在 ‘{‘ 和 ‘m’ 之间缺少一个空格

0
Amine Hajyoussef 回答 3天 前

另一种解决方案是使用标记,我最近创建的一个工具,它允许您“标记”命令模板并轻松地将光标放在命令占位符上:
命令行标记
我发现大多数时候,我都在使用 shell 函数,所以我不必在命令行中一次又一次地编写常用命令。在这个用例中使用函数的问题是在我的命令词汇表中添加新术语,并且必须记住实际命令中的函数参数指的是什么。标记的目标是消除这种精神负担。