标签 Shell 下的文章

给你的 shell 脚本添加选项。

 title=

终端命令通常具有 选项或开关,用户可以使用它们来修改命令的执行方式。关于命令行界面的 POSIX 规范 中就对选项做出了规范,这也是最早的 UNIX 应用程序建立的一个由来已久的惯例,因此你在创建自己的命令时,最好知道如何将选项包含进 Bash 脚本 中。

与大多数语言一样,有若干种方法可以解决 Bash 中解析选项的问题。但直到今天,我最喜欢的方法仍然是我从 Patrick Volkerding 的 Slackware 构建脚本中学到的方法,当我第一次发现 Linux 并敢于冒险探索操作系统所附带的纯文本文件时,这些脚本就是我的 shell 脚本的引路人。

Bash 中的选项解析

在 Bash 中解析选项的策略是循环遍历所有传递给 shell 脚本的参数,确定它们是否是一个选项,然后转向下一个参数。重复这个过程,直到没有选项为止。

#!/bin/bash

while [ True ]; do
if [ "$1" = "--alpha" -o "$1" = "-a" ]; then
    ALPHA=1
    shift 1
else
    break
fi
done

echo $ALPHA

在这段代码中,我创建了一个 while 循环,它会一直进行循环操作,直到处理完所有参数。if 语句会试着将在第一个位置($1)中找到的参数与 --alpha-a 匹配。(此处的待匹配项是任意选项名称,并没有特殊意义。在实际的脚本中,你可以使用 --verbose-v 来触发详细输出)。

shift 关键字会使所有参数位移一位,这样位置 2($2)的参数移动到位置 1($1)。处理完所有参数后会触发 else 语句,进而中断 while 循环。

在脚本的末尾,$ALPHA 的值会输出到终端。

测试一下这个脚本:

$ bash ./test.sh --alpha
1
$ bash ./test.sh

$ bash ./test.sh -a
1

可以看到,选项被正确地检测到了。

在 Bash 中检测参数

但上面的脚本还有一个问题:多余的参数被忽略了。

$ bash ./test.sh --alpha foo
1
$

要想捕获非选项名的参数,可以将剩余的参数转储到 Bash 数组 中。

#!/bin/bash

while [ True ]; do
if [ "$1" = "--alpha" -o "$1" = "-a" ]; then
    ALPHA=1
    shift 1
else
    break
fi
done

echo $ALPHA

ARG=( "${@}" )
for i in ${ARG[@]}; do
    echo $i
done

测试一下新版的脚本:

$ bash ./test.sh --alpha foo
1
foo
$ bash ./test.sh foo

foo
$ bash ./test.sh --alpha foo bar
1
foo
bar

带参选项

有一些选项需要传入参数。比如,你可能希望允许用户设置诸如颜色或图形分辨率之类的属性,或者将应用程序指向自定义配置文件。

要在 Bash 中实现这一点,你仍然可以像使用布尔开关一样使用 shift 关键字,但参数需要位移两位而不是一位。

#!/bin/bash

while [ True ]; do
if [ "$1" = "--alpha" -o "$1" = "-a" ]; then
    ALPHA=1
    shift 1
elif [ "$1" = "--config" -o "$1" = "-c" ]; then
    CONFIG=$2
    shift 2
else
    break
fi
done

echo $ALPHA
echo $CONFIG

ARG=( "${@}" )

for i in ${ARG[@]}; do
    echo $i
done

在这段代码中,我添加了一个 elif 子句来将每个参数与 --config-c 进行比较。如果匹配,名为 CONFIG 的变量的值就设置为下一个参数的值(这就表示 --config 选项需要一个参数)。所有参数都位移两位:其中一位是跳过 --config-c,另一位是跳过其参数。与上节一样,循环重复直到没有匹配的参数。

下面是新版脚本的测试:

$ bash ./test.sh --config my.conf foo bar
my.conf
foo
bar
$ bash ./test.sh -a --config my.conf baz
1
my.conf
baz

Bash 让选项解析变得简单

还有一些其他方法也可以解析 Bash 中的选项。你可以替换使用 case 语句或 getopt 命令。无论使用什么方法,给你的用户提供选项都是应用程序的重要功能,而 Bash 让解析选项成为了一件简单的事。


via: https://opensource.com/article/21/8/option-parsing-bash

作者:Seth Kenlon 选题:lujun9972 译者:unigeorge 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

Shell 作为 Linux 中的第一语言,几乎每一个使用 Linux 的人都用到或用过 Shell,但绝大多数人都并不能掌握 Shell 编程的基本能力和技巧。

