在 Bash 中,何时使用别名、何时使用脚本以及何时编写函数?

在 Bash 中,何时使用别名、何时使用脚本以及何时编写函数?

没有人需要 10 年的时间才能问出这个问题,就像我一样。如果我刚开始使用 Linux,我想知道:何时使用别名、何时使用脚本以及何时编写函数?

就别名而言,我将别名用于不带参数的非常简单的操作。

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

这似乎是显而易见的。但有些人这样做:

alias command="bash bashscriptname"

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

有充分的理由这样做吗?我没有遇到过这种情况。如果存在会产生影响的边缘情况,请在下面回答。

这就是我将一些东西放入我的 PATH 中的地方chmod +x,这是经过多年 Linux 反复试验后出现的另一件事。

这让我想到了下一个主题。例如,我通过在我的( ) 中添加.scripts/一行,将主目录中的隐藏文件夹 ( ) 添加到我的 PATH 中,因此其中的任何可执行文件都会自动完成。.bashrcPATH=$PATH:/home/username/.scripts/

我真的不需要那个,是吗?我只会将其用于非 shell 的语言,例如 Python。如果是 shell,我可以在其中编写一个函数.bashrc

funcname () {
  somecommand -someARGS "$@"
}

我错过了什么吗?
关于何时使用别名、何时编写脚本以及何时编写函数,您会告诉 Linux 新手什么?

如果不明显,我假设回答这个问题的人会利用所有三个选项。如果您只使用这三者中的一两个(别名、脚本、函数),那么这个问题并不是真正针对您的。

答案1

其他答案根据个人品味提供了一些软性的一般准则,但忽略了许多相关的内容事实在决定脚本、函数或别名时应该考虑这一点。

别名和函数 ¹

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

脚本

  • Shell 不会将脚本保存在内存中。相反,每次需要脚本时都会从存储脚本的文件中读取脚本。如果通过搜索找到脚本$PATH,许多 shell 会将其路径名的哈希值存储在内存中,以节省将来$PATH查找的时间,但这就是脚本不使用时的内存占用范围。
  • 与函数和别名相比,脚本的调用方式更多。它们可以作为参数传递给解释器,例如sh script,或直接作为可执行文件调用,在这种情况下,将调用 shebang 行(例如#!/bin/sh)中的解释器来运行它。在这两种情况下,脚本都由单独的解释器进程运行,其自己的环境与 shell 的环境分开,脚本不能以任何方式影响 shell 的环境。事实上,解释器 shell 甚至不必与调用 shell 匹配。因为以这种方式调用的脚本看起来就像任何普通的可执行文件一样,所以它们可以被任何程序使用。

    最后,当前 shell 可以使用 读取并运行脚本.,或者在某些 shell 中使用source。在这种情况下,脚本的行为很像按需读取的函数,而不是持续保存在内存中。

应用

鉴于上述情况,我们可以就是否将某些内容制作为脚本或函数/别名提出一些一般准则。

  • 除了 shell 之外,其他程序是否需要能够使用它? 如果是这样,它必须是一个脚本。

  • 您只想从交互式 shell 中使用它吗? 通常希望在交互运行时更改许多命令的默认行为而不影响外部命令/脚本。对于这种情况,请使用 shell 的“interactive-mode-only”rc 文件中设置的别名/函数(对于bash此为.bashrc)。

  • 需要改变shell的环境吗? 函数/别名或源脚本都是可能的选择。

  • 这是你经常使用的东西吗? 将其保存在内存中可能会更有效,因此如果可能的话,将其设为函数/别名。

  • 相反,它是你很少使用的东西吗? 在这种情况下,当你不需要它时,让它占用内存是没有意义的,所以将它作为一个脚本。


1 虽然函数和别名有一些重要的区别,但它们被分组在一起,因为函数可以完成别名可以做的所有事情。别名不能有局部变量,也不能处理参数,并且对于超过一行的任何内容都不方便。

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

答案2

实际上,别名(一般来说)不应只更改命令的默认选项。它只不过是命令名称上的简单文本替换。它不能对参数执行任何操作,只能将它们传递给它实际运行的命令。因此,如果您只需要在单个命令的前面添加一个参数,则可以使用别名。常见的例子有

# 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根据它是否在管道中更改 的默认行为:

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

这是一个完美的函数示例,因为它对于别名来说太复杂了(根据条件需要不同的默认值),但它不是非交互式脚本中需要的东西。

如果您获得太多函数或函数太大,请将它们放入隐藏目录中的单独文件中,并将它们来源到您的~/.bashrc

if [ -d ~/.bash_functions ]; then
    for file in ~/.bash_functions/*; do
        . "$file"
    done
fi

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

答案3

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

  • 首先我尝试创建一个别名,因为这是最简单的。
  • 如果事情太复杂而无法用一行来表达,我会尝试将其变成一个函数。
  • 当函数开始增长到超过十几行时,我将其放入脚本中。

确实没有什么可以限制你做某事作品

答案4

什么时候写剧本...

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

何时编写函数...

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

什么时候写别名...

在库脚本等脚本中,有时需要函数的别名,例如当函数被重命名但需要向后兼容性时。这可以通过创建一个具有旧名称的简单函数来完成,该函数将其所有参数传递给新函数......

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

可以在以下位置找到该alias命令和其他 shell的全面描述:bash维基百科/别名(命令)

相关内容