标签 Bash 下的文章

学习 Bash 读取和写入数据的不同方式,以及何时使用每种方法。

 title=

当你使用 Bash 编写脚本时,有时你需要从一个文件中读取数据或向一个文件写入数据。有时文件可能包含配置选项,而另一些时候这个文件是你的用户用你的应用创建的数据。每种语言处理这个任务的方式都有些不同,本文将演示如何使用 Bash 和其他 POSIX shell 处理数据文件。

安装 Bash

如果你在使用 Linux,你可能已经有了 Bash。如果没有,你可以在你的软件仓库里找到它。

在 macOS 上,你可以使用默认终端,Bash 或 Zsh,这取决于你运行的 macOS 版本。

在 Windows 上,有几种方法可以体验 Bash,包括微软官方支持的 Windows Subsystem for Linux(WSL)。

安装 Bash 后,打开你最喜欢的文本编辑器并准备开始。

使用 Bash 读取文件

除了是 shell 之外,Bash 还是一种脚本语言。有几种方法可以从 Bash 中读取数据。你可以创建一种数据流并解析输出, 或者你可以将数据加载到内存中。这两种方法都是有效的获取信息的方法,但每种方法都有相当具体的用例。

在 Bash 中援引文件

当你在 Bash 中 “ 援引 source ” 一个文件时,你会让 Bash 读取文件的内容,期望它包含有效的数据,Bash 可以将这些数据放入它建立的数据模型中。你不会想要从旧文件中援引数据,但你可以使用这种方法来读取配置文件和函数。

(LCTT 译注:在 Bash 中,可以通过 source. 命令来将一个文件读入,这个行为称为 “sourcing”,英文原意为“一次性(试)采购”、“寻找供应商”、“获得”等,考虑到 Bash 的语境和发音,我建议可以翻译为“援引”,或有不当,供大家讨论参考 —— wxy)

例如,创建一个名为 example.sh 的文件,并输入以下内容:

#!/bin/sh

greet opensource.com

echo "The meaning of life is $var"

运行这段代码,看见失败了:

$ bash ./example.sh
./example.sh: line 3: greet: command not found
The meaning of life is

Bash 没有一个叫 greet 的命令,所以无法执行那一行,也没有一个叫 var 的变量记录,所以文件没有意义。为了解决这个问题,建立一个名为 include.sh 的文件:

greet() {
    echo "Hello ${1}"
}

var=42

修改你的 example.sh 脚本,加入一个 source 命令:

#!/bin/sh

source include.sh

greet opensource.com

echo "The meaning of life is $var"

运行脚本,可以看到工作了:

$ bash ./example.sh
Hello opensource.com
The meaning of life is 42

greet 命令被带入你的 shell 环境,因为它被定义在 include.sh 文件中,它甚至可以识别参数(本例中的 opensource.com)。变量 var 也被设置和导入。

在 Bash 中解析文件

另一种让数据“进入” Bash 的方法是将其解析为数据流。有很多方法可以做到这一点. 你可以使用 grepcat 或任何可以获取数据并管道输出到标准输出的命令。另外,你可以使用 Bash 内置的东西:重定向。重定向本身并不是很有用,所以在这个例子中,我也使用内置的 echo 命令来打印重定向的结果:

#!/bin/sh

echo $( < include.sh )

将其保存为 stream.sh 并运行它来查看结果:

$ bash ./stream.sh
greet() { echo "Hello ${1}" } var=42
$

对于 include.sh 文件中的每一行,Bash 都会将该行打印(或 echo)到你的终端。先用管道把它传送到一个合适的解析器是用 Bash 读取数据的常用方法。例如, 假设 include.sh 是一个配置文件, 它的键和值对用一个等号(=)分开. 你可以用 awk 甚至 cut 来获取值:

#!/bin/sh

myVar=`grep var include.sh | cut -d'=' -f2`

echo $myVar

试着运行这个脚本:

$ bash ./stream.sh
42

用 Bash 将数据写入文件

无论你是要存储用户用你的应用创建的数据,还是仅仅是关于用户在应用中做了什么的元数据(例如,游戏保存或最近播放的歌曲),都有很多很好的理由来存储数据供以后使用。在 Bash 中,你可以使用常见的 shell 重定向将数据保存到文件中。

例如, 要创建一个包含输出的新文件, 使用一个重定向符号:

#!/bin/sh

TZ=UTC
date > date.txt

运行脚本几次:

$ bash ./date.sh
$ cat date.txt
Tue Feb 23 22:25:06 UTC 2021
$ bash ./date.sh
$ cat date.txt
Tue Feb 23 22:25:12 UTC 2021

要追加数据,使用两个重定向符号:

#!/bin/sh

TZ=UTC
date >> date.txt

运行脚本几次:

$ bash ./date.sh
$ bash ./date.sh
$ bash ./date.sh
$ cat date.txt
Tue Feb 23 22:25:12 UTC 2021
Tue Feb 23 22:25:17 UTC 2021
Tue Feb 23 22:25:19 UTC 2021
Tue Feb 23 22:25:22 UTC 2021

Bash 轻松编程

Bash 的优势在于简单易学,因为只需要一些基本的概念,你就可以构建复杂的程序。完整的文档请参考 GNU.org 上的 优秀的 Bash 文档


via: https://opensource.com/article/21/3/input-output-bash

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

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

了解如何在 Bash 中编写定制程序以自动执行重复性操作任务。

 title=

Unix 最初的希望之一是,让计算机的日常用户能够微调其计算机,以适应其独特的工作风格。几十年来,人们对计算机定制的期望已经降低,许多用户认为他们的应用程序和网站的集合就是他们的 “定制环境”。原因之一是许多操作系统的组件未不开源,普通用户无法使用其源代码。

但是对于 Linux 用户而言,定制程序是可以实现的,因为整个系统都围绕着可通过终端使用的命令啦进行的。终端不仅是用于快速命令或深入排除故障的界面;也是一个脚本环境,可以通过为你处理日常任务来减少你的工作量。

如何学习编程

如果你以前从未进行过任何编程,可能面临考虑两个不同的挑战:一个是了解怎样编写代码,另一个是了解要编写什么代码。你可以学习 语法,但是如果你不知道 语言 中有哪些可用的关键字,你将无法继续。在实践中,要同时开始学习这两个概念,是因为如果没有关键字的堆砌就无法学习语法,因此,最初你要使用基本命令和基本编程结构来编写简单的任务。一旦熟悉了基础知识,就可以探索更多编程语言的内容,从而使你的程序能够做越来越重要的事情。

Bash 中,你使用的大多数 关键字 是 Linux 命令。 语法 就是 Bash。如果你已经频繁地使用过了 Bash,则向 Bash 编程的过渡相对容易。但是,如果你不曾使用过 Bash,你会很高兴地了解到它是一种为清晰和简单而构建的简单语言。

交互设计

有时,学习编程时最难搞清楚的事情就是计算机可以为你做些什么。显然,如果一台计算机可以自己完成你要做的所有操作,那么你就不必再碰计算机了。但是现实是,人类很重要。找到你的计算机可以帮助你的事情的关键是注意到你一周内需要重复执行的任务。计算机特别擅长于重复的任务。

但是,为了能告知计算机为你做某事,你必须知道怎么做。这就是 Bash 擅长的领域:交互式编程。在终端中执行一个动作时,你也在学习如何编写脚本。

例如,我曾经负责将大量 PDF 书籍转换为低墨和友好打印的版本。一种方法是在 PDF 编辑器中打开 PDF,从数百张图像(页面背景和纹理都算作图像)中选择每张图像,删除它们,然后将其保存到新的 PDF中。仅仅是一本书,这样就需要半天时间。

我的第一个想法是学习如何编写 PDF 编辑器脚本,但是经过数天的研究,我找不到可以编写编辑 PDF 应用程序的脚本(除了非常丑陋的鼠标自动化技巧)。因此,我将注意力转向了从终端内找出完成任务的方法。这让我有了几个新发现,包括 GhostScript,它是 PostScript 的开源版本(PDF 基于的打印机语言)。通过使用 GhostScript 处理了几天的任务,我确认这是解决我的问题的方法。

