标签 Bash 下的文章

欢迎来到面向初学者的 Bash Shell 脚本知识第三部分。这最后一篇文章将再来学习一些知识点,这些将使你为持续的个人发展做好准备。它将涉及到函数、用 if/elif 语句进行比较,并以研究 while 循环作为结尾。

函数

让我们从一个看似困难但其实很简单的基本概念开始,即函数。把它看作是一种简单的方法,可以把脚本中被反复使用的部分放到一个可重复使用的组中。你在本系列第一篇或第二篇文章中所做的任何事情都可以放在一个函数中。因此,让我们把一个函数放到我们的 learnToScript.sh 文件中。让我指出几个关键点。你将需要为你的函数起一个名字、一对小括号,以及用大括号包围放在你的函数中的命令。

#!/bin/bash

#A function to return an echo statement.
helloFunc() {
        echo "Hello from a function."
}

#invoke the first function helloFunc()
helloFunc

你会看到如下输出结果:

[zexcon@fedora ~]$ ./learnToScript.sh
Hello from a function.
[zexcon@fedora ~]$

函数是重复使用一组命令的好方法,但如果你能使它们在每次使用时对不同的数据进行操作,它们会更加有用。这就要求你在每次调用函数时提供数据,这称为参数。

要提供参数,你只需在调用函数时把它们加在函数名称后面。为了使用你提供的数据,你在函数命令中使用位置来引用它们。它们将被命名为 $1$2$3,以此类推,这取决于你的函数将需要多少个参数。

让我们修改上一个例子来帮助更好地理解这个问题。

#!/bin/bash

#A function to return an echo statement.
helloFunc() {
        echo "Hello from a function."
        echo $1
        echo $2
        echo "You gave me $# arguments"
}

#invoke the function helloFunc()
helloFunc "How is the weather?" Fine

输出如下:

Hello from a function.
How is the weather?
Fine
You gave me 2 arguments

输出中发生的事情是 helloFunc() 在每一行都做了一个回显。首先它回显了一个 Hello from a function,然后它继续回显变量 $1 的值,结果是你传递给 helloFunc"How is the weather?"。然后它将继续处理变量 $2,并回显其值,这是你传递的第二个项目:Fine。该函数将以回显 You gave me $# arguments 结束。注意,第一个参数是一个用双引号括起来的单个字符串 "How is the weather?"。第二个参数 Fine 没有空格,所以不需要引号。

除了使用 $1$2 等之外,你还可以通过使用变量 $# 来确定传递给函数的参数数量。这意味着你可以创建一个接受可变参数数量的函数。

关于 bash 函数的更多细节,网上有很多好的参考资料。这里有一个可以让你入门的资料

我希望你能了解到函数如何在你的 bash 脚本中提供巨大的灵活性。

数值比较 []

如果你想进行数字比较,你需要在方括号 [] 中使用以下运算符之一:

  • -eq (等于)
  • -ge (等于或大于)
  • -gt (大于)
  • -le (小于或等于)
  • -lt (小于)
  • -ne (不相等)

因此,举例来说,如果你想看 12 是否等于或小于 25,可以像 [ 12 -le 25 ] 这样。当然,12 和 25 可以是变量。例如,[ $twelve -le $twentyfive ]。(LCTT 译注:注意保留方括号和判断语句间的空格)

if 和 elif 语句

那么让我们用数字比较来介绍 if 语句。Bash 中的 if 语句将以 if 开始, 以 fi 结束。if 语句 以 if 开始,然后是你想做的检查。在本例中,检查的内容是变量 numberOne 是否等于 1。如果 numberOne 等于 1,将执行 then 语句,否则将执行 else 语句。

#!/bin/bash

numberTwelve=12

if [ $numberTwelve -eq 12 ]
then
        echo "numberTwelve is equal to 12"
elif [ $numberTwelve -gt 12 ]
then
        echo "numberTwelve variable is greater than 12"
else
        echo "neither of the statemens matched"
fi

输出如下:

[zexcon@fedora ~]$ ./learnToScript.sh
numberTwelve variable is equal to 12

你所看到的是 if 语句的第一行,它在检查变量的值是否真的等于 12。如果是的话,语句就会停止,并发出 numberTwelve is equal to 12 的回显,然后在 fi 之后继续执行你的脚本。如果变量大于 12 的话,就会执行 elif 语句,并在 fi 之后继续执行。当你使用 ifif/elif 语句时,它是自上而下工作的。当第一条语句是匹配的时候,它会停止并执行该命令,并在 fi 之后继续执行。

字符串比较 [[]]

这就是数字比较。那么字符串的比较呢?使用双方括号 [[]] 和以下运算符等于或不等于。(LCTT 译注:注意保留方括号和判断语句间的空格)

  • = (相等)
  • != (不相等)

请记住,字符串还有一些其他的比较方法,我们这里不会讨论,但可以深入了解一下它们以及它们是如何工作的。

#!/bin/bash

#variable with a string
stringItem="Hello"

#This will match since it is looking for an exact match with $stringItem
if [[ $stringItem = "Hello" ]]
then
        echo "The string is an exact match."
else
        echo "The strings do not match exactly."
fi

#This will utilize the then statement since it is not looking for a case sensitive match
if [[ $stringItem = "hello" ]]
then
        echo "The string does match but is not case sensitive."
else
        echo "The string does not match because of the capitalized H."
fi

你将得到以下三行:

