我花了将近10年的Linux使用时间问这个问题。这都是反复试验和随机的深夜互联网冲浪。

但是人们不需要10年的时间。如果我刚开始使用Linux,那么我想知道:什么时候使用别名,何时编写脚本以及何时编写函数?

在涉及别名的地方,我使用别名非常简单不带参数的操作。

alias houston='cd /home/username/.scripts/'


这似乎很明显。但是有些人这样做:

alias command="bash bashscriptname"


(并将其添加到.bashrc文件中)

是否有充分的理由这样做?我真的很努力,但是我真的无法想到在任何情况下都需要这样做。因此,如果有一个极端的情况会有所作为,请在下面回答。

因为这是我只需要在PATH中放入某些内容并进行格式化的原因,这是几年之后的另一件事Linux的反复试验。

这带我进入下一个话题。例如,我在主目录中添加了一个隐藏文件夹(chmod +x)到我的PATH中,只需在我的.scripts/.bashrc)中添加一行即可,因此其中的任何可执行文件都会自动完成。

如果我需要。

但是我真的不需要那个吗?我只会将其用于非外壳的语言,例如Python。

如果是外壳,我可以在相同的PATH=$PATH:/home/username/.scripts/内编写一个函数:

正如我所说,我通过反复试验发现了很多。我只有在计算机死机时才真正看到功能的美,而当我周围的人不使用计算机时,他们被迫使用它们。

我没有将整个脚本目录从一个计算机移动到另一个计算机,而是最终只用我自己的其他人替换了.bashrc,因为他们甚至从未进行任何修改。

但是我想念什么吗?

那么,您将如何告诉Linux新手何时使用别名,何时编写脚本以及何时编写函数?

如果不是很明显,我假设回答这个问题的人将使用这三个选项。如果您仅使用别名,或仅使用脚本,或仅使用函数,或者仅使用别名和脚本,或者仅使用别名和函数,或者脚本和函数,则此问题并非针对您。

评论

bash函数与脚本的可能重复

+1用于明确说明此问题不针对的{别名,脚本,函数}的所有子集。 +1代表孩子般的信念,您可以忽略空子集。

尽管该问题专门询问有关bash的问题,但请注意,较旧的Bourne外壳具有“别名”功能,但没有功能。如果您担心兼容性,可能会有所不同。

如果.bashrc真的是最好的地方,或者至少是一个可靠的地方,对此呢?在Linux中有很多方法可以做相同的事情,但是我很欣赏,但是在所有条件都相同的情况下,我更喜欢以最常见的方式做事情。

#1 楼

别名(通常)实际上不应做得比更改命令的默认选项更多。无非就是命令名称上的简单文本替换。它不能对参数做任何事情,而是将它们传递给它实际运行的命令。因此,如果您只需要在单个命令的开头添加参数,则别名将起作用。常见的示例是

# Make ls output in color by default.
alias ls="ls --color=auto"
# make mv ask before overwriting a file by default
alias mv="mv -i"


当您需要执行比别名更复杂的操作但不能单独使用时,应使用函数。例如,针对我问过的有关更改grep的默认行为(取决于它是否在管道中)的问题,请回答以下问题:因为它对于别名来说太复杂了(根据条件要求不同的默认值),但是在非交互式脚本中并不是必需的。

如果功能太多或功能太大,请将它们放在隐藏目录中的单独文件中,并将它们提供到您的~/.bashrc中:

grep() { 
    if [[ -t 1 ]]; then 
        command grep -n "$@"
    else 
        command grep "$@"
    fi
}

< br脚本应该独立存在。它应该具有可重复使用或用于多个目的的价值。

评论


记住这一点也很重要-除非来自。或源–脚本由单独的bash进程执行,并具有自己的环境。因此,任何修改shell环境的内容(例如函数,变量等)都不会持久存在于运行脚本的shell环境中。

–威尔登(Will Vousden)
2015年5月5日在11:02



#2 楼

其他答案根据个人喜好提供了一些软性的通用准则,但忽略了在决定脚本,函数或别名时应该考虑的许多相关事实。

别名和函数¹


别名和函数的全部内容存储在外壳程序的内存中。
这样的自然结果是别名和函数只能由当前shell使用,而不能由您可以从shell调用的任何其他程序使用,例如文本编辑器,脚本或同一shell的子实例。
别名和函数由当前Shell执行,即它们在内部Shell中运行并影响Shell的当前环境。²运行别名或函数不需要单独的过程。