但如果你的工作涉及到与 Linux 进行操作和交互,Shell 编程对于你来说,都是一个必学的选项。今天,Linux 中国将为大家带来系列公益训练营之 《Shell 编程训练营》,以帮助大家掌握 Shell 编程的精髓和奥义。本次训练营得到了阿里云开发者社区的大力支持。

在本次训练营当中,你将会学习到体系化的 Linux Shell 编程内容,从 0 开始,掌握 Shell 编程的各项基本信息。

本次课程将会分三天进行授课:

  • DAY 1:Shell 的基本介绍、常用命令以及 Shell 的流程控制
  • DAY 2:Shell 中的数组应用、参数处理和函数的应用
  • DAY 3:Shell 的编码规范、调试 Debug 方案以及公共函数库

在授课过程中, 老师将以实战与理论相结合的方式,带着你学习基础知识,并掌握实战能力。

在本次训练营中,训练营导师将会带着你用三天的时间,从 Shell 的基础开始,一起开发一个 Shell 脚本,帮助你掌握 Shell 编程的精髓,提升你的 Shell 编程能力。

此外,通过 Linux 中国专属渠道报名并完成训练营学习的同学,还将获得一张由 Linux 中国颁发的训练营结业证书、全网可查询的电子证书。并有机会获得一本赠送的最新计算机书籍(共计 30 本,直播时抽取赠送)。

课程安排

授课时间:2021年 9 月 6 日 - 2021 年 9 月 8 日 21:00 - 22:00(为期 3 晚)

授课方式:直播授课,有回放

授课费用:免费授课,无任何费用

上课地点:报名后进入专属钉钉群

报名链接:https://developer.aliyun.com/learning/trainingcamp/linux/2?utm_content=g_1000289929

在 Linux 中有两种运行 shell 脚本的方法。你可以使用:

bash script.sh

或者,你可以像这样执行 shell 脚本:

./script.sh

这可能很简单,但没太多解释。不要担心,我将使用示例来进行必要的解释,以便你能理解为什么在运行一个 shell 脚本时要使用给定的特定语法格式。

我将使用这一行 shell 脚本来使需要解释的事情变地尽可能简单:

abhishek@itsfoss:~/Scripts$ cat hello.sh

echo "Hello World!"

方法 1:通过将文件作为参数传递给 shell 以运行 shell 脚本

第一种方法涉及将脚本文件的名称作为参数传递给 shell 。

考虑到 bash 是默认 shell,你可以像这样运行一个脚本:

bash hello.sh

你知道这种方法的优点吗?你的脚本不需要执行权限。对于简单的任务非常方便快速。

在 Linux 中运行一个 Shell 脚本

如果你还不熟悉,我建议你 阅读我的 Linux 文件权限详细指南

记住,将其作为参数传递的需要是一个 shell 脚本。一个 shell 脚本是由命令组成的。如果你使用一个普通的文本文件,它将会抱怨错误的命令。

运行一个文本文件为脚本

在这种方法中,你要明确地具体指定你想使用 bash 作为脚本的解释器

shell 只是一个程序,并且 bash 只是 Shell 的一种实现。还有其它的 shell 程序,像 ksh 、zsh 等等。如果你安装有其它的 shell ,你也可以使用它们来代替 bash 。

例如,我已安装了 zsh ,并使用它来运行相同的脚本:

使用 Zsh 来执行 Shell 脚本

方法 2:通过具体指定 shell 脚本的路径来执行脚本

另外一种运行一个 shell 脚本的方法是通过提供它的路径。但是要这样做之前,你的文件必须是可执行的。否则,当你尝试执行脚本时,你将会得到 “权限被拒绝” 的错误。

因此,你首先需要确保你的脚本有可执行权限。你可以 使用 chmod 命令 来给予你自己脚本的这种权限,像这样:

chmod u+x script.sh

使你的脚本是可执行之后,你只需输入文件的名称及其绝对路径或相对路径。大多数情况下,你都在同一个目录中,因此你可以像这样使用它:

./script.sh

如果你与你的脚本不在同一个目录中,你可以具体指定脚本的绝对路径或相对路径:

在其它的目录中运行 Shell 脚本

在脚本前的这个 ./ 是非常重要的(当你与脚本在同一个目录中)。

为什么当你在同一个目录下,却不能使用脚本名称?这是因为你的 Linux 系统会在 PATH 环境变量中指定的几个目录中查找可执行的文件来运行。

这里是我的系统的 PATH 环境变量的值:

abhishek@itsfoss:~$ echo $PATH
/home/abhishek/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin

这意味着在下面目录中具有可执行权限的任意文件都可以在系统的任何位置运行:

  • /home/abhishek/.local/bin
  • /usr/local/sbin
  • /usr/local/bin
  • /usr/sbin
  • /usr/bin
  • /sbin
  • /bin
  • /usr/games
  • /usr/local/games
  • /snap/bin

Linux 命令(像 lscat 等)的二进制文件或可执行文件都位于这些目录中的其中一个。这就是为什么你可以在你系统的任何位置通过使用命令的名称来运作这些命令的原因。看看,ls 命令就是位于 /usr/bin 目录中。

当你使用脚本而不具体指定其绝对路径或相对路径时,系统将不能在 PATH 环境变量中找到提及的脚本。

为什么大多数 shell 脚本在其头部包含 #! /bin/bash ?

记得我提过 shell 只是一个程序,并且有 shell 程序的不同实现。

当你使用 #! /bin/bash 时,你是具体指定 bash 作为解释器来运行脚本。如果你不这样做,并且以 ./script.sh 的方式运行一个脚本,它通常会在你正在运行的 shell 中运行。

有问题吗?可能会有。看看,大多数的 shell 语法是大多数种类的 shell 中通用的,但是有一些语法可能会有所不同。

例如,在 bash 和 zsh 中数组的行为是不同的。在 zsh 中,数组索引是从 1 开始的,而不是从 0 开始。

Bash Vs Zsh

使用 #! /bin/bash 来标识该脚本是 bash 脚本,并且应该使用 bash 作为脚本的解释器来运行,而不受在系统上正在使用的 shell 的影响。如果你使用 zsh 的特殊语法,你可以通过在脚本的第一行添加 #! /bin/zsh 的方式来标识其是 zsh 脚本。

#!/bin/bash 之间的空格是没有影响的。你也可以使用 #!/bin/bash

它有帮助吗?

我希望这篇文章能够增加你的 Linux 知识。如果你还有问题或建议,请留下评论。

专家用户可能依然会挑出我遗漏的东西。但这种初级题材的问题是,要找到信息的平衡点,避免细节过多或过少,并不容易。

如果你对学习 bash 脚本感兴趣,在我们专注于系统管理的网站 Linux Handbook 上,我们有一个 完整的 Bash 初学者系列 。如果你想要,你也可以 购买带有附加练习的电子书 ,以支持 Linux Handbook。


via: https://itsfoss.com/run-shell-script-linux/

作者:Abhishek Prakash 选题:lujun9972 译者:robsean 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

你想学习 shell 脚本编程吗?或者你想提升现有的 bash 知识?我收集了以下免费的资源来帮助你学习 shell 脚本编程。

(LCTT 译注:毫无疑问,这些都是英文的)

shell 是一个命令行解释器,它允许你输入命令并获得输出。当你在使用终端的时候,你就已经在看 shell 了。

是的,shell 是一个你可以和它进行交互的命令行界面,你可以通过它给操作系统某种指令。虽然有不同类型的 shell,但是 bash)(GNU Bourne-Again Shell)是在各 Linux 发行版中最流行的。

当谈到 shell 脚本编程的时候,也就意味着 —— 用户希望使用脚本来执行多条命令来获得一个输出。

也许你需要学习 shell 脚本编程作为你的课程或者工作的一部分。了解 shell 脚本编程也可以帮助你在 Linux 中自动化某些重复的任务。

不管出于什么原因学习 shell 脚本编程,都可以看看这些我给你展示的资源。

最好的免费学习 shell 脚本编程的资源

还没在你的系统上安装 Linux?不用担心。有很多种方法在 Windows 上使用 Linux 终端。你也可以在某些情况下使用在线 Linux 终端来练习 shell 脚本编程。

1、学习 Shell —— 互动网站

如果你正在找一个互动网站来学习 shell 脚本编程,并且还可以在线试试,“学习 Shell” 是一个不错的起点。

它涵盖了基础知识,并且也提供了一些高级的练习。通常,内容还是简明扼要的 —— 因此,我建议你看看这个网站。

2、Shell 脚本编程教程 —— 门户网站

Shell 脚本编程教程” 是一个完全专注于 shell 脚本编程的网站。你可以选择免费阅读其中的资源,也可以购买 PDF、实体书籍和电子书来支持他们。

当然,花钱买纸质的版本或者电子书不是强制的。但是,这些免费资源查看起来还是很方便的。

3、Udemy:Shell 脚本 —— 免费视频课程