[zexcon@fedora ~]$ ./learnToScript.sh
The string is an exact match.
The string does not match because of the capitalized H.
[zexcon@fedora ~]$

while 循环

在结束这个系列之前,让我们看一下循环。一个关于 while 循环的例子是:“当 1 小于 10 时,在数值上加 1”,你继续这样做直到该判断语句不再为真。下面你将看到变量 number 设置为 1。在下一行,我们有一个 while 语句,它检查 number 是否小于或等于 10。在 dodone 之间包含的命令被执行,因为 while 的比较结果为真。所以我们回显一些文本,并在 number 的值上加 1。我们继续执行,直到 while 语句不再为真,它脱离了循环,并回显 We have completed the while loop since $number is greater than 10.

#!/bin/bash

number=1

while [ $number -le 10 ]
do
        echo "We checked the current number is $number so we will increment once"
        ((number=number+1))
done
        echo "We have completed the while loop since $number is greater than 10."

while 循环的结果如下:

[zexcon@fedora ~]$ ./learnToScript.sh
We checked the current number is 1 so we will increment once
We checked the current number is 2 so we will increment once
We checked the current number is 3 so we will increment once
We checked the current number is 4 so we will increment once
We checked the current number is 5 so we will increment once
We checked the current number is 6 so we will increment once
We checked the current number is 7 so we will increment once
We checked the current number is 8 so we will increment once
We checked the current number is 9 so we will increment once
We checked the current number is 10 so we will increment once
We have completed the while loop since 11 is greater than 10.
[zexcon@fedora ~]$

正如你所看到的,实现这一目的所需的脚本量要比用 if 语句不断检查每个数字少得多。这就是循环的伟大之处,而 while 循环只是众多方式之一,它以不同的方式来满足你的个人需要。

总结

下一步是什么?正如文章所指出的,这是,面向 Bash Shell 脚本初学者的。希望我激发了你对脚本的兴趣或终生的热爱。我建议你去看看其他人的脚本,了解你不知道或不理解的地方。请记住,由于本系列每篇文章都介绍了数学运算、比较字符串、输出和归纳数据的多种方法,它们也可以用函数、循环或许多其他方法来完成。如果你练习所讨论的基础知识,你将会很开心地把它们与你还要学习的所有其他知识结合起来。试试吧,让我们在 Fedora Linux 世界里见。


via: https://fedoramagazine.org/bash-shell-scripting-for-beginners-part-3

作者:Matthew Darnell 选题:lujun9972 译者:wxy 校对:wxy

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

欢迎来到面向初学者的 Bash Shell 脚本知识第二部分。本篇将就 Bash 脚本一些更独特的方面进行深入探讨。我们会用到一些 上篇 中已经熟悉的命令(如果遇到新命令,会给出讲解),进而涵盖一些标准输出、标准输入、标准错误、“管道”和数据重定向的相关知识。

使用 # 添加注释

随着脚本变得愈加复杂和实用,我们需要添加注释,以便记住程序在做什么。如果与其他人分享你的脚本,注释也将帮助他们理解思考过程,以及更好理解你的脚本实现的功能。想一想上篇文章中的数学方程,我们在新版脚本中添加了一些注释。注意,在 learnToScript.sh 文件(如下所示)中,注释是前面带有 # 号的行。当脚本运行时,这些注释行并不会出现。

#!/bin/bash

#Let's pick up from our last article. We
#learned how to use mathematical equations
#in bash scripting.

echo $((5+3))
echo $((5-3))
echo $((5*3))
echo $((5/3))
[zexcon ~]$ ./learnToScript.sh
8
2
15
1

管道符 |

我们将使用另一个名为 grep 的工具来介绍管道运算符。

grep 可以在输入文件中搜索可以匹配指定模式的行。默认情况下,grep 会输出相应的匹配行。

https://www.gnu.org/software/grep/

Paul W. Frields 在 《Fedora 杂志》上的文章很好地介绍了关于 grep 的知识。

命令行快速小技巧:使用 grep 进行搜索

管道键在键盘上位于回车键上方,可以在英文状态下按 Shift + \ 输入。

现在你已经略微熟悉了 grep,接下来看一个使用管道命令的示例。在命令行输入 ls -l | grep learn

[zexcon ~]$ ls -l | grep learn
-rwxrw-rw-. 1 zexcon zexcon   70 Sep 17 10:10 learnToScript.sh

通常 ls -l 命令会在屏幕上显示文件列表。这里 ls -l 命令的完整结果通过管道传送到搜索字符串 learngrep 命令中。你可以将管道命令想象成一个过滤器。先运行一个命令(本例中为 ls -l,结果会给出目录中的文件),这些结果通过管道命令给到 grep,后者会在其中搜索 learn,并且只显示符合条件的目标行。

下面再看一个例子以巩固相关知识。less 命令可以让用户查看超出一个屏幕尺寸的命令结果。以下是命令手册页中关于 less 的简要说明。

less 是一个类似于 more 的程序,但它允许在文件中向后或向前进行翻页移动。此外,less 不必在开始之前读取整个输入文件,因此对于大型输入文件而言,它比 vi 等文本编辑器启动更快。该命令较少使用 termcap(或某些系统上的 terminfo),因此可以在各种终端上运行。甚至还在一定程度上支持用于硬拷贝终端的端口。(在硬拷贝终端上,显示在屏幕顶部的行会以插入符号为前缀。)

Fedora 34 手册页

下面让我们看看管道命令和 less 命令结合使用会是什么样子。