编写基本的脚本来运行命令,只不过是复制我用来从 PDF 中删除图像的命令和选项,并将其粘贴到文本文件中而已。将这个文件作为脚本运行,大概也会产生同样的结果。

向 Bash 脚本传参数

在终端中运行命令与在 Shell 脚本中运行命令之间的区别在于前者是交互式的。在终端中,你可以随时进行调整。例如,如果我刚刚处理 example_1.pdf 并准备处理下一个文档,以适应我的命令,则只需要更改文件名即可。

Shell 脚本不是交互式的。实际上,Shell 脚本 存在的唯一原因是让你不必亲自参与。这就是为什么命令(以及运行它们的 Shell 脚本)会接受参数的原因。

在 Shell 脚本中,有一些预定义的可以反映脚本启动方式的变量。初始变量是 $0,它代表了启动脚本的命令。下一个变量是 $1 ,它表示传递给 Shell 脚本的第一个 “参数”。例如,在命令 echo hello 中,命令 echo$0,,关键字 hello$1,而 world$2

在 Shell 中交互如下所示:

$ echo hello world
hello world

在非交互式 Shell 脚本中,你 可以 以非常直观的方式执行相同的操作。将此文本输入文本文件并将其另存为 hello.sh

echo hello world

执行这个脚本:

$ bash hello.sh
hello world

同样可以,但是并没有利用脚本可以接受输入这一优势。将 hello.sh 更改为:

echo $1

用引号将两个参数组合在一起来运行脚本:

$ bash hello.sh "hello bash"
hello bash

对于我的 PDF 瘦身项目,我真的需要这种非交互性,因为每个 PDF 都花了几分钟来压缩。但是通过创建一个接受我的输入的脚本,我可以一次将几个 PDF 文件全部提交给脚本。该脚本按顺序处理了每个文件,这可能需要半小时或稍长一点时间,但是我可以用半小时来完成其他任务。

流程控制

创建 Bash 脚本是完全可以接受的,从本质上讲,这些脚本是你开始实现需要重复执行任务的准确过程的副本。但是,可以通过控制信息流的方式来使脚本更强大。管理脚本对数据响应的常用方法是:

  • if/then 选择结构语句
  • for 循环结构语句
  • while 循环结构语句
  • case 语句

计算机不是智能的,但是它们擅长比较和分析数据。如果你在脚本中构建一些数据分析,则脚本会变得更加智能。例如,基本的 hello.sh 脚本运行后不管有没有内容都会显示:

$ bash hello.sh foo
foo
$ bash hello.sh

$

如果在没有接收输入的情况下提供帮助消息,将会更加容易使用。如下是一个 if/then 语句,如果你以一种基本的方式使用 Bash,则你可能不知道 Bash 中存在这样的语句。但是编程的一部分是学习语言,通过一些研究,你将了解 if/then 语句:

if [ "$1" = "" ]; then
        echo "syntax: $0 WORD"
        echo "If you provide more than one word, enclose them in quotes."
else
        echo "$1"
fi

运行新版本的 hello.sh 输出如下:

$ bash hello.sh
syntax: hello.sh WORD
If you provide more than one word, enclose them in quotes.
$ bash hello.sh "hello world"
hello world

利用脚本工作

无论你是从 PDF 文件中查找要删除的图像,还是要管理混乱的下载文件夹,抑或要创建和提供 Kubernetes 镜像,学习编写 Bash 脚本都需要先使用 Bash,然后学习如何将这些脚本从仅仅是一个命令列表变成响应输入的东西。通常这是一个发现的过程:你一定会找到新的 Linux 命令来执行你从未想象过可以通过文本命令执行的任务,你会发现 Bash 的新功能,使你的脚本可以适应所有你希望它们运行的不同方式。

学习这些技巧的一种方法是阅读其他人的脚本。了解人们如何在其系统上自动化死板的命令。看看你熟悉的,并寻找那些陌生事物的更多信息。

另一种方法是下载我们的 Bash 编程入门 电子书。它向你介绍了特定于 Bash 的编程概念,并且通过学习的构造,你可以开始构建自己的命令。当然,它是免费的,并根据 创作共用许可证 进行下载和分发授权,所以今天就来获取它吧。


via: https://opensource.com/article/20/4/bash-programming-guide

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

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

Bash 自动测试系统可以使 Bash 代码也通过 Java、Ruby 和 Python 开发人员所使用的同类测试过程。

用 Java、Ruby 和 Python 等语言编写应用程序的软件开发人员拥有复杂的库,可以帮助他们随着时间的推移保持软件的完整性。他们可以创建测试,以在结构化环境中通过执行一系列动作来运行应用程序,以确保其软件所有的方面均按预期工作。

当这些测试在持续集成(CI)系统中自动进行时,它们的功能就更加强大了,每次推送到源代码库都会触发测试,并且在测试失败时会立即通知开发人员。这种快速反馈提高了开发人员对其应用程序功能完整性的信心。

Bash 自动测试系统 Bash Automated Testing System BATS)使编写 Bash 脚本和库的开发人员能够将 Java、Ruby、Python 和其他开发人员所使用的相同惯例应用于其 Bash 代码中。

安装 BATS

BATS GitHub 页面包含了安装指令。有两个 BATS 辅助库提供更强大的断言或允许覆写 BATS 使用的 Test Anything Protocol(TAP)输出格式。这些库可以安装在一个标准位置,并被所有的脚本引用。更方便的做法是,将 BATS 及其辅助库的完整版本包含在 Git 仓库中,用于要测试的每组脚本或库。这可以通过 git 子模块 系统来完成。

以下命令会将 BATS 及其辅助库安装到 Git 知识库中的 test 目录中。

git submodule init
git submodule add https://github.com/sstephenson/bats test/libs/bats
git submodule add https://github.com/ztombol/bats-assert test/libs/bats-assert
git submodule add https://github.com/ztombol/bats-support test/libs/bats-support
git add .
git commit -m 'installed bats'

要克隆 Git 仓库并同时安装其子模块,请在 git clone 时使用 --recurse-submodules 标记。

每个 BATS 测试脚本必须由 bats 可执行文件执行。如果你将 BATS 安装到源代码仓库的 test/libs 目录中,则可以使用以下命令调用测试:

./test/libs/bats/bin/bats <测试脚本的路径>

或者,将以下内容添加到每个 BATS 测试脚本的开头:

#!/usr/bin/env ./test/libs/bats/bin/bats
load 'libs/bats-support/load'
load 'libs/bats-assert/load'

并且执行命令 chmod +x <测试脚本的路径>。 这将 a、使它们可与安装在 ./test/libs/bats 中的 BATS 一同执行,并且 b、包含这些辅助库。BATS 测试脚本通常存储在 test 目录中,并以要测试的脚本命名,扩展名为 .bats。例如,一个测试 bin/build 的 BATS 脚本应称为 test/build.bats