脚本


Shell不将脚本保留在内存中。而是在每次需要时从存储它们的文件中读取脚本。如果通过$PATH搜索找到了该脚本,那么许多shell会将其路径名的哈希存储在内存中,以节省以后的$PATH查找时间,但这就是不使用该脚本时所​​占用的内存的范围。

与功能和别名相比,可以通过更多方式调用脚本。它们可以作为参数传递给诸如sh script之类的解释器,也可以直接作为可执行文件调用,在这种情况下,将调用shebang行中的解释器(例如#!/bin/sh)来运行它。在这两种情况下,脚本都是由一个单独的解释器进程运行的,其解释器环境与您的外壳环境不同,该脚本的环境不能以任何方式影响。实际上,解释器外壳甚至不必匹配调用外壳。因为以这种方式调用的脚本看起来像任何普通的可执行文件,所以它们可以被任何程序使用。

最后,当前外壳程序可以使用.或在某些外壳程序source中读取并运行脚本。在这种情况下,脚本的行为就像是按需读取的函数,而不是始终保存在内存中。


应用程序

鉴于以上所述,我们可以为是否制作脚本或函数/别名提供一些通用指南。


除了shell之外,其他程序是否需要使用它?如果是这样,它必须是一个脚本。
您只希望它可以从交互式shell中获得吗?通常希望在交互运行时更改许多命令的默认行为而不影响外部命令/脚本。对于这种情况,请在外壳程序的“仅交互式模式” rc文件中使用别名/函数集(对于bash,这是.bashrc)。
是否需要更改外壳程序的环境?函数/别名或源脚本都是可能的选择。
您经常使用它吗?将其保留在内存中可能更有效,因此,如果可能的话,使其成为函数/别名。
相反,它很少使用吗?在这种情况下,不需要它时会占用内存,因此将其设置为脚本。


¹虽然函数和别名有一些重要的区别,但是它们被分组因为函数可以做别名可以做的所有事情。别名不能具有局部变量,也不能处理参数,并且它们对于一行以上的内容都不方便。

²Unix系统中的每个正在运行的进程都具有由一堆variable=value对组成的环境。通常包含全局配置设置,例如LANG(用于默认语言环境)和PATH(用于指定可执行搜索路径)。

评论


那函数和别名呢?函数严格比别名强大吗?

– Ciro Santilli郝海东冠状病六四事件法轮功
2014年3月16日在18:13

如果仅仅是因为系统允许或可以处理的两个不同概念的嵌套层数太多,则此答案不一定正确。还有<< << here文档,POSIX为此为其指定了一个iohere系统调用,该调用使它成为一个内联文件,因此如果包含脚本,它可以模糊两者之间的行。因为函数可以定义文件并且函数可以执行脚本,所以您的事实可能有很多变体。

–mikeserv
2014年4月30日在17:06

@mikeserv-也许您应该写一篇关于这些事实和变化的博客文章。但这将属于高级bash脚本编制类别,而不是此答案的基本准则类别。

–杰西·奇斯霍尔姆(Jesse Chisholm)
2015年11月10日在21:44

值得注意的是:如果两个(或更多)脚本需要共享一些代码,则最好将这些代码放入一个函数中,该函数本身位于这两个脚本导入/源出的第三文件中。

– kbolino
16-09-21在19:40

另一个添加到该问题列表中的项目:您是否需要即时更改命令中的功能?对脚本的更改将反映在所有会话中,而函数和别名必须在每个会话的基础上重新加载或重新定义。

–Stratus3D
17年4月14日在21:02

#3 楼

我认为这取决于每个人的口味。对我来说,逻辑是这样的:


首先,我尝试创建一个别名,因为它是最简单的。
如果事情太复杂而不能放在一行中,我尝试使它成为一个函数。
当函数开始扩展到超过十行时,我将其放入脚本中。

确实没有任何限制您执行有效的操作。

评论


我经常跳过功能选项,并立即编写脚本。但我同意,这在一定程度上取决于口味

–伯恩哈德
2012年2月5日的15:00

如果您需要在多个脚本中使用某个函数,则该函数开始变得有意义。

–指甲
2012-2-10 21:45

...或者如果您需要副作用来更改当前的shell。

–格伦·杰克曼
15年7月27日在16:30

#4 楼

至少部分是个人喜好问题。另一方面,还有一些明显的功能区别:


别名:仅适用于简单的文本替换,无参数/参数
功能:易于编写/使用,完整的外壳脚本功能,仅在bash内可用
脚本:或多或少类似于函数,但在bash之外也可用(可调用)

看看shell脚本我最近几年做了已经或多或少停止了编写别名(因为它们都会随着时间的流逝而增长为函数),并且仅在需要在非bash环境中也可以使用它们时才执行脚本。实际上没有任何理由这样做。即使alias command="bash bashscriptname"不在$ PATH中,只需一个简单的bashscriptname就足够了。

评论


在别名command =“ bash bashscriptname”中,脚本不一定必须是可执行的。在别名c = / path / to / script中必须这样做。

–马丁-マーチン
18年7月24日在16:29

函数“仅在Bash内部可用”根本不是真的。如果您要说它们是仅Bash的功能,那简直是错误的(Bourne shell和每个兼容的派生都有它们);而且,如果您要说它们是交互式外壳程序的功能,那也不是很准确(尽管在交互式外壳程序启动时被加载的文件中定义的别名,变量和函数显然不会被非交互式外壳程序加载)。

–tripleee
18年8月19日在8:10

@tripleee含义更像是“您无法执行exec()shell函数” :-)