[zexcon ~]$ ls -l /etc | less
total 1504
drwxr-xr-x. 1 root root       126 Jul  7 17:46 abrt
-rw-r--r--. 1 root root        18 Jul  7 16:04 adjtime
-rw-r--r--. 1 root root      1529 Jun 23  2020 aliases
drwxr-xr-x. 1 root root        70 Jul  7 17:47 alsa
drwxr-xr-x. 1 root root        14 Apr 23 05:58 cron.d
drwxr-xr-x. 1 root root         0 Jan 25  2021 cron.daily
:
:

为便于阅读,此处对结果进行了修剪。用户可以使用键盘上的箭头键向上或向下滚动,进而控制显示。如果使用命令行,结果超出屏幕的话,用户可能会看不到结果的开头行。要退出 less 屏幕,只需点击 q 键。

标准输出(stdout)重定向 >、>>、1>、1>>

>>> 符号之前的命令输出结果,会被写入到紧跟的文件名对应的文件中。>1> 具有相同的效果,因为 1 就代表着标准输出。如果不显式指定 1,则默认为标准输出。>>1>> 将数据附加到文件的末尾。使用 >>> 时,如果文件不存在,则会创建对应文件。

例如,如果你想查看 ping 命令的输出,以查看它是否丢弃了数据包。与其关注控制台,不如将输出结果重定向到文件中,这样你就可以稍后再回来查看数据包是否被丢弃。下面是使用 > 的重定向测试。

[zexcon ~]$ ls -l ~ > learnToScriptOutput

该命令会获取本应输出到终端的结果(~ 代表家目录),并将其重定向到 learnToScriptOutput 文件。注意,我们并未手动创建 learnToScriptOutput,系统会自动创建该文件。

total 128
drwxr-xr-x. 1 zexcon zexcon   268 Oct  1 16:02 Desktop
drwxr-xr-x. 1 zexcon zexcon    80 Sep 16 08:53 Documents
drwxr-xr-x. 1 zexcon zexcon     0 Oct  1 15:59 Downloads
-rw-rw-r--. 1 zexcon zexcon   685 Oct  4 16:00 learnToScriptAllOutput
-rw-rw-r--. 1 zexcon zexcon    23 Oct  4 12:42 learnToScriptInput
-rw-rw-r--. 1 zexcon zexcon     0 Oct  4 16:42 learnToScriptOutput
-rw-rw-r--. 1 zexcon zexcon    52 Oct  4 16:07 learnToScriptOutputError
-rwxrw-rw-. 1 zexcon zexcon   477 Oct  4 15:01 learnToScript.sh
drwxr-xr-x. 1 zexcon zexcon     0 Jul  7 16:04 Videos

标准错误(stderr)重定向 2>2>>

>>> 符号之前命令的错误信息输出,会被写入到紧跟的文件名对应的文件中。2>2>> 具有相同的效果,但 2>> 是将数据追加到文件末尾。你可能会想,这有什么用?不妨假象一下用户只想捕获错误信息的场景,然后你就会意识到 2>2>> 的作用。数字 2 表示本应输出到终端的标准错误信息输出。现在我们试着追踪一个不存在的文件,以试试这个知识点。

[zexcon ~]$ ls -l /etc/invalidTest 2> learnToScriptOutputError

这会生成错误信息,并将错误信息重定向输入到 learnToScriptOutputError 文件中。

ls: cannot access '/etc/invalidTest': No such file or directory

所有输出重定向 &>、&>>、|&

如果你不想将标准输出(stdout)和标准错误信息(stderr)写入不同的文件,那么在 Bash 5 中,你可以使用 &> 将标准输出和标准错误重定向到同一个文件,或者使用 &>> 追加到文件末尾。

[zexcon ~]$ ls -l ~ &>> learnToScriptAllOutput
[zexcon ~]$ ls -l /etc/invalidTest &>> learnToScriptAllOutput

运行这些命令后,两者的输出都会进入同一个文件中,而不会区分是错误信息还是标准输出。

total 128
drwxr-xr-x. 1 zexcon zexcon   268 Oct  1 16:02 Desktop
drwxr-xr-x. 1 zexcon zexcon    80 Sep 16 08:53 Documents
drwxr-xr-x. 1 zexcon zexcon     0 Oct  1 15:59 Downloads
-rw-rw-r--. 1 zexcon zexcon   685 Oct  4 16:00 learnToScriptAllOutput
-rw-rw-r--. 1 zexcon zexcon    23 Oct  4 12:42 learnToScriptInput
-rw-rw-r--. 1 zexcon zexcon     0 Oct  4 16:42 learnToScriptOutput
-rw-rw-r--. 1 zexcon zexcon    52 Oct  4 16:07 learnToScriptOutputError
-rwxrw-rw-. 1 zexcon zexcon   477 Oct  4 15:01 learnToScript.sh
drwxr-xr-x. 1 zexcon zexcon     0 Jul  7 16:04 Videos
ls: cannot access '/etc/invalidTest': No such file or directory

如果你直接使用命令行操作,并希望将所有结果通过管道传输到另一个命令,可以选择使用 |& 实现。

[zexcon ~]$ ls -l |& grep learn
-rw-rw-r--. 1 zexcon zexcon    1197 Oct 18 09:46 learnToScriptAllOutput
-rw-rw-r--. 1 zexcon zexcon     343 Oct 14 10:47 learnToScriptError
-rw-rw-r--. 1 zexcon zexcon       0 Oct 14 11:11 learnToScriptOut
-rw-rw-r--. 1 zexcon zexcon     348 Oct 14 10:27 learnToScriptOutError
-rwxr-x---. 1 zexcon zexcon     328 Oct 18 09:46 learnToScript.sh
[zexcon ~]$