毫无疑问,Udemy 是最受欢迎的在线课程平台之一。而且,除了付费认证课程之外,它还提供了不包含证书的免费内容。

Shell 脚本” 是 Udemy 上推荐度最高的免费课程之一。你不需要花费任何费用就可以注册这门课。

4、Udemy:Bash Shell 脚本编程 —— 免费视频课程

Udemy 上另一个专注于 bash shell 脚本编程的有趣且免费的课程。与前面提到的课程相比,这个资源似乎更受欢迎。所以,你可以注册这门课,看看它都教些什么。

别忘了 Udemy 的免费课程不能提供证书。但是,它确实是一个让人印象深刻的免费 shell 脚本编程学习资源。

5、Bash 研究院 —— 互动游戏在线门户

顾名思义,“Bash 研究院” 专注于向用户提供 bash shell 的教学。

尽管它没有很多的内容,它还是非常适合初学者和有一定经验的用户。不仅仅局限于指导 —— 它也可以提供交互式的游戏来练习,不过目前已经不能用了。

因此,如果这个足够有趣,你可以去看看这个 Github 页面,并且如果你愿意的话,还可以复刻它并对现有资源进行改进。

6、LinkedIn:学习 Bash 脚本编程 —— 免费视频课程

LinkedIn 提供了大量免费课程来帮助你提成技能,并且为更多工作做好准备。你还可以找到一些专注于 shell 脚本编程的课程,这些课程有助于重温基本技能或者这个过程中获得一些高级技能。

在这里,我提供一个 学习 Bash 脚本编程 的课程链接,你还可以发现其他类似的免费课程。

7、高级 Bash 脚本编程指南 —— 免费 PDF 书籍

这是一个令人印象深刻的《高级 Bash 脚本编程指南》,并且可以免费获得到它的 PDF 版本。这个 PDF 资源没有版权限制,在公开领域是完全免费的。

尽管这个资源主要是提供高级的知识,通过参考这个 PDF 并且开始学习 shell 脚本编程,它还是很适合初学者的。

8、专业 Bash 笔记 —— 免费 PDF 书籍

如果你已经对 Bash Shell 脚本编程比较熟悉或者只是想快速总结一下,那这是一个很好的参考。

这个《专业 Bash 笔记》可以免费下载的书有 100 多页,通过简单的描述和例子,这本书涵盖了各种各样的主题。

9、Tutorialspoint —— 门户网站

Tutorialspoint” 是一个非常流行的学习各种编程语言的门户网站。我想说这对于初学者学习基础知识非常好。

也许这不太适合作为一个详细的资源——但是它应该是不错的免费资源。

10、旧金山城市学院:在线笔记 —— 门户网站

也许这不是最好的免费资源 —— 但是如果你已经为学习 shell 脚本编程做好了探索每种资源的准备,为什么不看看旧金山城市学院的 “在线笔记” 呢?

当我在网上随便搜索关于 shell 脚本编程的资源的时候,我偶然遇到到了这个资源。

同样需要注意的是,这个在线笔记可能会有点过时。但是,这应该还是一个值得探索的有趣资源。

荣誉奖: Linux 手册

不要忘记,bash 手册也应该是一个相当不错的免费资源,可以用它来查看命令和使用方法。

尽管它不是专门为你掌握 shell 脚本编程而量身打造的,它依然是一个你可以免费使用的重要网络资源。你可以选择访问在线手册,或者直接打开终端然后输入以下命令:

man bash

总结

有很多很受欢迎的付费资源,比如这些最好的 Linux 书籍。从网络上的一些免费资源开始学习 shell 脚本编程还是很方便的。

除了我提到的这些,我敢肯定网上还有不计其数的资源可以帮助你学习 shell 脚本编程。

你喜欢换上面提到的资源吗?如果你知道我可能错过的非常棒的免费资源,记得在下面评论区告诉我。


via: https://itsfoss.com/shell-scripting-resources/

作者:Ankush Das 选题:lujun9972 译者:Yufei-Yan 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

Fish — A Friendly Interactive Shell

你是否正在寻找 bash 的替代品?你是否在寻找更人性化的东西?不用再看了,因为你刚发现了 fish!

Fish(友好的交互式 shell)是一个智能且用户友好的命令行 shell,可在 Linux、MacOS 和其他操作系统上运行。可以将其用于终端的日常工作和脚本编写。用 fish 编写的脚本比相同的 bash 版本具有更少的神秘性。