– nohillside
18-09-17在8:50

#5 楼

何时编写脚本...


脚本将软件组件(也称为工具,命令,过程,可执行文件,程序)组装为更复杂的组件,这些组件本身也可以组装为更多组件复杂的组件。
脚本通常可以执行,因此可以按名称调用。当被调用时,将产生一个新的子进程供脚本运行。任何exported变量和/或函数的副本均按值传递给脚本。对这些变量的更改不会传播回父脚本。
脚本也可以被加载(源),就像它们是调用脚本的一部分一样。这类似于某些其他语言称为“导入”或“包含”的内容。来源时,它们在现有过程中执行。

何时编写函数...


函数实际上是预加载的Shell脚本。它们的性能比调用单独的脚本好一些,但前提是必须从机械磁盘读取该脚本。如今,闪存驱动器,SSD和Linux在未使用的RAM中进行常规缓存的激增使这种改进几乎无法衡量。
函数是bash实现模块化,封装和重用的主要手段。它们提高了脚本的清晰度,可靠性和可维护性。
调用函数的语法规则与调用可执行文件的语法规则相同。将调用与可执行文件同名的函数,而不是可执行文件。
函数是它们所在脚本的本地函数。
函数可以导出(按值复制),因此可以在内部使用称为脚本。因此,函数只会传播到子进程,而不会传播到父进程。
函数会创建可重用的命令,这些命令通常组装到库(仅包含函数定义的脚本)中,并由其他脚本提供。

何时写一个别名...


Bash别名在if / then和for / do循环之类的shell脚本结构中不起作用,因此应仅用作交互式命令行上的键盘快捷键。因此,所有别名定义通常都驻留在适当的登录脚本中,例如~/.profile~/.bashrc。在库脚本等脚本中,有时需要函数的别名,例如重命名函数但向后兼容时是必须的。这可以通过使用旧名称创建一个简单函数并将其所有参数传递给
new函数...

# A bash in-script 'alias'
function oldFunction () { newFunction "$@"; }


的全面描述来实现。可在Wikipedia / Alias(命令)中找到alias中的bash命令和其他外壳程序

评论


在我尝试过的任何shell中,“别名在if / then和for / do循环之类的shell脚本结构中均不起作用”。

– Mikel
5月2日16:02

问题只是关于bash。可以通过设置shell选项shopt -s expand_aliases来限制别名的使用,但是使用函数会更健壮。我已经添加了指向Wikipedia文章的链接,这可能会有所帮助。

– DocSalvager
5月3日5:52

啊! expand_aliases已启用。感谢您的澄清!

– Mikel
5月3日下午16:40

#6 楼

以下是有关别名和函数的一些其他要点:



同名别名和函数可以共存
首先查找别名空间(请参见第一个示例) )
不能在子shell或非交互环境中设置(取消)别名(请参见第二个示例)

例如:

alias f='echo Alias'; f             # prints "Alias"
function f { echo 'Function'; }; f  # prints "Alias"
unalias f; f                        # prints "Function"