标准输入(stdin)

在本篇和上篇文章中,我们已经多次使用过标准输入(stdin),因为在每次使用键盘输入时,我们都在使用标准输入。为了区别通常意义上的“键盘即标准输入”,这次我们尝试在脚本中使用 read 命令。下面的脚本中就使用了 read 命令,字面上就像“读取标准输入”。

#!/bin/bash

#Here we are asking a question to prompt the user for standard input. i.e.keyboard
echo 'Please enter your name.'

#Here we are reading the standard input and assigning it to the variable name with the read command.
read name

#We are now going back to standard output, by using echo and printing your name to the command line.
echo "With standard input you have told me your name is: $name"

这个示例通过标准输出给出提示,提醒用户输入信息,然后从标准输入(键盘)获取信息,使用 read 将其存储在 name 变量中,并通过标准输出显示出 name 中的值。

[zexcon@fedora ~]$ ./learnToScript.sh
Please enter your name.
zexcon
With standard input you have told me your name is: zexcon
[zexcon@fedora ~]$

在脚本中使用

现在我们把学到的东西放入脚本中,学习一下如何实际应用。下面是增加了几行后的新版本 learnToScript.sh 文件。它用追加的方式将标准输出、标准错误信息,以及两者混合后的信息,分别写入到三个不同文件。它将标准输出写入 learnToScriptStandardOutput,标准错误信息写入 learnToScriptStandardError,二者共同都写入 learnToScriptAllOutput 文件。

#!/bin/bash

#As we know this article is about scripting. So let's
#use what we learned in a script. 

#Let's get some information from the user and add it to our scripts with stanard input and read

echo "What is your name? "
read name


#Here standard output directed to append a file to learnToScirptStandardOutput
echo "$name, this will take standard output with append >> and redirect to learnToScriptStandardOutput." 1>> learnToScriptStandardOutput


#Here we are taking the standard error and appending it to learnToScriptStandardError but to see this we need to #create an error.
eco "Standard error with append >> redirect to learnToScriptStandardError." 2>> learnToScriptStandardError

#Here we are going to create an error and a standard output and see they go to the same place.
echo "Standard output with append >> redirect to learnToScriptAllOutput." &>> learnToScriptAllOutput
eco "Standard error with append >> redirect to learnToScriptAllOutput." &>> learnToScriptAllOutput

脚本在同一目录中创建了三个文件。命令 echo 故意输入错误(LCTT 译注:缺少了字母 h)以产生错误信息。如果查看三个文件,你会在 learnToScriptStandardOutput 中看到一条信息,在 learnToScriptStandardError 中看到一条信息,在 learnToScriptAllOutput 中看到两条信息。另外,该脚本还会再次提示输入的 name 值,再将其写入 learnToScriptStandardOutput 中。

结语

至此你应该能够明确,可以在命令行中执行的操作,都可以在脚本中执行。在编写可能供他人使用的脚本时,文档非常重要。如果继续深入研究脚本,标准输出会显得更有意义,因为你将会控制它们的生成。在脚本中,你可以与命令行中操作时应用相同的内容。下一篇文章我们会讨论函数、循环,以及在此基础上进一步构建的结构。


via: https://fedoramagazine.org/bash-shell-scripting-for-beginners-part-2/

作者:Matthew Darnell 选题:lujun9972 译者:unigeorge 校对:wxy

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

如标题所示,本文将涵盖面向初学者的 Bash Shell 脚本知识。虽然本文没有回顾 Bash 的历史,但是有很多资源能够便于读者了解相关内容,读者也可以直接访问 https://www.gnu.org/software/bash/ 上的 GNU 项目。我们会从了解一些非常基本的概念开始,然后再将相关知识综合起来。

创建脚本文件

首先来创建一个脚本文件。我们要确保当前目录为家目录。

cd ~

在主目录中创建示例文件。文件名可以是任意名称,本文使用 learnToScript.sh 作为文件名。

touch learnToScript.sh

此时家目录中就生成了一个名为 learnToScript.sh 的文件。输入以下命令以检验该文件是否存在,还可以注意到其权限是 -rw-rw-r–

[zexcon@trinity ~]$ ls -l
total 7
drwxr-xr-x. 1 zexcon zexcon   90 Aug 30 13:08 Desktop
drwxr-xr-x. 1 zexcon zexcon   80 Sep 16 08:53 Documents
drwxr-xr-x. 1 zexcon zexcon 1222 Sep 16 08:53 Downloads
-rw-rw-r--. 1 zexcon zexcon   70 Sep 17 10:10 learnToScript.sh
drwxr-xr-x. 1 zexcon zexcon    0 Jul  7 16:04 Music
drwxr-xr-x. 1 zexcon zexcon  318 Sep 15 13:53 Pictures
drwxr-xr-x. 1 zexcon zexcon    0 Jul  7 16:04 Public
drwxr-xr-x. 1 zexcon zexcon    0 Jul  7 16:04 Videos
[zexcon@trinity ~]$

正式开始之前还有一件事要做。可以试一下在没有写入任何内容的情况下执行脚本,命令行输入以下内容:

./learnToScript.sh
[zexcon ~]$ ./learnToScript.sh
bash: ./learnToScript.sh: Permission denied