Fish 的用户友好功能

  • 建议:Fish 会提示你之前写过的命令。当经常输入相同命令时,这样可以提高生产率。
  • 健全的脚本能力:Fish 避免使用那些隐秘字符。这提供了更清晰和更友好的语法。
  • 基于手册页的补全:Fish 会根据命令的手册页自动补全参数。
  • 语法高亮:Fish 会高亮显示命令语法以使其在视觉上友好。

安装

Fedora 工作站

使用 dnf 命令安装 fish:

$ sudo dnf install fish

安装 util-linux-user 包,然后使用适当的参数运行 chsh(更改 shell 程序)命令,将 fish 设置为默认 shell 程序:

$ sudo dnf install util-linux-user
$ chsh -s /usr/bin/fish

你需要注销然后重新登录,更改才能生效。

Fedora Silverblue

由于它不是 GUI 应用,因此你需要使用 rpm-ostree 将其加到层内。使用以下命令在 Fedora Silverblue 上安装 fish:

$ rpm-ostree install fish

在 Fedora Silverblue 上,你需要重启 PC 才能切换到新的 ostree 镜像。

如果你想在 Fedora Silverblue 用 fish 作为主要 shell,最简单的方法是更新 /etc/passwd 文件。找到你的用户,并将 /bin/bash 更改为 /usr/bin/fish

你需要 root 权限来编辑 /etc/passwd 文件。另外,你需要注销并重新登录才能使更改生效。

配置

fish 的用户配置文件在 ~/.config/fish/config.fish。要更改所有用户的配置,请编辑 /etc/fish/config.fish

用户配置文件必须手动创建。安装脚本不会创建 ~/.config/fish/config.fish

以下是两个个配置示例以及它们的 bash 等效项,以帮助你入门:

创建别名

  • ~/.bashrcalias ll='ls -lh'
  • ~/.config/fish/config.fish: alias ll='ls -lh'

设置环境变量

  • ~/.bashrcexport PATH=$PATH:~/bin
  • ~/.config/fish/config.fishset -gx PATH $PATH ~/bin

使用 fish 工作

将 fish 配置为默认 shell 程序后,命令提示符将类似于下图所示。如果尚未将 fish 配置为默认 shell,只需运行 fish 命令以在当前终端会话中启动。

在你开始输入命令时,你会注意到语法高亮显示:

很酷,不是吗??

你还将在输入时看到建议的命令。例如,再次开始输入上一个命令:

注意输入时出现的灰色文本。灰色文本显示建议之前编写的命令。要自动补全,只需按 CTRL+F

通过输入连接号()然后使用 TAB 键,它会根据前面命令的手册页获取参数建议:

如果你按一次 TAB,它将显示前几个建议(或所有建议,如果只有少量参数可用)。如果再次按 TAB,它将显示所有建议。如果连续三次按 TAB,它将切换到交互模式,你可以使用箭头键选择一个参数。

除此之外,fish 的工作与大多数其他 shell 相似。其他差异已经写在文档中。因此,找到你可能感兴趣的其他功能应该不难。

让 fish 变得更强大

使用 powerline 使 fish 变得更强大。Powerline 可以为 fish 的界面添加命令执行时间、彩色化 git 状态、当前 git 分支等。

在安装 powerline 之前,你必须先安装 Oh My Fish。Oh My Fish 扩展了 fish 的核心基础架构,以支持安装其他插件。安装 Oh My Fish 的最简单方法是使用 curl 命令:

> curl -L https://get.oh-my.fish | fish

如果你不想直接将安装命令管道传给 fish,请参见 Oh My Fish 的 README 的安装部分,以了解其他安装方法。

Fish 的 powerline 插件是 bobthefish。Bobthefish 需要 powerline-fonts 包。

在 Fedora 工作站上:

> sudo dnf install powerline-fonts

在 Fedora Silverblue 上:

> rpm-ostree install powerline-fonts

在 Fedora Silverblue 上,你必须重启以完成字体的安装。

安装 powerline-fonts 之后,安装 bobthefish

> omf install bobthefish

现在你可以通过 powerline 体验 fish 的全部奇妙之处:

更多资源

查看这些网页,了解更多 fish 内容:


via: https://fedoramagazine.org/fish-a-friendly-interactive-shell/

作者:Michal Konečný 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

让我们通过本系列文章来学习基本的 Bash 编程语法和工具,以及如何使用变量和控制运算符,这是三篇中的第一篇。