你还可以通过向 BATS 传递正则表达式来运行一整套 BATS 测试文件,例如 ./test/lib/bats/bin/bats test/*.bats

为 BATS 覆盖率而组织库和脚本

Bash 脚本和库必须以一种有效地方式将其内部工作原理暴露给 BATS 进行组织。通常,在调用或执行时库函数和运行诸多命令的 Shell 脚本不适合进行有效的 BATS 测试。

例如,build.sh 是许多人都会编写的典型脚本。本质上是一大堆代码。有些人甚至可能将这堆代码放入库中的函数中。但是,在 BATS 测试中运行一大堆代码,并在单独的测试用例中覆盖它可能遇到的所有故障类型是不可能的。测试这堆代码并有足够的覆盖率的唯一方法就是把它分解成许多小的、可重用的、最重要的是可独立测试的函数。

向库添加更多的函数很简单。额外的好处是其中一些函数本身可以变得出奇的有用。将库函数分解为许多较小的函数后,你可以在 BATS 测试中 援引 source 这些库,并像测试任何其他命令一样运行这些函数。

Bash 脚本也必须分解为多个函数,执行脚本时,脚本的主要部分应调用这些函数。此外,还有一个非常有用的技巧,可以让你更容易地用 BATS 测试 Bash 脚本:将脚本主要部分中执行的所有代码都移到一个函数中,称为 run_main。然后,将以下内容添加到脚本的末尾:

if [[ "${BASH_SOURCE[0]}" == "${0}" ]]
then
  run_main
fi

这段额外的代码做了一些特别的事情。它使脚本在作为脚本执行时与使用 援引 source 进入环境时的行为有所不同。通过援引并测试单个函数,这个技巧使得脚本的测试方式和库的测试方式变得一样。例如,这是重构的 build.sh,以获得更好的 BATS 可测试性

编写和运行测试

如上所述,BATS 是一个 TAP 兼容的测试框架,其语法和输出对于使用过其他 TAP 兼容测试套件(例如 JUnit、RSpec 或 Jest)的用户来说将是熟悉的。它的测试被组织成单个测试脚本。测试脚本被组织成一个或多个描述性 @test 块中,它们描述了被测试应用程序的单元。每个 @test 块将运行一系列命令,这些命令准备测试环境、运行要测试的命令,并对被测试命令的退出和输出进行断言。许多断言函数是通过 batsbats-assertbats-support 库导入的,这些库在 BATS 测试脚本的开头加载到环境中。下面是一个典型的 BATS 测试块:

@test "requires CI_COMMIT_REF_SLUG environment variable" {
  unset CI_COMMIT_REF_SLUG
  assert_empty "${CI_COMMIT_REF_SLUG}"
  run some_command
  assert_failure
  assert_output --partial "CI_COMMIT_REF_SLUG"
}

如果 BATS 脚本包含 setup(安装)和/或 teardown(拆卸) 函数,则 BATS 将在每个测试块运行之前和之后自动执行它们。这样就可以创建环境变量、测试文件以及执行一个或所有测试所需的其他操作,然后在每次测试运行后将其拆卸。Build.bats 是对我们新格式化的 build.sh 脚本的完整 BATS 测试。(此测试中的 mock_docker 命令将在以下关于模拟/打标的部分中进行说明。)

当测试脚本运行时,BATS 使用 exec(执行)来将每个 @test 块作为单独的子进程运行。这样就可以在一个 @test 中导出环境变量甚至函数,而不会影响其他 @test 或污染你当前的 Shell 会话。测试运行的输出是一种标准格式,可以被人理解,并且可以由 TAP 使用端以编程方式进行解析或操作。下面是 CI_COMMIT_REF_SLUG 测试块失败时的输出示例:

 ✗ requires CI_COMMIT_REF_SLUG environment variable
   (from function `assert_output' in file test/libs/bats-assert/src/assert.bash, line 231,
    in test file test/ci_deploy.bats, line 26)
     `assert_output --partial "CI_COMMIT_REF_SLUG"' failed

   -- output does not contain substring --
   substring (1 lines):
     CI_COMMIT_REF_SLUG
   output (3 lines):
     ./bin/deploy.sh: join_string_by: command not found
     oc error
     Could not login
   --

   ** Did not delete , as test failed **

1 test, 1 failure

下面是成功测试的输出:

✓ requires CI_COMMIT_REF_SLUG environment variable

辅助库

像任何 Shell 脚本或库一样,BATS 测试脚本可以包括辅助库,以在测试之间共享通用代码或增强其性能。这些辅助库,例如 bats-assertbats-support 甚至可以使用 BATS 进行测试。

库可以和 BATS 脚本放在同一个测试目录下,如果测试目录下的文件数量过多,也可以放在 test/libs 目录下。BATS 提供了 load 函数,该函数接受一个相对于要测试的脚本的 Bash 文件的路径(例如,在我们的示例中的 test),并援引该文件。文件必须以后缀 .bash 结尾,但是传递给 load 函数的文件路径不能包含后缀。build.bats 加载 bats-assertbats-support 库、一个小型 helpers.bash 库以及 docker_mock.bash 库(如下所述),以下代码位于测试脚本的开头,解释器魔力行下方:

load 'libs/bats-support/load'
load 'libs/bats-assert/load'
load 'helpers'
load 'docker_mock'

打标测试输入和模拟外部调用

大多数 Bash 脚本和库运行时都会执行函数和/或可执行文件。通常,它们被编程为基于这些函数或可执行文件的输出状态或输出(stdoutstderr)以特定方式运行。为了正确地测试这些脚本,通常需要制作这些命令的伪版本,这些命令被设计成在特定测试过程中以特定方式运行,称为“ 打标 stubbing ”。可能还需要监视正在测试的程序,以确保其调用了特定命令,或者使用特定参数调用了特定命令,此过程称为“ 模拟 mocking ”。有关更多信息,请查看在 Ruby RSpec 中 有关模拟和打标的讨论,它适用于任何测试系统。

Bash shell 提供了一些技巧,可以在你的 BATS 测试脚本中使用这些技巧进行模拟和打标。所有这些都需要使用带有 -f 标志的 Bash export 命令来导出一个覆盖了原始函数或可执行文件的函数。必须在测试程序执行之前完成此操作。下面是重写可执行命令 cat 的简单示例:

function cat() { echo "THIS WOULD CAT ${*}" }
export -f cat

此方法以相同的方式覆盖了函数。如果一个测试需要覆盖要测试的脚本或库中的函数,则在对函数进行打标或模拟之前,必须先声明已测试脚本或库,这一点很重要。否则,在声明脚本时,打标/模拟将被原函数替代。另外,在运行即将进行的测试命令之前确认打标/模拟。下面是build.bats 的示例,该示例模拟 build.sh 中描述的raise 函数,以确保登录函数会引发特定的错误消息:

@test ".login raises on oc error" {
  source ${profile_script}
  function raise() { echo "${1} raised"; }
  export -f raise
  run login
  assert_failure
  assert_output -p "Could not login raised"
}

一般情况下,没有必要在测试后复原打标/模拟的函数,因为 export(输出)仅在当前 @test 块的 exec(执行)期间影响当前子进程。但是,可以模拟/打标 BATS assert 函数在内部使用的命令(例如 catsed 等)是可能的。在运行这些断言命令之前,必须对这些模拟/打标函数进行 unset(复原),否则它们将无法正常工作。下面是 build.bats 中的一个示例,该示例模拟 sed,运行 build_deployable 函数并在运行任何断言之前复原 sed

@test ".build_deployable prints information, runs docker build on a modified Dockerfile.production and publish_image when its not a dry_run" {
  local expected_dockerfile='Dockerfile.production'
  local application='application'
  local environment='environment'
  local expected_original_base_image="${application}"
  local expected_candidate_image="${application}-candidate:${environment}"
  local expected_deployable_image="${application}:${environment}"
  source ${profile_script}
  mock_docker build --build-arg OAUTH_CLIENT_ID --build-arg OAUTH_REDIRECT --build-arg DDS_API_BASE_URL -t "${expected_deployable_image}" -
  function publish_image() { echo "publish_image ${*}"; }
  export -f publish_image
  function sed() {
    echo "sed ${*}" >&2;
    echo "FROM application-candidate:environment";
  }
  export -f sed
  run build_deployable "${application}" "${environment}"
  assert_success
  unset sed
  assert_output --regexp "sed.*${expected_dockerfile}"
  assert_output -p "Building ${expected_original_base_image} deployable ${expected_deployable_image} FROM ${expected_candidate_image}"
  assert_output -p "FROM ${expected_candidate_image} piped"
  assert_output -p "build --build-arg OAUTH_CLIENT_ID --build-arg OAUTH_REDIRECT --build-arg DDS_API_BASE_URL -t ${expected_deployable_image} -"
  assert_output -p "publish_image ${expected_deployable_image}"
}

有的时候相同的命令,例如 foo,将在被测试的同一函数中使用不同的参数多次调用。这些情况需要创建一组函数:

  • mock_foo:将期望的参数作为输入,并将其持久化到 TMP 文件中
  • foo:命令的模拟版本,该命令使用持久化的预期参数列表处理每个调用。必须使用 export -f 将其导出。
  • cleanup_foo:删除 TMP 文件,用于拆卸函数。这可以进行测试以确保在删除之前成功完成 @test 块。

由于此功能通常在不同的测试中重复使用,因此创建一个可以像其他库一样加载的辅助库会变得有意义。

docker\_mock.bash 是一个很棒的例子。它被加载到 build.bats 中,并在任何测试调用 Docker 可执行文件的函数的测试块中使用。使用 docker_mock 典型的测试块如下所示:

@test ".publish_image fails if docker push fails" {
  setup_publish
  local expected_image="image"
  local expected_publishable_image="${CI_REGISTRY_IMAGE}/${expected_image}"
  source ${profile_script}
  mock_docker tag "${expected_image}" "${expected_publishable_image}"
  mock_docker push "${expected_publishable_image}" and_fail
  run publish_image "${expected_image}"
  assert_failure
  assert_output -p "tagging ${expected_image} as ${expected_publishable_image}"
  assert_output -p "tag ${expected_image} ${expected_publishable_image}"
  assert_output -p "pushing image to gitlab registry"
  assert_output -p "push ${expected_publishable_image}"
}

该测试建立了一个使用不同的参数两次调用 Docker 的预期。在对Docker 的第二次调用失败时,它会运行测试命令,然后测试退出状态和对 Docker 调用的预期。

一方面 BATS 利用 mock_docker.bash 引入 ${BATS_TMPDIR} 环境变量,BATS 在测试开始的位置对其进行了设置,以允许测试和辅助程序在标准位置创建和销毁 TMP 文件。如果测试失败,mock_docker.bash 库不会删除其持久化的模拟文件,但会打印出其所在位置,以便可以查看和删除它。你可能需要定期从该目录中清除旧的模拟文件。

关于模拟/打标的一个注意事项:build.bats 测试有意识地违反了关于测试声明的规定:不要模拟没有拥有的! 该规定要求调用开发人员没有编写代码的测试命令,例如 dockercatsed 等,应封装在自己的库中,应在使用它们脚本的测试中对其进行模拟。然后应该在不模拟外部命令的情况下测试封装库。

这是一个很好的建议,而忽略它是有代价的。如果 Docker CLI API 发生变化,则测试脚本不会检测到此变化,从而导致错误内容直到经过测试的 build.sh 脚本在使用新版本 Docker 的生产环境中运行后才显示出来。测试开发人员必须确定要严格遵守此标准的程度,但是他们应该了解其所涉及的权衡。

总结

在任何软件开发项目中引入测试制度,都会在以下两方面产生权衡: a、增加开发和维护代码及测试所需的时间和组织,b、增加开发人员在对应用程序整个生命周期中完整性的信心。测试制度可能不适用于所有脚本和库。

通常,满足以下一个或多个条件的脚本和库才可以使用 BATS 测试:

  • 值得存储在源代码管理中
  • 用于关键流程中,并依靠它们长期稳定运行
  • 需要定期对其进行修改以添加/删除/修改其功能
  • 可以被其他人使用

一旦决定将测试规则应用于一个或多个 Bash 脚本或库,BATS 就提供其他软件开发环境中可用的全面测试功能。

致谢:感谢 Darrin Mann 向我引荐了 BATS 测试。


via: https://opensource.com/article/19/2/testing-bash-bats

作者:Darin London 选题:lujun9972 译者:stevenzdg988 校对:wxy

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

Bash 是大多数 Linux 系统上的默认命令行 shell。所以你为什么不试着学习如何最大限度地利用它呢?

 title=

Bash 是大多数 Linux 系统上的默认命令行 shell。所以你为什么不试着学习如何最大限度地利用它呢?今年,我们推荐了许多很棒的文章来帮助你充分利用 Bash shell 的强大功能。以下是一些关于 Bash 阅读次数最多的文章:

《通过重定向在 Linux 终端任意读写数据》

输入和输出重定向是任何编程或脚本语言的基础功能。从技术上讲,只要你与电脑互动,它就会自然而然地发生。输入从 stdin(标准输入,通常是你的键盘或鼠标)读取,输出到 stdout(标准输出,一般是文本或数据流),而错误被发送到 stderr(标准错误,一般和标准输出是一个位置)。了解这些数据流的存在,使你能够在使用 Bash 等 shell 时控制信息的去向。Seth Kenlon 分享了这些很棒的技巧,可以让你在不需要大量鼠标移动和按键的情况下从一个地方获取数据。你可能不经常使用重定向,但学习使用它可以为你节省大量不必要的打开文件和复制粘贴数据的时间。

《系统管理员 Bash 脚本入门》

Bash 是自由开源软件,所以任何人都可以安装它,不管他们运行的是 Linux、BSD、OpenIndiana、Windows 还是 macOS。Seth Kenlon 帮助你学习如何使用 Bash 的命令和特性,使其成为最强大的 shell 之一。

《针对大型文件系统可以试试此 Bash 脚本》

你是否曾经想列出一个目录中的所有文件,只显示其中的文件,不包括其他内容?或者只显示目录?如果你有,那么 Nick Clifton 的文章可能正是你正在寻找的。Nick 分享了一个漂亮的 Bash 脚本,它可以列出目录、文件、链接或可执行文件。该脚本使用 find 命令进行搜索,然后运行 ls 显示详细信息。对于管理大型 Linux 系统的人来说,这是一个漂亮的解决方案。

《用 Bash 工具对你的 Linux 系统配置进行快照》

你可能想与他人分享你的 Linux 配置,原因有很多。你可能需要帮助排除系统上的一个问题,或者你对自己创建的环境非常自豪,想向其他开源爱好者展示它。Don Watkins 向我们展示了 screenFetch 和 Neofetch 来捕获和分享你的系统配置。

《6 个方便的 Git 脚本》

Git 已经成为一个无处不在的代码管理系统。了解如何管理 Git 存储库可以简化你的开发体验。Bob Peterson 分享了 6 个 Bash 脚本,它们将使你在使用 Git 存储库时更加轻松。gitlog 打印当前补丁的简略列表,并与主版本相对照。这个脚本的不同版本可以显示补丁的 SHA1 id 或在一组补丁中搜索字符串。

《改进你 Bash 脚本的 5 种方法》

系统管理员通常编写各种或长或短的 Bash 脚本,以完成各种任务。Alan Formy-Duval 解释了如何使 Bash 脚本更简单、更健壮、更易于阅读和调试。我们可能会考虑到我们需要使用诸如 Python、C 或 Java 之类的语言来实现更高的功能,但其实也不一定需要。因为 Bash 脚本语言就已经非常强大。要最大限度地发挥它的效用,还有很多东西要学。

《我珍藏的 Bash 秘籍》

Katie McLaughlin 帮助你提高你的工作效率,用别名和其他快捷方式解决你经常忘记的事情。当你整天与计算机打交道时,找到可重复的命令并标记它们以方便以后使用是非常美妙的。Katie 总结了一些有用的 Bash 特性和帮助命令,可以节省你的时间。

这些 Bash 小技巧将一个已经很强大的 shell 提升到一个全新的级别。也欢迎分享你自己的建议。


via: https://opensource.com/article/21/1/bash

作者:Jim Hall 选题:lujun9972 译者:Chao-zhi 校对:wxy

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

编程一个简单的游戏是练习一门新语言并与其他你掌握的语言进行比较的好方法。

学习一门新的编程语言是很有趣的。每当我尝试学习一门新的语言时,我都会专注于定义变量、编写语句和评估表达式。一旦我对这些概念有了大致的了解,我通常可以自己弄清楚其余的概念。大多数编程语言都有一些相似之处,所以一旦你了解了一种编程语言,学习下一种编程语言就是要弄清楚其独特的细节,认识到其中的差异。

为了帮助我练习一种新的编程语言,我喜欢写一些测试程序。我经常写的一个示例程序是一个简单的“猜数字”程序,电脑在 1 到 100 之间选一个数字,让我猜这个数字。程序会一直循环,直到我猜对为止。

“猜数字”程序锻炼了编程语言中的几个概念:如何给变量赋值,如何写语句,如何进行条件判断和循环。对于学习一门新的编程语言来说,这是一个很好的实践实验。

用 Bash 猜数字

Bash) 是大多数 Linux 系统的标准 shell。除了提供丰富的命令行用户界面外,Bash 还以脚本的形式支持完整的编程语言。

如果你对 Bash 不熟悉,我推荐你看这些介绍:

你可以通过编写一个 Bash 版本的“猜数字”游戏来探索它。这是我的实现:

#!/bin/bash

number=$(( $RANDOM % 100 + 1 ))

echo "Guess a number between 1 and 100"

guess=0

while [ "0$guess" -ne $number ] ; do
        read guess
        [ "0$guess" -lt $number ] && echo "Too low"
        [ "0$guess" -gt $number ] && echo "Too high"
done

echo "That's right!"
exit 0

拆解这个脚本

脚本中的第一行,#!/bin/bash 告诉 Linux 使用 Bash shell 来运行这个脚本。每个脚本都以 #! 字符对(LCTT 译注:释伴)开始,这表示它是一个 shell 脚本。紧跟在#! 后面的是要运行的 shell。在本例中,/bin/bash 是指 Bash shell。

要给一个变量赋值,在变量名后面列出 = 号。例如,语句 guess=0guess 变量分配一个零值。

你也可以使用 read 语句提示用户输入一个值。如果你写了 read guess 语句,Bash 会等待用户输入一些文本,然后把这个值存储在 guess 变量中。

要引用一个变量的值,在变量名前使用 $。所以, 在 guess 变量中存储了一个值后, 你可以使用 $guess 来检索它。

你可以使用任何你喜欢的变量名称,但是 Bash 为自己保留了一些特殊的变量名称。一个特殊的变量是 RANDOM,每次引用它都会产生一个很大的随机数。

如果你想在存储一个值的同时执行一个操作,你需要用特殊的括号把语句括起来。这将告诉 Bash 先执行该语句,而 = 则将结果值存储在变量中。要评估一个数学表达式,使用 $(()) 围在你的语句上。双括号表示一个算术表达式。在我的例子中,number=$(( $RANDOM % 100 + 1 )) 评估表达式 $RANDOM % 100 + 1,然后将值存储在 number 变量中。

标准的算术运算符,如 +(加)、-(减)、*(乘)、/(除)和 %(模)都适用。

这意味着语句 number=$(( $RANDOM % 100 + 1 )) 产生一个 1 到 100 之间的随机数。模数运算符(%)返回两个数相除后的余数。在这种情况下,Bash 将一个随机数除以 100,剩下的余数范围是 0 到 99,通过在这个值上加 1,你可以得到一个介于 1 和 100 之间的随机数。

Bash 支持像循环这样的条件表达式流程控制。在“猜数字”的游戏中,只要 guess 中的值不等于 number,Bash 就会继续循环。如果猜的数小于随机数,Bash 就会打印“太低”,如果猜的数大于数字,Bash 就会打印“太高”。

它是如何工作的

现在你已经写好了你的 Bash 脚本,你可以运行它来玩“猜数字”游戏。一直猜,直到你找到正确的数字:

Guess a number between 1 and 100
50
Too high
30
Too high
20
Too high
10
Too low
15
Too high
13
Too low
14
That's right!

每次运行这个脚本,Bash 都会随机选择一个不同的数字。

这个“猜数字”游戏是学习新的编程语言时的一个很好的入门程序,因为它以一种很直接的方式锻炼了几个常见的编程概念。通过在不同的编程语言中实现这个简单的游戏,你可以展示一些核心概念,并比较每种语言的细节。

你有喜欢的编程语言吗?你会如何用它来写“猜数字”游戏呢?请关注本系列文章,看看你可能感兴趣的其他编程语言的例子。


via: https://opensource.com/article/20/12/learn-bash

作者:Jim Hall 选题:lujun9972 译者:wxy 校对:wxy

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

代码英雄讲述了开发人员、程序员、黑客、极客和开源反叛者如何彻底改变技术前景的真实史诗。

什么是《代码英雄》

代码英雄 Command Line Heroes 是世界领先的企业开源软件解决方案供应商红帽(Red Hat)精心制作的原创音频播客,讲述开发人员、程序员、黑客、极客和开源反叛者如何彻底改变技术前景的真实史诗。该音频博客邀请到了谷歌、NASA 等重量级企业的众多技术大牛共同讲述开源、操作系统、容器、DevOps、混合云等发展过程中的动人故事。

本文是《代码英雄》系列播客《代码英雄》第三季(6):Bash Shell 中的英雄音频脚本。

导语:Shell 使得大规模 IT 成为可能。它们是现代计算的必要组成部分。但是,如果没有 自由软件基金会 Free Software Foundation 一位名叫 Brian Fox 的开发者的辛勤工作,它可能不会变成这样。现在,世界上几乎每台电脑都有 Bash shell。

在上世纪 70 年代, 贝尔实验室 Bell Labs 希望将重复的、复杂的命令序列自动化。Chet Ramey 描述了贝尔实验室是如何开发出几个 shell 的 —— 但 UNIX 只能有一个官方支持的 shell。获选的是 Bourne shell。尽管 Bourne shell 是这些之中最好的一个 shell,但它也有其局限性。而且它只有在受到限制的 UNIX 许可证下才能使用。Brian J. Fox 讲述了他在自由软件基金会的工作,他需要创建一个自由的 Bourne shell 版本。它必须兼容但不使用任何原始源代码的元素。这个 Bourne-Again Shell,即 Bash,可能是这个星球上使用最广泛的软件。而 Taz Brown 描述了它是如何成为一个开发者可以学习使用的最重要的工具之一。

00:00:07 - Saron Yitbarek

那是 1987 年。里根总统治下的美国正蓬勃发展,一个怀揣远大梦想的人正驱车前往他位于 圣巴巴拉 Santa Barbara 的新家。这个人名叫 Brian Fox,27 岁,是高中辍学生。在他车的后备箱里,有两盒巨大的磁带,里面载满了他当时正在编写的代码。

00:00:28

Fox 多年来一直以程序员的身份工作在所谓的自由软件运动中。他相信他锁在这个后备箱里的代码,可以带来一场革命,这是一种全新的软件范例。他的社区正在一点一点地使之成为现实。

00:00:49

那年, 理查德•斯托曼 Richard Stallman (RMS)的 自由软件基金会 Free Software Foundation 的一组程序员,正在想尽办法给计算机界带来自由。他们想要构建一个 UNIX 的替代品,以取代自从 70 年代以来就主导编程的 UNIX 操作系统。他们的 GNU(表示 GNU's not UNIX)将成为公众的操作系统,任何人都可以使用它,无需担心许可费用或版权问题。

00:01:18

多年以来,基金会一直在努力制造这个崭新的系统。那么 Brian Fox 汽车后备箱里的那两盒装着代码的巨型磁带是什么?它们存储着这个系统一个至关重要的组成部分。这是一个自由的,而且可更改的 shell,它能够使 GNU 操作系统变得完整。这是 Brian Fox 送给自由软件运动的礼物。他称之为 Bash。

00:01:46

我是 Saron Yitbarek,这里是 代码英雄 Command Line Heroes ,一档来自 红帽 Red Hat 的原创播客节目。在这一集中,我们将来看看 Bash shell 中的英雄们。我们将探索 shell 的历史,以及它们为什么对我们如今的工作如此重要。大家可以将 shell 看作要给演员的剧本。它们提供了完整的命令序列,然后 shell 可以快速地运行,就像演员可以一行接一行地读她的台词一样。这是对于实现重复且复杂的代码的,是最终的解决方案,也是自动化的关键。你可能会说,shell 脚本是我们开发的一大助力。但是,是否可以编写一个,能给所有人带来帮助的 shell?这就是挑战所在。

00:02:38 - Ken Thompson

让我们回到 1969 年。那时候贝尔实验室的几位计算机科学家,正在根据自己的需求开发程序。

00:02:48 - Saron Yitbarek

这位是代码英雄先驱 Ken Thompson。由贝尔实验室设计的 UNIX 操作系统,在一开始确实是供他们个人使用的。最初,它只是一个内部系统。UNIX 鼓励程序员之间进行密切的交流,不过目的并不是要改变整个世界,而是改变贝尔实验室。

00:03:13 - Ken Thompson

到现在,几乎整个贝尔实验室都在使用这个系统。我们公司拥有近两万个计算机终端,其中大多数使用 UNIX 系统。

00:03:25 - Saron Yitbarek

一款由 Ken Thompson 所设计的 UNIX shell 在 1971 年发布。 虽然 Thompson shell 被设计为命令行解释器,但是它却不能很好地支持脚本。所以直到六年后的 1977 年,脚本才开始兴起。

00:03:44 - Chet Ramey

Shell 参数、特殊参数以及我们如今认为理所当然的变量,起源于 Steve Bourne 和 Bourne shell。

00:03:57 - Saron Yitbarek

这位是 Chet Ramey,Case Western Reserve 大学的 IT 架构师。Chet 致力于维护 Bash,他也为我们讲述了许多 Bash 的起源故事。他描述了贝尔实验室当时研究 UNIX shell 样子时的情景。

00:04:13 - Chet Ramey

我们如今使用的编程结构起源于 Steve Bourne,他的 shell 赢得了这场比赛。当时有大量使用 Mashey shell 的用户社区,也有大量用户开始使用 Bourne shell。那时候成立了一个委员会来决定哪一个将会获胜,哪一个将会成为从那时候起得到官方支持的 UNIX shell, Bourne 的 shell 赢了。而其他的 shell,正如他们所说,成为了历史。

00:04:54 - Saron Yitbarek

不过,这还不是历史的终结。当然,Bourne shell 是一个巨大的飞跃。它打开了一扇通向更高自动化水平的大门。但是尽管有一段时间 Bourne 占据了上风,但是 Bourne shell 并不能解决我们所有的脚本需求。

00:05:14 - Chet Ramey

Bourne 撰写自己的 shell 时所受到的限制,几乎是现在的你我难以想象的。显然,当你遇到这些限制时,你不得不放弃很多东西,Bourne 就放弃了很多。考虑到他所处理的空间、内存和 CPU 限制,他能够让 Bourne shell 包含那么多东西,这相当了不起。

00:05:42 - Saron Yitbarek

请记住,Bourne shell 仍然是贝尔实验室 UNIX 系统的一部分。它仍然与 UNIX 许可证绑定。这意味着它不是自由的,不是开放的。这款 shell 是私有的。

00:05:55 - Chet Ramey

如果你不在大学里,获取 UNIX 源码将会非常困难。显然,这对 Berkeley UNIX 的普及产生了影响。Berkeley UNIX 始于大学中,在大学社区中成长,并走了一条阻力最小的道路。因此,如果你在正确的地方,访问到 Bourne shell 的源码并不困难,但是总的来说,这并不是大众都能够认可的方案。

00:06:36 - Saron Yitbarek

Chet Ramey 是 Bash shell 的维护者。

00:06:41

因此,我们有了 shell 的雏形,可以着手写这些关键的组成部分,但是目前为止,最好的 shell 的许可证却有个大大的问题,它是闭源的。对于理查德•斯托曼和他的自由软件基金会而言,这是绝对无法接受的事情。我们所需要的是一个不与任何公司绑定的 shell,一个面向所有人的 shell。

00:07:05

但这就带来了问题。这意味着我们需要编写某种,能做到 Bourne shell 所能做到的一切,而又不会侵犯到版权的东西。如果逐字复制 Bourne shell 的代码,你会被起诉。

00:07:20

为了使人们摆脱 Bourne shell 的束缚,你必须找到一位能在没看过 Bourne shell 任何源代码的情况下,编写这款复杂程序的程序员。你必须找到这样的一位局外人天才。而理查德•斯托曼找到了完成这项工作的程序员。

00:07:46

Brian Fox 是一名 20 来岁的高中辍学生,比贝尔实验室的大多数人更懂代码。他从来没有见过任何 Bourne shell 的源代码,这使得他非常适合手头的任务。

00:08:02 - Brian Fox

我是 Brian Fox。

00:08:04 - Saron Yitbarek

为什么不直接问问这个年轻人,这个故事是什么样的呢?现如今,Fox 是一位开源倡导者以及 Opus Logica 的 CEO。但是早在 80 年代后期,他只是一个信仰开源软件运动的年轻人。我们聊了聊过去的日子,以及 Bash 是如何从那时演变过来的。

00:08:23

所以那时候理查德•斯托曼请你为 UNIX 开发一款 shell。那将会是一款自由的 shell,并且是 Bourne shell 的替代品。你是如何回应的呢?

00:08:38 - Brian Fox

“我们就不能做个更棒的吗?”

00:08:41 - Saron Yitbarek

我喜欢这个。再多跟我说说。

00:08:45 - Brian Fox

我为斯托曼所做的第一件事,其实就是编写个信息技术文档系统。我让理查德惊讶于我做这种编程的速度。他是个优秀的程序员而且工作的很快,但是他不认为其他人也能写得那么快。

00:09:00 - Brian Fox

因此,在第一周内,我完成了一款名为 GNU Info 的程序的第一版实现,理查德对此有点儿震惊。我说:“我的下一个项目是什么?我的下一个项目是什么?”他说:“好吧,现在给它做个编译器吧。”我就做了,一周时间之内就完成了。然后我说:“我的下一个项目是什么?我的下一个项目是什么?”他说:“好吧,另一个家伙一直在研究那个 shell,但他还没有太多进展。”我说了“好的”,九个月后, Bourne shell 的替代品完成了。

00:09:29 - Saron Yitbarek

九个月,哇。再多告诉我一些。为什么它如此具有挑战性?

00:09:33 - Brian Fox

这真是个有趣的问题。它之所以如此具有挑战性,是因为我们必须忠实地模仿 Stephen Bourne 最初的 Bourne shell 的所有行为,同时对其进行扩展,让它成为人们能使用的、更好的工具。

00:09:51

那时候,我和 Korn shell 的作者 David Korn 私下进行了秘密争论。POSIX 委员会,也就是规定了什么是标准 UNIX 的委员会,他们也参与了进来,并说:“哦,很好,我们需要知道 shell 到底要包含些什么。”而这方面最重要的两个人是我和 David Korn。David Korn 已经写了一个名为 KSH 的 shell。对于他所加入到 KSH 中的每一个功能,他都说:“这应该是一个标准功能。”是这样吗?对他来说这比起拥有最完美的 POSIX shell 要容易得多,如果这仅仅是他的 shell 的话。

00:10:31

其中的一些功能并不是很好的功能,不是很好的选择,而且使得这款 shell 与 Bourne shell 有些不兼容,或者我觉得缺少功能,对此我们进行了一些讨论和争论,因此构建一个兼容 POSIX 的 shell 与过去为 Bourne shell 所编写的每个 shell 脚本都完全兼容花了超过 3 个月时间。

00:10:54 - Saron Yitbarek

因此,如果你正在设计的产品不仅可以取代 Bourne shell,而且还试图模仿 Bourne shell 的每个部分,听起来你可能会遇到一些版权问题。你是如何处理的?

00:11:08 - Brian Fox

为了构建真正开源而自由的软件,你必须得在一个干净的空间里,开始做这项工作。你不能从查看别人的代码开始然后重新实现它。因此,我从未见过与任何贝尔的系统、UNIX 或者甚至 Berkeley UNIX 相关的任何软件,也从未见过这些东西的源代码。

00:11:29

当我开始构建 Bash shell 时,我使用了一个名为 Bison 的解析器,理查德已经将其整合到自由软件基金会里,并且与之前任何的其他程序完全不同。因此,我已经知道我所要构建的东西,绝对不会侵犯任何先前构建的东西的版权。

00:11:55 - Saron Yitbarek

创建 Bash 的工作有很多小插曲,对于那些硬核的代码英雄来说,这只是其中一个例子。

00:12:03 - Brian Fox

有一次,我正致力于在 shell 中实现 通配扩展 globbing 。举例来说,这是允许你匹配大量文件的通配符扩展。你可以给出 *.c,而这会匹配所有带有 .c 扩展名的文件。

00:12:17

因此我在通配扩展上忙活了几个小时,并且使其生效了,对此我感到很兴奋。这是一个很好的实现。而在创建这一版实现的过程中,我在我的目录里创建了一个名为 *.c 的文件,然后我想:“好吧,我应该删掉这个文件”,然后我输入了 RM、空格、引号、星号点 C、闭合引号,在现代 shell 中当你使用了括号,这意味着“不要扩展这个”,然后我按下了回车,提示符过了很长时间才重新出现,因为我们正在使用 Sun 350s,运行缓慢。我意识到,之所以花了很长时间是因为它要删除这个目录里的所有源文件。

00:12:58 - Saron Yitbarek

哦,不!

00:12:59 - Brian Fox

是的。所以我当时删掉了 Bash 的源代码。

00:13:01 - Saron Yitbarek

哦,不要。

00:13:04 - Brian Fox

这 ——

00:13:05 - Saron Yitbarek

哦我的天哪,嗯。

00:13:06 - Brian Fox

这件事让我笑了很久,笑的很大声。我甚至没有感到一丝沮丧。然后在接下来的几天里,我重新输入了全部。这份代码在我脑海里是完全是崭新的。

00:13:20 - Saron Yitbarek

哇。

00:13:20 - Brian Fox

问题解决了。只需将其记录到文件中即可。

00:13:25 - Saron Yitbarek

好的。因此,大多数人会在那一刻完全惊慌失措。而你笑了,只是说:“哦,我想我必须重新做一遍了。”为什么你当时那么冷静呢?

00:13:35 - Brian Fox

这让我感到疯狂很荒唐,也非常好笑,我正在打造这个工具,而要确保自己能搞好,确保该工具正常工作,你得在构建它的过程中就使用它。但是该工具无法正常工作。我还没有实现引号,并且因为我还没实现引号,所以我输入的命令没有按照我所预期的去执行,我觉得这真的很滑稽。

00:14:06 - Saron Yitbarek

太神奇了。

00:14:08

不过,甚至是关于错误的这个故事也能说明 Fox 的才华。他们说莫扎特在头脑中完成了交响曲,然后只需要在完成后写下来即可。Fox 也有类似的天赋。

00:14:23

因此,当你最终完成并交付 Bash 时,感觉如何呢?

00:14:27 - Brian Fox

呵,其实感觉很壮观。那么这里有一个故事,其实我一般不讲的。构建这款 shell 花了大约 8 个月的时候,当时我知道,我大概还需要大约一个月时间才能完成工作,然后另一个 shell 发布了 —— ASH,一个开源的 shell 被发布了,我很沮丧,因为我们还没有向任何人发布 Bash shell,所以只有少数人在使用它。我知道这还需一个月的工作量,于是我想:“哦,这太糟糕了。我投入的全部能量和精力都不会得到赞许,甚至可能都不会被看见。”所以我非常沮丧。这次我没有笑。

00:15:13 - Saron Yitbarek

然而,布丁好不好,吃了才知道。GNU 的 Bash 发布于 1989 年并且变成了 Linux 的默认 shell。如今,它是计算机中不可或缺的一部分。

00:15:25

它无处不在。如此多的人每天都在使用它。它遍布于每一台计算机上。作为 Bash 的作者感觉如何?

00:15:34 - Brian Fox

大多数时候,我甚至都没有注意到 Bash 是比工具更加重要的东西。我真的没有经常想这件事。每隔一段时间,我会走进一家苹果商店,环顾四周然后想:“哇,这里的每台计算机不仅运行着我 27 年前编写的软件,甚至上面还包含有我的名字。”然后我想:“互联网上的每台计算机、每台服务器都在运行着 Bash shell,并且其中包含有我的名字。”然后 Windows 在去年还是前年推出了 Power shell,就是 Bash,当时我想:“哦,天哪。我的名字遍及地球上的每台计算机了。”

00:16:21 - Saron Yitbarek

不过,我想让你们能仔细听听 Fox 接下来告诉我的内容,因为它是很重要。他从未想过,它的程序会这样统治全球。他试图提供帮助,试图帮助他所置身其中的编程文化。

00:16:37 - Brian Fox

我并没有打算去实现出现在每个人的计算机上这样的宏伟目标。我对此一点都不感兴趣。我想制作一款有用的软件,我希望它有典型的 3 到 5 年软件寿命,而不是像现在这样疯狂的 30 年的寿命。

00:16:58 - Saron Yitbarek

难道你对于你在计算机领域有如此巨大影响力的事情,一直反应那么平淡吗?

00:17:06 - Brian Fox

我为自己写了 Bash 而感到骄傲,而且它让我意识到了我的价值,所以有时候我会做一些事情,诸如接受播客邀约谈论 shell 之类的事情。

00:17:14 - Saron Yitbarek

非常感谢你。

00:17:15 - Brian Fox

谢谢。但这不是存在于我日常生活中的东西。幸运的是,我只是一个默默无闻的人,对吧?的确,我的软件正运行在每家每户的计算机上,不过也确实没有人知道这一点,对吧?因此我保持了许多个人隐私,而这个 shell 以及某个住在圣芭芭拉的人编写了它这一事实正越来越广为人知,我开始在生活中越来越多地注意到它。人们有时候来看我演奏音乐,然后告诉我说:“你是写了 shell 的那个家伙。”我感觉有点儿像 Keanu Reeves。

00:17:54 - Saron Yitbarek

很酷。所以你说过你不指望 Bash 出现在每台计算机上。你打算做的是什么呢?你对 Bash 有什么期望?

00:18:04 - Brian Fox

一个有用的替代工具,成为 GNU 项目的一部分,并帮助创建这个自由的开源操作系统。我实际上以为一旦我们完成了该开源操作系统的创建,该系统上的软件就可以升级,并且我将有机会创建自己想要创建的那种 shell,以帮助人们在某种程度上促进计算机科学的发展。

00:18:35 - Brian Fox

我最终意识到,Bash 被创建的原因实际上是与已经存在的 UNIX 世界向后兼容,并且这种势头使其保持了活力,这是另一个独一无二的地位,你的工具如此基础,几乎是一副不可或缺的螺母和螺栓。

00:19:01 - Saron Yitbarek

确实是这样。

00:19:01 - Brian Fox

知道我创造了世界上某种有价值的、别人仍然还在使用的东西,这真的是一种很棒的感觉。然后当我注意到这是怎么回事时,我意识到,更重要的是,“自由软件”和“开源”这些词存在于日常英语和全世界的日常语言之中了,而最初并不是这样的。这是我和理查德•斯托曼还有其他人所投入努力的产物。作为这一运动的一部分,我很幸运能这么早参与,但让我回过头来看时,也感到非常满意,我想:“哇,开源软件已经存在,而且我就是其中的一部分。”

00:19:50 - Saron Yitbarek

Brian Fox 是 Bash shell 的创建者和 Opus Logica 的 CEO。

00:20:01 - Steve Bourne

事实上,我确实听说过 Bash。

00:20:03 - Saron Yitbarek

这位就是被 Brian Fox 的工作所替代的 Bourne shell 的创建者 Steve Bourne。我们想知道 Bourne 对 Fox 的工作有何看法。他是否将重生的 shell Bash 视为自己作品的开源复制品?我的意思是,他觉得 Bash 怎么样?

00:20:20 - Steve Bourne

有一天,写了 Bash 的那个人在一次会议上找我,给了我一件 T 恤,前面印着 “Bourne again” 的字样。

00:20:26 - Saron Yitbarek

那就是 Brian Fox。

00:20:29 - Steve Bourne

那是一种友好的情绪,当时是:“好吧,希望您不介意,但我只是重写了您的 shell”,而我说:“听起来不错”,然后他给了我一件 T 恤。

00:20:38 - Saron Yitbarek

如果我在编程领域学到了一件事,那就是每个人都喜欢意外之喜。事实证明,Stephen Bourne 认为 Bash 是他和其他人在贝尔实验室所做工作的必要扩展。一点儿都不为此苦恼。

00:20:52 - Steve Bourne

曾经有一些人们想要,但是我没做的特性,例如变量替换和字符串管理,但是这些都被加入到了 Bash 中,现如今人们经常会用到。Bash 和原始 shell 之间的关系,我当时的印象是,它只是对语言的重新实现,并且随着时间的推移,它确实添加了功能,因此它确实取得了超越我所写作品的进步,当然是在字符串管理领域。我现在一直在用它。

00:21:21 - Saron Yitbarek

Steve Bourne 是 Bourne shell 的创建者和 Rally Ventures 的 CTO。

00:21:32 - Saron Yitbarek

自从 Bash 在前往圣芭芭拉的长途车程中被塞进 Brian Fox 的卡车以来,已经过去了很多年。 2019 年,版本 5.0 被发布,就像 Fox 提到的那样,Bash 现在被内置进了 Linux 中、macOS 中,甚至微软 Windows 中。Bash 已经成了开源世界中脚本编写的基石。这是我们自动化的基础。

00:22:02 - Taz Brown

随着组织规模的扩大,使用能够使我们更快完成工作的工具变得至关重要。它成为了必需品。

00:22:16 - Saron Yitbarek

Taz Brown 是 Red Hat 的资深 Ansible 自动化顾问,因此她非常了解 Bash 的价值。

00:22:24 - Taz Brown

我绝对认为人们在职业生涯初期就应该使用 Bash。与其使用 GUI 或者说是图形用户界面,不如将自己视为管理员或 DevOps 人员。

00:22:39 - Saron Yitbarek

而这是因为作为一名 Bash 程序员,你将会掌握能让你晋升的核心价值。

00:22:45 - Taz Brown

学习写脚本有一定的价值,因为这可以让你从自动化的角度,为程序的长期运行做打算。你可以看到脚本的运行方式,然后可以说:“好吧,我可以做到,我可以使这项任务自动化执行。”它开始使你成为与之前不一样的思想家和技术专家。

00:23:09 - Saron Yitbarek

对于运维而言,自动化已经变得不可或缺。复杂的程序、应用和工具均由优雅的 Bash 代码实现。

00:23:21 - Taz Brown

如果你愿意的话,你不必重复造轮子。你可以从 GitHub 库或是其他任何你存储这些特定文件的地方拉取它们。Bash 允许你这么做。Bash 允许你执行这些常见任务,并且可以从 10 台服务器扩展到 1000 台服务器。

00:23:42

关于自动化的伟大之处在于,一旦你制定了计划,就可以以一种非常有效的方式执行。它允许你执行那些,无法手动执行的操作。

00:23:56 - Saron Yitbarek

最近 Taz Brown 所从事开发的 Ansible® 这样的最新产品可以始终与 Bash 集成在一起,完成了工作。

00:24:04 - Taz Brown

虽然时代在不停前进,但是我认为 Bash 永远都会是管理员会去选择使用的工具,特别是他们想要快速自动化的情况下。

00:24:14 - Saron Yitbarek

最后,这一切的成功,都可以追溯到它是一个自由的、允许所有人加以改进的软件这件事上。它是 Brian Fox 提供给世界的,某种没有许可证和限制的东西。满足了人们一直的需求,所以是 Bash 成功的关键。实际上,他甚至已经不再主管 Bash 开发已经很长一段时间了。这位是 Chet Ramey,他维护了 Bash 数十年。

00:24:38 - Chet Ramey

我想,Brian 在发布 1.05 版本后就已经决定了他想要继续去从事其他工作。他曾在自由软件基金会负责过其他任务,他想做除了 Bash 以外的事情,而我是 Bash 最活跃的贡献者。他和我一起开发了许多新功能。我们共同努力解决了许多 bug,因此当到了需要其他人接手时,我是最佳人选。

00:25:16 - Saron Yitbarek

就像 Fox 一样,Ramey 也必须继续努力,因为 Bash 比任何一位维护者都重要。

00:25:25 - Chet Ramey

我是从 23 岁开始贡献的,有点儿像是我和 Bash 共同成长。在某些时刻,我会需要征集一个团队。我需要征集那些愿意并且有能力投入时间推动 shell 发展向前的人们。

00:25:46 - Saron Yitbarek

Bash,这款再次降生的 shell 明年将迎来 30 岁(LCTT 译注:Bash 发布于 1989 年,至本译文发表时,已经 31 岁了),并且没有衰老的迹象。Bash 乘着自由软件浪潮,然后是开源浪潮,直到传播至编程世界的每个角落。但曾经,它只是存储在 Brian Fox 汽车后备箱里磁带上的代码。它只是一些程序员,想要带给大家的 shell 语言。几乎偶然的,Brian Fox 在此过程中成为了一名伟大的代码英雄。

00:26:23

顺便说一句,有些事情始终困扰着我, Brian Fox 驱车将所有 Bash 代码载到了 Santa Barbara。为什么要转移呢?我的意思是,他在某家科技公司找到了新工作吗?

00:26:34 - Brian Fox

我想要继续我的音乐生涯,而我认为做到这一点的最佳去处就是气温总在 72 华氏度左右、天空没有乌云、海滩很美的地方。

00:26:45 - Saron Yitbarek

很好,我更喜欢这个理由。

00:26:49

现在让我们向 Wayne A. Lee 致敬,是他向我们建议了这一集标题《Bash Shell 中的英雄》。干得好,Wayne。

00:26:57

在下一集中,我们对于自动化的兴趣,将提升到一个全新的高度,并且着眼于 AI 语言,特别是 John McCarthy 创造的 LISP。

00:27:11

《代码英雄》是 Red Hat 的原创播客节目。如果你访问节目的网站 redhat.com/commandlineheroes,你将更深入了解到有关 Bash 或是我们本季所介绍的任何编程语言的故事。

00:27:28 - Saron Yitbarek

我是 Saron Yitbarek。下棋之前,坚持编程。

什么是 LCTT SIG 和 LCTT LCRH SIG

LCTT SIG 是 LCTT 特别兴趣小组 Special Interest Group ,LCTT SIG 是针对特定领域、特定内容的翻译小组,翻译组成员将遵循 LCTT 流程和规范,参与翻译,并获得相应的奖励。LCRH SIG 是 LCTT 联合红帽(Red Hat)发起的 SIG,当前专注任务是《代码英雄》系列播客的脚本汉化,已有数十位贡献者加入。敬请每周三、周五期待经过我们精心翻译、校对和发布的译文。

欢迎加入 LCRH SIG 一同参与贡献,并领取红帽(Red Hat)和我们联合颁发的专属贡献者证书。


via: https://www.redhat.com/en/command-line-heroes/season-3/heroes-in-a-bash-shell

作者:Red Hat 选题:bestony 译者:JonnieWayy 校对:acyanbird, wxy

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