提示权限被拒绝,因为该文件没有执行权限。所以你需要更改文件的权限才能执行脚本。如果你不熟悉权限相关知识,建议阅读 Paul W. Frields 写的 Fedora 杂志文章。

假定我们已经了解了权限相关知识,回到终端更改 learnToScript.sh 文件,增加执行权限。输入以下内容:

chmod 755 learnToScript.sh
[zexcon@trinity ~]$ ls -l
total 7
drwxr-xr-x. 1 zexcon zexcon   90 Aug 30 13:08 Desktop
drwxr-xr-x. 1 zexcon zexcon   80 Sep 16 08:53 Documents
drwxr-xr-x. 1 zexcon zexcon 1222 Sep 16 08:53 Downloads
-rwxr-xr-x. 1 zexcon zexcon   70 Sep 17 10:10 learnToScript.sh
drwxr-xr-x. 1 zexcon zexcon    0 Jul  7 16:04 Music
drwxr-xr-x. 1 zexcon zexcon  318 Sep 15 13:53 Pictures
drwxr-xr-x. 1 zexcon zexcon    0 Jul  7 16:04 Public
drwxr-xr-x. 1 zexcon zexcon    0 Jul  7 16:04 Videos
[zexcon@trinity ~]$

好的,现在一切准备就绪,你已经获得了 learnToScript.sh 命令的读取、写入和执行权限 (-rwxr-xr-x)。

编辑脚本文件

下面的内容需要你熟悉 vim 或其他类似的文本编辑器,本文选用 vim。在命令提示符下输入以下内容:

vim learnToScript.sh

这会打开一个空的文本文件,里面有一堆波浪号。键盘输入 i 将进入插入模式。通过查看终端窗口的左下角,你可以确认它处于该模式。(友情推荐,nano 编辑器也很不错。)

接下来我们要确保文件被正确的解释器识别。因此,输入 #! 和 bash 的目录 /bin/bash

#!/bin/bash