Shell 是操作系统的命令解释器,其中 Bash 是我最喜欢的。每当用户或者系统管理员将命令输入系统的时候,Linux 的 shell 解释器就会把这些命令转换成操作系统可以理解的形式。而执行结果返回 shell 程序后,它会将结果输出到 STDOUT(标准输出),默认情况下,这些结果会显示在你的终端。所有我熟悉的 shell 同时也是一门编程语言。

Bash 是个功能强大的 shell,包含众多便捷特性,比如:tab 补全、命令回溯和再编辑、别名等。它的命令行默认编辑模式是 Emacs,但是我最喜欢的 Bash 特性之一是我可以将其更改为 Vi 模式,以使用那些储存在我肌肉记忆中的的编辑命令。

然而,如果你把 Bash 当作单纯的 shell 来用,则无法体验它的真实能力。我在设计一套包含三卷的 Linux 自学课程时(这个系列的文章正是基于此课程),了解到许多 Bash 的知识,这些是我在过去 20 年的 Linux 工作经验中所没有掌握的,其中的一些知识就是关于 Bash 的编程用法。不得不说,Bash 是一门强大的编程语言,是一个能够同时用于命令行和 shell 脚本的完美设计。

本系列文章将要探讨如何使用 Bash 作为命令行界面(CLI)编程语言。第一篇文章简单介绍 Bash 命令行编程、变量以及控制运算符。其他文章会讨论诸如:Bash 文件的类型;字符串、数字和一些逻辑运算符,它们能够提供代码执行流程中的逻辑控制;不同类型的 shell 扩展;通过 forwhileuntil 来控制循环操作。

Shell

Bash 是 Bourne Again Shell 的缩写,因为 Bash shell 是 基于 更早的 Bourne shell,后者是 Steven Bourne 在 1977 年开发的。另外还有很多其他的 shell 可以使用,但下面四个是我经常见到的:

  • csh:C shell 适合那些习惯了 C 语言语法的开发者。
  • ksh:Korn shell,由 David Korn 开发,在 Unix 用户中更流行。
  • tcsh:一个 csh 的变种,增加了一些易用性。
  • zsh:Z shell,集成了许多其他流行 shell 的特性。

所有 shell 都有内置命令,用以补充或替代核心工具集。打开 shell 的 man 说明页,找到“BUILT-INS”那一段,可以查看都有哪些内置命令。

每种 shell 都有它自己的特性和语法风格。我用过 csh、ksh 和 zsh,但我还是更喜欢 Bash。你可以多试几个,寻找更适合你的 shell,尽管这可能需要花些功夫。但幸运的是,切换不同 shell 很简单。

所有这些 shell 既是编程语言又是命令解释器。下面我们来快速浏览一下 Bash 中集成的编程结构和工具。

作为编程语言的 Bash

大多数场景下,系统管理员都会使用 Bash 来发送简单明了的命令。但 Bash 不仅可以输入单条命令,很多系统管理员可以编写简单的命令行程序来执行一系列任务,这些程序可以作为通用工具,能节省时间和精力。

编写 CLI 程序的目的是要提高效率(做一个“懒惰的”系统管理员)。在 CLI 程序中,你可以用特定顺序列出若干命令,逐条执行。这样你就不用盯着显示屏,等待一条命令执行完,再输入另一条,省下来的时间就可以去做其他事情了。

什么是“程序”?

自由在线计算机词典(FOLDOC)对于程序的定义是:“由计算机执行的指令,而不是运行它们的物理硬件。”普林斯顿大学的 WordNet 将程序定义为:“……计算机可以理解并执行的一系列指令……”维基百科上也有一条不错的关于计算机程序的条目。

总结下,程序由一条或多条指令组成,目的是完成一个具体的相关任务。对于系统管理员而言,一段程序通常由一系列的 shell 命令构成。Linux 下所有的 shell (至少我所熟知的)都有基本的编程功能,Bash 作为大多数 linux 发行版的默认 shell,也不例外。

本系列用 Bash 举例(因为它无处不在),假如你使用一个不同的 shell 也没关系,尽管结构和语法有所不同,但编程思想是相通的。有些 shell 支持某种特性而其他 shell 则不支持,但它们都提供编程功能。Shell 程序可以被存在一个文件中被反复使用,或者在需要的时候才创建它们。

简单 CLI 程序

最简单的命令行程序只有一或两条语句,它们可能相关,也可能无关,在按回车键之前被输入到命令行。程序中的第二条语句(如果有的话)可能取决于第一条语句的操作,但也不是必须的。