如我们所见,别名和函数有单独的命名空间;使用declare -A -p BASH_ALIASESdeclare -f f可以找到更多详细信息,它们会打印它们的定义(都存储在内存中)。

示例显示别名的局限性:

alias a='echo Alias'
a        # OK: prints "Alias"
eval a;  # OK: prints "Alias"
( alias a="Nested"; a );  # prints "Alias" (not "Nested")
( unalias a; a );         # prints "Alias"
bash -c "alias aa='Another Alias'; aa"  # ERROR: bash: aa: command not found


正如我们看到的,别名与函数不同,是不可嵌套的。此外,它们的用法仅限于交互式会话。

最后,请注意,您可以通过声明一个函数立即调用它来对别名进行任意计算,例如:

alias a_complex_thing='f() { do_stuff_in_function; } f'


在Git别名的情况下已经广泛使用。这样做比声明一个函数的好处在于,别名不能简单地通过源代码(或使用.)一个恰巧声明一个同名函数的脚本来覆盖。

#7 楼

我不敢相信的另一件事是:函数在调用过程的上下文中执行,而脚本派生一个新的shell。

这对于性能很重要-功能更快,因为它没有fork()exec()。在正常情况下,差异很小,但是如果您调试的是内存不足且页面崩溃的系统,则可能会有很大的不同。

此外,如果要修改自己的当前的shell环境中,应该使用一个函数。例如,一个函数可以更改当前shell的命令查找$PATH,但脚本不能更改,因为它在$PATH的fork / exec副本上运行。

评论


这种向孩子传播功能的方式是如何工作的?

– HappyFace
18/12/16在21:27

@HappyFace在Bash中,您可以导出-f一个函数,尽管此函数的确切内部功能有些晦涩。我相信这不能移植到传统的Bourne外壳上。

–tripleee
19年8月5日在16:53

#8 楼

脚本和别名与脚本和功能不是互斥的。您可以并且确实将别名和函数存储在脚本中。

脚本只是可以持久化的代码。您希望将来使用的有用功能和别名存储在脚本中。但是,脚本通常是一个以上功能的集合。

由于未对别名进行参数化,因此它们的作用非常有限。通常定义一些默认参数。

函数是一个单独的代码单元,是几行代码的明确定义的概念,这些行不能分成较小的有用部分;一个可以直接重用或由其他功能重用的功能。

#9 楼

如果它非常快,请使其成为别名或函数。

如果应在首选shell之外使用,则使其成为脚本。1

如果需要参数,使其成为函数或脚本。

如果需要包含特殊字符,则使其成为别名或脚本。2

如果需要与sudo一起使用,使其成为别名或脚本。3

如果要在不注销和登录的情况下轻松进行更改,则脚本会更容易。4

脚注

1或将其命名为别名,将其放在~/.env中并设置export ENV="$HOME/.env",但使其可移植地工作很复杂。

2函数名称必须是标识符,因此必须以字母开头,并且只能包含字母,数字和下划线。例如,我有一个别名alias +='pushd +1'

3并添加别名alias sudo='sudo '。同其他将命令作为第一个参数的命令,例如stracegdb等。

4另请参见:fpath。当然,您也可以执行source ~/.bashrc或类似操作,但这通常还会带来其他副作用。

评论


我不知道您可以在bash中使用别名+。有趣的是,经过测试,我发现在bash中可以使+成为别名,但不能使函数成为函数,正如您所说,但是zsh是相反的-+可以使函数而不是别名。

–凯文
2012年5月9日18:42

在zsh中,您必须编写别名-+ =“此处有一些命令”。

– Mikel
2012年5月10日下午1:07

不知何故,我认为别名+是可移植的。请参阅关于别名的POSIX规范

– jw013
2012年8月23日在18:08



支持覆盖sudo用法。关于脚注4,我将别名存储在〜/ .bash_aliases中,将函数定义存储在〜/ .bash_functions中,因此我可以轻松地获取它们的资源(没有任何副作用的危险)。

– Anthony Geoghegan
15年11月19日在11:15

#10 楼

只需添加一些注意事项:


sudo只能使用单独的脚本(例如,如果需要编辑系统文件),例如:


sudo v /etc/rc.conf  #where v runs vim in a new terminal window;





只有别名或函数才能替换相同名称的系统命令(假设您将脚本目录添加到PATH的末尾,我认为这是为了安全起见,在意外或恶意创建名称与系统命令相同的脚本时),例如:


alias ls='ls --color=auto'  #enable colored output;



<别名和函数需要较少的内存和时间来执行,但是要花费一些时间来加载(因为shell必须在显示提示之前先对它们进行解释)。如果您定期运行新的Shell进程,请考虑到这一点,例如:


# pressing key to open new terminal
# waiting for a few seconds before shell prompt finally appears.



除此之外,还可以使用最简单的形式可能,即首先考虑别名,然后考虑功能,然后考虑脚本。

评论


别名也可以与sudo一起使用。但是首先您需要别名sudo ='sudo'。

– Mikel
2012年5月9日15:32

的确,在fork + exec的过程中执行脚本会瞬间消耗更多的内存,但是将大量代码加载到当前shell实例的内存中会使它继续消耗更多的内存,通常会存储仅被很少使用。

–tripleee
18年8月19日在8:17

#11 楼

我的经验法则是:


别名-一个命令,没有参数
功能-一个命令,一些参数
脚本-几个命令,没有参数


#12 楼

在多用户(或多sysamin)环境中,即使所有内容最终只是一个简短的“ exec something ....”包装程序,我都使用脚本来编写所有内容。效率比别名或函数低,但是几乎没有关系-并且只要它在路径中,脚本就总是可以工作。例如sudo或env,或者用户可能只是在使用一个与您不同的Shell-全部或可能破坏别名或功能。

如果您对性能敏感,则应将其作为特殊处理情况或更佳的情况,请考虑使用一种功能更强大的脚本语言进行重写的触发器。

如果我们谈论的是仅在其他脚本中使用的功能,那么您也可以考虑定义一个标准外壳程序,并编写一个可以为的函数库脚本。将我采购到所有其他脚本。

T

#13 楼

何时编写脚本

当您可能想要从外壳以外的工具运行命令时。

这包括vim(对我来说):编写了过滤器和其他程序作为脚本,我可以执行:%!my-filter之类的操作,以通过编辑器中的程序过滤文件。

如果my-filter是函数或别名,那是不可能的。

#14 楼

您最有可能想要使用别名的情况的例子。

我知道这是一篇过时的文章,但我想指出一种情况,在这种情况下,我几乎不得不使用别名组合使用脚本,我选择不使用功能。特定目录。
定义一些变量。
打印有关目录状态的消息。2


重点是,如果我只运行~/.bin/,我就不会没有定义那些变量,我根本就不会进入目录。我发现最好的解决方案是将此脚本添加到setup,然后将setup <project-name>添加到PATH或其他。

注意:这个任务而不是一个函数,不是因为它特别长,而是因为我可以编辑它,并且如果要刷新它的用法,不必在编辑后获取文件。
我遇到类似的情况创建了一个脚本来重新加载我的所有dotfiles。3





该脚本在我的dotfiles存储库中位于alias setup=". ~/.bin/setup"下。

关于脚本:我给这个脚本一个参数,它是我在高级中定义的项目的名称。然后,脚本知道根据某个~/.bashrc文件将其带到正确的目录。它定义的变量取自该目录中的makefile。该脚本随后运行.bin/csv,向我展示发生了什么。
该脚本也可以在我的dotfiles存储库中的ls -l下找到。



评论


嗯,似乎应该只是一个函数,而不是别名脚本组合。 (顺便说一句,环境被拼写为“环境”而不是“环境”。)

–通配符
16年4月25日在19:57

感谢您对错字的评论,我将在下一次提交时对其进行修复。至于使用函数而不是脚本-也许我将函数用于此特定任务,并删除这些别名。关键是,如果您不时编辑脚本和别名,有时使用脚本和别名非常容易。

– Doron Behar
16年4月25日在22:46

#15 楼

我只能评论自己对别名的个人使用情况(特别是如果您像我这样使用键盘时速度较慢。或者无法触摸打字。也像我一样)
我发现使用别名捕获很少使用的内容要容易得多但是复杂的命令,尤其是诸如alias dockpurge='docker rmi $(docker images | grep "^<none>" | awk "{print }")'的命令,我很少使用,但仍然有用。
甚至是我发现更容易混用的一些简单命令,例如alias mcpi='mvn clean compile package install'
因为我只是那懒惰:)
如果我要总结一下,别名的使用是一个人的常识和个人品味的个人结合(尽管有最佳实践)。