本文教程步骤的最后一件事是保存文档。按 Esc 退出输入模式,然后按 Shift+:。在冒号处输入 wq,按下回车键,就会写入(w)文件并退出(qvim 编辑器。

使用 vim 时要记住,要想写入文档,就需要输入 i,此时你会在底部看到 –INSERT–。要保存文档,就需要按 Esc 退出输入模式,然后按 Shift+:,输入 w 写入文件;或者按 Esc,然后 Shift+: 输入 q,实现退出而不保存;或者将 wq 连在一起,可以实现写入并关闭文件。Esc 本身就只是退出插入模式。你可以在其 官方网站教程网站 上找到更多关于 vim 的信息。

开始正式写脚本

echo 命令

echo 命令用于向终端返回一些信息,可以使用单引号、双引号或不使用引号。那么让我们用一个传统的 Hello World 来试试它!

#!/bin/bash

echo Hello World!
echo 'Hello World!'
echo "Hello World!"
[zexcon ~]$ ./learnToScript.sh
Hello World!
Hello World!
Hello World!
[zexcon ~]$

注意,上述三行得到了相同的结果。使用单引号、双引号或不使用引号的结果不是一定相同的,但在这个基本脚本中确实得到了同样的结果。在某些情况下,引号的类型不同会导致结果差异。至此恭喜你编写了第一个 Bash 脚本。当你继续创建更多脚本并放飞你的想法时,可能会需要了解以下更多的知识。

命令替换 $( ) 和

命令替换可以实现以下效果:获取在命令行执行命令的结果,并将该结果写入变量。例如,如果在命令提示符下输入 ls,可以获得当前工作目录的列表。我们用这个命令来实践一下。命令替换选项有两个,请注意:第一个选项使用键盘左侧 Tab 键上方的反引号。它与波浪号 ~ 在一个键位上。第二个选项使用 shell 变量。

#!/bin/bash

command1=`ls`
echo $command1

command2=$(ls)
echo $command2
[zexcon ~]$ ./learnToScript.sh
Desktop Documents Downloads learnToScript.sh Music Pictures Public snap Videos
Desktop Documents Downloads learnToScript.sh Music Pictures Public snap Videos
[zexcon ~]$

注意变量、等号和命令开头之间不要有空格。使用这两个选项得到的结果是完全相同的。请注意,变量需要以美元符号开头。如果你忘记了,并且回显了不带美元符号的命令变量,会只输出命令的名称,如下所示:

#!/bin/bash

command1=`ls`
echo command1

command2=$(ls)
echo command2
[zexcon ~]$ ./learnToScript.sh
command1
command2
[zexcon ~]$

双括号 (())

那么双括号有什么用呢? 很简单,双括号用于数学方程式。

#!/bin/bash

echo $((5+3))
echo $((5-3))
echo $((5*3))
echo $((5/3))
[zexcon ~]$ ./learnToScript.sh
8
2
15
1
[zexcon ~]$

结语

至此,我们已经创建了第一个脚本。我们知道了如何执行多个命令:将它们放在一个脚本中并运行,就可以获得结果。下一篇文章会继续讨论,了解输入和输出的重定向、管道命令、使用双括号或者添加注释等知识。


via: https://fedoramagazine.org/bash-shell-scripting-for-beginners-part-1/

作者:zexcon 选题:lujun9972 译者:unigeorge 校对:wxy

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

巧用 Bash 脚本程序能帮助你完成很多极具挑战的任务。

 title=

系统管理员经常写脚本程序,不论长短,这些脚本可以完成某种任务。

你是否曾经查看过某个软件发行方提供的安装用的 脚本 script 程序?为了能够适应不同用户的系统配置,顺利完成安装,这些脚本程序经常包含很多函数和逻辑分支。多年来,我积累了一些改进脚本程序的一些技巧,这里分享几个,希望能对朋友们也有用。这里列出一组短脚本示例,展示给大家做脚本样本。

初步尝试

我尝试写一个脚本程序时,原始程序往往就是一组命令行,通常就是调用标准命令完成诸如更新网页内容之类的工作,这样可以节省时间。其中一个类似的工作是解压文件到 Apache 网站服务器的主目录里,我的最初脚本程序大概是下面这样:

cp january_schedule.tar.gz /usr/apache/home/calendar/
cd /usr/apache/home/calendar/
tar zvxf january_schedule.tar.gz

这帮我节省了时间,也减少了键入多条命令操作。时日久了,我掌握了另外的技巧,可以用 Bash 脚本程序完成更难的一些工作,比如说创建软件安装包、安装软件、备份文件系统等工作。

1、条件分支结构

和众多其他编程语言一样,脚本程序的条件分支结构同样是强大的常用技能。条件分支结构赋予了计算机程序逻辑能力,我的很多实例都是基于条件逻辑分支。

基本的条件分支结构就是 if 条件分支结构。通过判定是否满足特定条件,可以控制程序选择执行相应的脚本命令段。比如说,想要判断系统是否安装了 Java ,可以通过判断系统有没有一个 Java 库目录;如果找到这个目录,就把这个目录路径添加到可运行程序路径,也就可以调用 Java 库应用了。

if [ -d "$JAVA_HOME/bin" ] ; then
    PATH="$JAVA_HOME/bin:$PATH"

2、限定运行权限

你或许想只允许特定的用户才能执行某个脚本程序。除了 Linux 的权限许可管理,比如对用户和用户组设定权限、通过 SELinux 设定此类的保护权限等,你还可以在脚本里设置逻辑判断来设置执行权限。类似的情况可能是,你需要确保只有网站程序的所有者才能执行相应的网站初始化操作脚本。甚至你可以限定只有 root 用户才能执行某个脚本。这个可以通过在脚本程序里设置逻辑判断实现,Linux 提供的几个环境变量可以帮忙。其中一个是保存用户名称的变量 $USER, 另一个是保存用户识别码的变量 $UID 。在脚本程序里,执行用户的 UID 值就保存在 $UID 变量里。

用户名判别

第一个例子里,我在一个带有几个应用服务器实例的多用户环境里指定只有用户 jboss1 可以执行脚本程序。条件 if 语句主要是判断,“要求执行这个脚本程序的用户不是 jboss1 吗?”当此条件为真时,就会调用第一个 echo 语句,接着是 exit 1,即退出这个脚本程序。

if [ "$USER" != 'jboss1' ]; then
     echo "Sorry, this script must be run as JBOSS1!"
     exit 1
fi
echo "continue script"

根用户判别

接下来的例子是要求只有根用户才能执行脚本程序。根用户的用户识别码(UID)是 0,设置的条件判断采用大于操作符(-gt),所有 UID 值大于 0 的用户都被禁止执行该脚本程序。

if [ "$UID" -gt 0 ]; then
     echo "Sorry, this script must be run as ROOT!"
     exit 1
fi
echo "continue script"

3、带参数执行程序

可执行程序可以附带参数作为执行选项,命令行脚本程序也是一样,下面给出几个例子。在这之前,我想告诉你,能写出好的程序并不只是写出我们想要它执行什么的程序,程序还需要不执行我们不要它执行的操作。如果运行程序时没有提供参数造成程序缺少足够信息,我愿意脚本程序不要做任何破坏性的操作。因而,程序的第一步就是确认命令行是否提供了参数,判定的条件就是参数数量 $# 是否为 0 ,如果是(意味着没有提供参数),就直接终止脚本程序并退出操作。

if [ $# -eq 0 ]; then
    echo "No arguments provided"
    exit 1
fi
echo "arguments found: $#"

多个运行参数

可以传递给脚本程序的参数不止一个。脚本使用内部变量指代这些参数,内部变量名用非负整数递增标识,也就是 $1$2$3 等等递增。我只是扩展前面的程序,并在下面一行输出显示用户提供的前三个参数。显然,要针对所有的每个参数有对应的响应需要更多的逻辑判断,这里的例子只是简单展示参数的使用。

echo $1 $2 $3

我们在讨论这些参数变量名,你或许有个疑问,“参数变量名怎么跳过了 $0,(而直接从$1 开始)?”

是的,是这样,这是有原因的。变量名 $0 确实存在,也非常有用,它储存的是被执行的脚本程序的名称。

echo $0

程序执行过程中有一个变量名指代程序名称,很重要的一个原因是,可以在生成的日志文件名称里包含程序名称,最简单的方式应该是调用一个 echo 语句。

echo test >> $0.log

当然,你或许要增加一些代码,确保这个日志文件存放在你希望的路径,日志名称包含你认为有用的信息。

4、交互输入

脚本程序的另一个好用的特性是可以在执行过程中接受输入,最简单的情况是让用户可以输入一些信息。

echo "enter a word please:"
read word
echo $word

这样也可以让用户在程序执行中作出选择。

read -p "Install Software ?? [Y/n]: " answ
if [ "$answ" == 'n' ]; then
    exit 1
fi
    echo "Installation starting..."

5、出错退出执行

几年前,我写了个脚本,想在自己的电脑上安装最新版本的 Java 开发工具包(JDK)。这个脚本把 JDK 文件解压到指定目录,创建更新一些符号链接,再做一下设置告诉系统使用这个最新的版本。如果解压过程出现错误,在执行后面的操作就会使整个系统上的 Java 破坏不能使用。因而,这种情况下需要终止程序。如果解压过程没有成功,就不应该再继续进行之后的更新操作。下面语句段可以完成这个功能。

tar kxzmf jdk-8u221-linux-x64.tar.gz -C /jdk --checkpoint=.500; ec=$?
if [ $ec -ne 0 ]; then
     echo "Installation failed - exiting."
     exit 1
fi

下面的单行语句可以给你快速展示一下变量 $? 的用法。

ls T; ec=$?; echo $ec

先用 touch T 命令创建一个文件名为 T 的文件,然后执行这个单行命令,变量 ec 的值会是 0。然后,用 rm T 命令删除文件,再执行该单行命令,变量 ec 的值会是 2,因为文件 T 不存在,命令 ls 找不到指定文件报错。

在逻辑条件里利用这个出错标识,参照前文我使用的条件判断,可以使脚本文件按需完成设定操作。

结语

要完成复杂的功能,或许我们觉得应该使用诸如 Python、C 或 Java 这类的高级编程语言,然而并不尽然,脚本编程语言也很强大,可以完成类似任务。要充分发挥脚本的作用,有很多需要学习的,希望这里的几个例子能让你意识到脚本编程的强大。


via: https://opensource.com/article/20/1/improve-bash-scripts

作者:Alan Formy-Duval 选题:lujun9972 译者:fisherue 校对:wxy

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

《Bash it out》使用 16 个谜题,涵盖了基本、中级和高级 Bash 脚本。

 title=

计算机既是我的爱好,也是我的职业。我的公寓里散布着大约 10 台计算机,它们都运行 Linux(包括我的 Mac)。由于我喜欢升级我的电脑和提升我的电脑技能,当我遇到 Sylvain Leroux 的《Bash it out》时,我抓住了购买它的机会。我在 Debian Linux 上经常使用命令行,这似乎是扩展我的 Bash 知识的好机会。当作者在前言中解释他使用 Debian Linux 时,我笑了,这是我最喜欢的两个发行版之一。

Bash 可让你自动执行任务,因此它是一种省力、有趣且有用的工具。在阅读本书之前,我已经有相当多的 Unix 和 Linux 上的 Bash 经验。我不是专家,部分原因是脚本语言非常广泛和强大。当我在基于 Arch 的 Linux 发行版 EndeavourOS 的欢迎屏幕上看到 Bash 时,我第一次对 Bash 产生了兴趣。

以下屏幕截图显示了 EndeavourOS 的一些选项。你可能不相信,这些面板只指向 Bash 脚本,每个脚本都完成一些相对复杂的任务。而且因为它都是开源的,所以我可以根据需要修改这些脚本中的任何一个。

 title=

 title=

总有东西要学

我对这本书的印象非常好。虽然不长,但经过了深思熟虑。作者对 Bash 有非常广泛的了解,并且具有解释如何使用它的不可思议的能力。这本书使用 16 个谜题涵盖了基本、中级和高级 Bash 脚本,他称之为“挑战”。这教会了我将 Bash 脚本视为需要解决的编程难题,这让我玩起来更有趣。

Bash 的一个令人兴奋的方面是它与 Linux 系统深度集成。虽然它的部分能力在于它的语法,但它也很强大,因为它可以访问很多系统资源。你可以编写重复性任务或简单但厌倦了手动执行的任务的脚本。不管是大事还是小事,《Bash it out》可以帮助你了解可以做什么以及如何实现它。

如果我不提及 David Both 的发布在 Opensource.com 的免费资源《A sysadmin's guide to Bash scripting\_》,这篇书评就不会完整。这个 17 页的 PDF 指南与《Bash it out》不同,但它们共同构成了任何想要了解它的人的成功组合。

我不是计算机程序员,但《Bash it out》增加了我进入更高级 Bash 脚本水平的欲望——虽然没有这个打算,但我可能最终无意中成为一名计算机程序员。

我喜欢 Linux 的原因之一是因为它的操作系统功能强大且用途广泛。无论我对 Linux 了解多少,总有一些新东西需要学习,这让我更加欣赏 Linux。

在竞争激烈且不断变化的就业市场中,我们所有人都应该不断更新我们的技能。这本书帮助我以非常实际的方式学习了 Bash。几乎感觉作者和我在同一个房间里,耐心地指导我学习。

作者 Leroux 具有不可思议的能力去吸引读者。这是一份难得的天赋,我认为比他的技术专长更有价值。事实上,我写这篇书评是为了感谢作者预见了我自己的学习需求;虽然我们从未见过面,但我从他的天赋中受益匪浅。


via: https://opensource.com/article/20/4/bash-it-out-book

作者:Carlos Aguayo 选题:lujun9972 译者:baddates 校对:wxy

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

在家玩流行的英国游戏节目 “Countdown” 中的数字游戏。

 title=

像许多人一样,我在大流行期间看了不少新的电视节目。我最近发现了一个英国的游戏节目,叫做 Countdown,参赛者在其中玩两种游戏:一种是 单词 游戏,他们试图从杂乱的字母中找出最长的单词;另一种是 数字 游戏,他们从随机选择的数字中计算出一个目标数字。因为我喜欢数学,我发现自己被数字游戏所吸引。

数字游戏可以为你的下一个家庭游戏之夜增添乐趣,所以我想分享我自己的一个游戏变体。你以一组随机数字开始,分为 1 到 10 的“小”数字和 15、20、25,以此类推,直到 100 的“大”数字。你从大数字和小数字中挑选六个数字的任何组合。

接下来,你生成一个 200 到 999 之间的随机“目标”数字。然后用你的六个数字进行简单的算术运算,尝试用每个“小”和“大”数字计算出目标数字,但使用不能超过一次。如果你能准确地计算出目标数字,你就能得到最高分,如果距离目标数字 10 以内就得到较低的分数。

例如,如果你的随机数是 75、100、2、3、4 和 1,而你的目标数是 505,你可以说 2+3=55×100=5004+1=5,以及 5+500=505。或者更直接地:(2+3)×100 + 4 + 1 = 505

在命令行中随机化列表

我发现在家里玩这个游戏的最好方法是从 1 到 10 的池子里抽出四个“小”数字,从 15 到 100 的 5 的倍数中抽出两个“大”数字。你可以使用 Linux 命令行来为你创建这些随机数。

让我们从“小”数字开始。我希望这些数字在 1 到 10 的范围内。你可以使用 Linux 的 seq 命令生成一个数字序列。你可以用几种不同的方式运行 seq,但最简单的形式是提供序列的起始和结束数字。要生成一个从 1 到 10 的列表,你可以运行这个命令:

$ seq 1 10
1
2
3
4
5
6
7
8
9
10

为了随机化这个列表,你可以使用 Linux 的 shuf(“shuffle”,打乱)命令。shuf 将随机化你给它的东西的顺序,通常是一个文件。例如,如果你把 seq 命令的输出发送到 shuf 命令,你会收到一个 1 到 10 之间的随机数字列表:

$ seq 1 10 | shuf
3
6
8
10
7
4
5
2
1
9

要从 1 到 10 的列表中只选择四个随机数,你可以将输出发送到 head 命令,它将打印出输入的前几行。使用 -4 选项来指定 head 只打印前四行:

$ seq 1 10 | shuf | head -4
6
1
8
4

注意,这个列表与前面的例子不同,因为 shuf 每次都会生成一个随机顺序。

现在你可以采取下一步措施来生成“大”数字的随机列表。第一步是生成一个可能的数字列表,从 15 开始,以 5 为单位递增,直到达到 100。你可以用 Linux 的 seq 命令生成这个列表。为了使每个数字以 5 为单位递增,在 seq 命令中插入另一个选项来表示 步进

$ seq 15 5 100
15
20
25
30
35
40
45
50
55
60
65
70
75
80
85
90
95
100

就像以前一样,你可以随机化这个列表,选择两个“大”数字:

$ seq 15 5 100 | shuf | head -2
75
40

用 Bash 生成一个随机数

我想你可以用类似的方法从 200 到 999 的范围内选择游戏的目标数字。但是生成单个随机数的最简单的方案是直接在 Bash 中使用 RANDOM 变量。当你引用这个内置变量时,Bash 会生成一个大的随机数。要把它放到 200 到 999 的范围内,你需要先把随机数放到 0 到 799 的范围内,然后加上 200。

要把随机数放到从 0 开始的特定范围内,你可以使用模数算术运算符。模数计算的是两个数字相除后的 余数。如果我用 801 除以 800,结果是 1,余数是 1(模数是 1)。800 除以 800 的结果是 1,余数是 0(模数是 0)。而用 799 除以 800 的结果是 0,余数是 799(模数是 799)。

Bash 通过 $(()) 结构支持算术展开。在双括号之间,Bash 将对你提供的数值进行算术运算。要计算 801 除以 800 的模数,然后加上 200,你可以输入:

$ echo $(( 801 % 800 + 200 ))
201

通过这个操作,你可以计算出一个 200 到 999 之间的随机目标数:

$ echo $(( RANDOM % 800 + 200 ))
673

你可能想知道为什么我在 Bash 语句中使用 RANDOM 而不是 $RANDOM。在算术扩展中, Bash 会自动扩展双括号内的任何变量. 你不需要在 $RANDOM 变量上的 $ 来引用该变量的值, 因为 Bash 会帮你做这件事。

玩数字游戏

让我们把所有这些放在一起,玩玩数字游戏。产生两个随机的“大”数字, 四个随机的“小”数值,以及目标值:

$ seq 15 5 100 | shuf | head -2
75
100
$ seq 1 10 | shuf | head -4
4
3
10
2
$ echo $(( RANDOM % 800 + 200 ))
868

我的数字是 7510043102,而我的目标数字是 868

如果我用每个“小”和“大”数字做这些算术运算,并不超过一次,我就能接近目标数字了:

10×75 = 750
750+100 = 850

然后:

4×3 = 12
850+12 = 862
862+2 = 864

只相差 4 了,不错!但我发现这样可以用每个随机数不超过一次来计算出准确的数字:

4×2 = 8
8×100 = 800

然后:

75-10+3 = 68
800+68 = 868

或者我可以做 这些 计算来准确地得到目标数字。这只用了六个随机数中的五个:

4×3 = 12
75+12 = 87

然后:

87×10 = 870
870-2 = 868

试一试 Countdown 数字游戏,并在评论中告诉我们你做得如何。


via: https://opensource.com/article/21/4/math-game-linux-commands

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

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