做一个Bash别名,需要一个参数?
我曾经使用CShell( csh ),它可以让你使一个参数的别名。 这个符号是类似的
alias junk="mv \\!* ~/.Trash"
在Bash中,这似乎不起作用。 鉴于Bash有许多有用的function,我会假设这一个已经实施,但我想知道如何。
Bash别名不直接接受参数。 你将不得不创build一个函数和别名。
alias
不接受参数,但可以像一个别名一样调用一个函数。 例如:
myfunction() { #do things with parameters like $1 such as mv "$1" "$1.bak" cp "$2" "$1" } myFunction xyz #calls `myfunction`
顺便说一下,在.bashrc
和其他文件中定义的Bash函数可以在您的shell中作为命令使用。 所以,比如你可以像这样调用早期的函数
$ myfunction original.conf my.conf
细化上面的答案,你可以得到一行语法,就像你可以用别名一样,这对于shell或者.bashrc文件中的ad-hoc定义更方便:
bash$ myfunction() { mv "$1" "$1.bak"; cp "$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; }
这个问题只是被问到错误。 您不会创build一个带参数的别名,因为alias
只会为已存在的内容添加第二个名称。 OP需要的function
是创build新function的function
命令。 您不需要别名function,因为该function已经有一个名称。
我想你想要这样的东西:
function trash() { mv "$@" ~/.Trash; }
而已! 你可以使用参数$ 1,$ 2,$ 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
仅在最后接受的parameter passing给将它们插入中间然后执行命令的包装器。
解决scheme1
如果你真的反对使用一个函数本身,你可以使用:
$ alias wrap_args='f(){ echo before "$@" after; unset -ff; }; f' $ wrap_args xyz before xyz after
如果您只想要第一个参数,您可以用$1
replace$@
。
说明1
这创build了一个临时函数f
,它传递了参数(注意f
在最后被调用)。 unset -f
在别名被执行时删除函数定义,所以它不会在后面挂起。
解决scheme2
你也可以使用一个子shell:
$ alias wrap_args='sh -c '\''echo before "$@" after'\'' _'
说明2
别名构build一个命令,如:
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
之前被replace成双引号。 这是certificate:echo "Consumed: $0 Printing: $@" Consumed: -bash Printing:
单引号确保这些variables不被交互式shell解释,而是直接传递给
sh -c
。你可以使用双引号和
\$@
,但是最好的做法是引用你的论点(因为它们可能包含空格),而\"\$@\"
看起来更丑陋,但是可能会帮助你赢得模糊的头发是进入的先决条件。
另一种解决scheme是使用标记 ,我最近创build的一个工具可以让你“命令”模板,并轻松地将光标放在命令占位符处:
我发现大多数时候,我正在使用shell函数,所以我不必一次又一次地在命令行中编写经常使用的命令。 在这个用例中使用函数的问题是为我的命令词汇表添加新的术语,并且必须记住实际命令中参数引用的function。 标记的目标是消除这种精神负担。
以下是我在~/.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 }
参考文献:
- 从string修剪空白: 如何从Bashvariables修剪空白?
- 实际换行符: https : //stackoverflow.com/a/4296147/2736496
- 别名w / param: https : //stackoverflow.com/a/7131683/2736496 (在这个问题的另一个答案)
- HISTIGNORE: https ://superuser.com/questions/232885/can-you-share-wisdom-on-using-histignore-in-bash
- 是/否提示: https : //stackoverflow.com/a/3232082/2736496
- 删除历史logging中的所有匹配项: https : //unix.stackexchange.com/questions/57924/how-to-delete-commands-in-history-matching-a-given-string
- string为空/空: http : //tldp.org/LDP/abs/html/comparison-ops.html
注意:如果这个想法不明显,那么除别名之外别名都使用别名是一个坏主意,第一个是“别名中的函数”,第二个是“难以读取的redirect/源”。 此外,还有一些缺陷(我认为这些缺陷很明显,但是如果您感到困惑,我并不是说它们实际上可以在任何地方使用!)
………………………………………….. ………………………………………….. ……………………………………..
我以前回答过这个问题,过去一直如此:
alias foo='__foo() { unset -f $0; echo "arg1 for foo=$1"; }; __foo()'
这很好,除非你一起避免使用函数。 在这种情况下,您可以利用bash的巨大能力redirect文本:
alias bar='cat <<< '\''echo arg1 for bar=$1'\'' | source /dev/stdin'
他们都是一样的长度给或几个字符。
真正的踢球者是时间差,顶部是“function方法”,底部是“redirect源”方法。 为了certificate这个理论,时机本身就说明了这一点:
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个结果的底部,随机间隔完成。 看来,function创build/销毁需要比redirect更多的时间。 希望这将有助于未来的访问者这个问题(不想保留自己)。
为了参数,你应该使用函数!
但是,在创build别名时,而不是在别名的执行过程中,$ @被解释,并且转义$也不起作用。 我如何解决这个问题?
你需要使用shell函数而不是别名来摆脱这个问题。 你可以如下定义foo:
function foo() { /path/to/command "$@" ;}
要么
foo() { /path/to/command "$@" ;}
最后,使用下面的语法调用你的foo():
foo arg1 arg2 argN
确保你添加你的foo()到~/.bash_profile
或~/.zshrc
文件。
在你的情况下,这将工作
function trash() { mv $@ ~/.Trash; }
如果你正在寻找一种通用的方法来将所有的参数应用于一个函数,而不是一个或两个或其他硬编码的数量,你可以这样做:
#!/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
。
我喜欢这个基于$@
的解决scheme,因为它适用于任何数量的参数。
有一个合理的技术理由,希望得到一个通用的解决scheme来解决bash别名没有一个机制来重定位任意论点的问题。 一个原因是,如果您希望执行的命令会受到执行function导致的环境变化的不利影响。 在所有其他情况下,都应该使用函数。
最近迫使我试图解决这个问题的是我想创build一些缩写命令来打印variables和函数的定义。 所以我为此写了一些function。 但是,有一些variables是(或可能)被函数调用本身改变的。 其中有:
FUNCNAME BASH_SOURCE BASH_LINENO BASH_ARGC BASH_ARGV
我一直使用(在函数中)打印variablesdefns的基本命令。 在set命令输出的forms是:
sv () { set | grep --color=never -- "^$1=.*"; }
例如:
> V=voodoo sv V V=voodoo
问题:这不会像当前上下文那样打印上面提到的variables的定义,例如,如果在交互式shell提示下(或者不在任何函数调用中),则不定义FUNCNAME。 但是我的function告诉我错误的信息:
> sv FUNCNAME FUNCNAME=([0]="sv")
我提出的一个解决scheme已经被其他人在这个主题上的其他职位提到过。 对于这个特定的命令来打印variablesdefns。,而只需要一个参数,我做到了这一点:
alias asv='(grep -- "^$(cat -)=.*" <(set)) <<<'
其中给出正确的输出(无)和结果状态(假):
> asv FUNCNAME > echo $? 1
但是,我仍然不得不寻找一个适用于任意数量参数的解决scheme。
将任意parameter passing给Bash别名命令的一般解决scheme:
# (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
这个解决scheme的一个好处是,在编写trapped命令时,用于处理命令的位置参数(参数)的所有特殊技巧都将起作用。 唯一的区别是必须使用数组语法。
例如,
如果你想要“$ @”,使用“$ {CMD_ARGV [@]}”。
如果你想要“$#”,使用“$ {#CMD_ARGV [@]}”。
等等。
function确实几乎总是答案,已经充分的贡献,并从手册页的这个引用证实: “几乎所有的目的,别名被shell函数取代。
为了完整性,并且因为这可能是有用的(稍微更轻量级的语法),可以注意到,当参数跟随别名时,它们仍然可以被使用(尽pipe这不会解决OP的要求)。 这可能是最简单的例子:
alias ssh_disc='ssh -O stop'
允许我键入smth,如ssh_disc myhost
,它按照预期扩展,如下所示: ssh -O stop myhost
这对需要复杂参数的命令很有用(我的记忆不再是它所使用的东西)
但是,在.bashrc
文件中创build别名时不需要使用函数。 例如
# create an alias that takes port-number by the user alias serve="python -m SimpleHTTPServer $1"
在.bashrc文件中进行更改后,确保input以下命令。
~$ source .bashrc
你应该可以像这样使用它
~$ serve 8998