这里需要特别讲解一个标点符号。当你在命令行输入一条命令,按下回车键的时候,其实在命令的末尾有一个隐含的分号(;)。当一段 CLI shell 程序在命令行中被串起来作为单行指令使用时,必须使用分号来终结每个语句并将其与下一条语句分开。但 CLI shell 程序中的最后一条语句可以使用显式或隐式的分号。

一些基本语法

下面的例子会阐明这一语法规则。这段程序由单条命令组成,还有一个显式的终止符:

[student@studentvm1 ~]$ echo "Hello world." ;
Hello world.

看起来不像一个程序,但它确是我学习每个新编程语言时写下的第一个程序。不同语言可能语法不同,但输出结果是一样的。

让我们扩展一下这段微不足道却又无所不在的代码。你的结果可能与我的有所不同,因为我的家目录有点乱,而你可能是在 GUI 桌面中第一次登录账号。

[student@studentvm1 ~]$ echo "My home directory." ; ls ;
My home directory.
chapter25   TestFile1.Linux  dmesg2.txt  Downloads  newfile.txt  softlink1  testdir6
chapter26   TestFile1.mac    dmesg3.txt  file005    Pictures     Templates  testdir
TestFile1      Desktop       dmesg.txt   link3      Public       testdir    Videos
TestFile1.dos  dmesg1.txt    Documents   Music      random.txt   testdir1

现在是不是更明显了。结果是相关的,但是两条语句彼此独立。你可能注意到我喜欢在分号前后多输入一个空格,这样会让代码的可读性更好。让我们再运行一遍这段程序,这次不要带结尾的分号:

[student@studentvm1 ~]$ echo "My home directory." ; ls

输出结果没有区别。

关于变量

像所有其他编程语言一样,Bash 支持变量。变量是个象征性的名字,它指向内存中的某个位置,那里存着对应的值。变量的值是可以改变的,所以它叫“变~量”。

Bash 不像 C 之类的语言,需要强制指定变量类型,比如:整型、浮点型或字符型。在 Bash 中,所有变量都是字符串。整数型的变量可以被用于整数运算,这是 Bash 唯一能够处理的数学类型。更复杂的运算则需要借助 bc 这样的命令,可以被用在命令行编程或者脚本中。

变量的值是被预先分配好的,这些值可以用在命令行编程或者脚本中。可以通过变量名字给其赋值,但是不能使用 $ 符开头。比如,VAR=10 这样会把 VAR 的值设为 10。要打印变量的值,你可以使用语句 echo $VAR。变量名必须以文本(即非数字)开始。

Bash 会保存已经定义好的变量,直到它们被取消掉。

下面这个例子,在变量被赋值前,它的值是空(null)。然后给它赋值并打印出来,检验一下。你可以在同一行 CLI 程序里完成它:

[student@studentvm1 ~]$ echo $MyVar ; MyVar="Hello World" ; echo $MyVar ;

Hello World
[student@studentvm1 ~]$

注意:变量赋值的语法非常严格,等号(=)两边不能有空格。

那个空行表明了 MyVar 的初始值为空。变量的赋值和改值方法都一样,这个例子展示了原始值和新的值。

正如之前说的,Bash 支持整数运算,当你想计算一个数组中的某个元素的位置,或者做些简单的算术运算,这还是挺有帮助的。然而,这种方法并不适合科学计算,或是某些需要小数运算的场景,比如财务统计。这些场景有其它更好的工具可以应对。

下面是个简单的算术题:

[student@studentvm1 ~]$ Var1="7" ; Var2="9" ; echo "Result = $((Var1*Var2))"
Result = 63

好像没啥问题,但如果运算结果是浮点数会发生什么呢?

[student@studentvm1 ~]$ Var1="7" ; Var2="9" ; echo "Result = $((Var1/Var2))"
Result = 0
[student@studentvm1 ~]$ Var1="7" ; Var2="9" ; echo "Result = $((Var2/Var1))"
Result = 1
[student@studentvm1 ~]$

结果会被取整。请注意运算被包含在 echo 语句之中,其实计算在 echo 命令结束前就已经完成了,原因是 Bash 的内部优先级。想要了解详情的话,可以在 Bash 的 man 页面中搜索 “precedence”。

控制运算符

Shell 的控制运算符是一种语法运算符,可以轻松地创建一些有趣的命令行程序。在命令行上按顺序将几个命令串在一起,就变成了最简单的 CLI 程序:

command1 ; command2 ; command3 ; command4 ; . . . ; etc. ;

只要不出错,这些命令都能顺利执行。但假如出错了怎么办?你可以预设好应对出错的办法,这就要用到 Bash 内置的控制运算符, &&||。这两种运算符提供了流程控制功能,使你能改变代码执行的顺序。分号也可以被看做是一种 Bash 运算符,预示着新一行的开始。

&& 运算符提供了如下简单逻辑,“如果 command1 执行成功,那么接着执行 command2。如果 command1 失败,就跳过 command2。”语法如下:

command1 && command2

现在,让我们用命令来创建一个新的目录,如果成功的话,就把它切换为当前目录。确保你的家目录(~)是当前目录,先尝试在 /root 目录下创建,你应该没有权限:

[student@studentvm1 ~]$ Dir=/root/testdir ; mkdir $Dir/ && cd $Dir
mkdir: cannot create directory '/root/testdir/': Permission denied
[student@studentvm1 ~]$

上面的报错信息是由 mkdir 命令抛出的,因为创建目录失败了。&& 运算符收到了非零的返回码,所以 cd 命令就被跳过,前者阻止后者继续运行,因为创建目录失败了。这种控制流程可以阻止后面的错误累积,避免引发更严重的问题。是时候讲点更复杂的逻辑了。

当一段程序的返回码大于零时,使用 || 运算符可以让你在后面接着执行另一段程序。简单语法如下:

command1 || command2

解读一下,“假如 command1 失败,执行 command2”。隐藏的逻辑是,如果 command1 成功,跳过 command2。下面实践一下,仍然是创建新目录:

[student@studentvm1 ~]$ Dir=/root/testdir ; mkdir $Dir || echo "$Dir was not created."
mkdir: cannot create directory '/root/testdir': Permission denied
/root/testdir was not created.
[student@studentvm1 ~]$

正如预期,因为目录无法创建,第一条命令失败了,于是第二条命令被执行。

&&|| 两种运算符结合起来才能发挥它们的最大功效。请看下面例子中的流程控制方法:

前置 commands ; command1 && command2 || command3 ; 跟随 commands

语法解释:“假如 command1 退出时返回码为零,就执行 command2,否则执行 command3。”用具体代码试试:

[student@studentvm1 ~]$ Dir=/root/testdir ; mkdir $Dir && cd $Dir || echo "$Dir was not created."
mkdir: cannot create directory '/root/testdir': Permission denied
/root/testdir was not created.
[student@studentvm1 ~]$

现在我们再试一次,用你的家目录替换 /root 目录,你将会有权限创建这个目录了:

[student@studentvm1 ~]$ Dir=~/testdir ; mkdir $Dir && cd $Dir || echo "$Dir was not created."
[student@studentvm1 testdir]$

command1 && command2 这样的控制语句能够运行的原因是,每条命令执行完毕时都会给 shell 发送一个返回码,用来表示它执行成功与否。默认情况下,返回码为 0 表示成功,其他任何正值表示失败。一些系统管理员使用的工具用值为 1 的返回码来表示失败,但其他很多程序使用别的数字来表示失败。

Bash 的内置变量 $? 可以显示上一条命令的返回码,可以在脚本或者命令行中非常方便地检查它。要查看返回码,让我们从运行一条简单的命令开始,返回码的结果总是上一条命令给出的。

[student@studentvm1 testdir]$ ll ; echo "RC = $?"
total 1264
drwxrwxr-x  2 student student   4096 Mar  2 08:21 chapter25
drwxrwxr-x  2 student student   4096 Mar 21 15:27 chapter26
-rwxr-xr-x  1 student student     92 Mar 20 15:53 TestFile1
drwxrwxr-x. 2 student student 663552 Feb 21 14:12 testdir
drwxr-xr-x. 2 student student   4096 Dec 22 13:15 Videos
RC = 0
[student@studentvm1 testdir]$

在这个例子中,返回码为零,意味着命令执行成功了。现在对 root 的家目录测试一下,你应该没有权限:

[student@studentvm1 testdir]$ ll /root ; echo "RC = $?"
ls: cannot open directory '/root': Permission denied
RC = 2
[student@studentvm1 testdir]$

本例中返回码是 2,表明非 root 用户没有权限进入这个目录。你可以利用这些返回码,用控制运算符来改变程序执行的顺序。

总结

本文将 Bash 看作一门编程语言,并从这个视角介绍了它的简单语法和基础工具。我们学习了如何将数据输出到 STDOUT,怎样使用变量和控制运算符。在本系列的下一篇文章中,将会重点介绍能够控制指令执行流程的逻辑运算符。


via: https://opensource.com/article/19/10/programming-bash-part-1

作者:David Both 选题:lujun9972 译者:jdh8383 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出