9 个 Bash 脚本示例,让您在 Linux 上入门

如果您刚开始在 Linux 上使用 Bash 脚本,扎实掌握基础知识将对您大有裨益。它们是更深入的知识和更高的脚本技能的基础。
记住,让你的脚本可执行
对于要执行脚本的 shell,脚本必须具有可执行文件权限集。没有这个,你的脚本只是一个文本文件。有了它,它仍然是一个文本文件,但 shell 知道它包含指令,并会在脚本启动时尝试执行它们。
编写脚本的全部意义在于它们可以运行,因此第一个基本步骤是了解如何让 Linux 知道您的脚本应该被视为可执行的。
chmod
命令让我们设置文件权限。可以使用 +x 标志设置执行权限。
chmod +x script1.sh

您需要对每个脚本执行此操作。将“script1.sh”替换为您的脚本名称。
1. 奇怪的第一行是什么?
脚本的第一行告诉 shell 应该调用哪个解释器来运行该脚本。第一行必须以 shebang“#!”开头,也称为 hashbang。这 ”#!”告诉 shell 这一行包含脚本所针对的解释器的路径和名称。
这很重要,因为如果您编写了一个在 Bash 中运行的脚本,您不希望它被不同的 shell 解释。可能存在不兼容性。与大多数 shell 一样,Bash 有自己的语法和功能怪癖,这是其他 shell 所没有的,或者将以不同的方式实现。
运行脚本时,当前 shell 打开脚本并确定应使用哪个 shell 或解释器来执行该脚本。然后它启动该 shell 并将脚本传递给它。
#!/bin/bash
echo Running in $SHELL
该脚本的第一行可以理解为“使用位于 /bin/bash 的解释器来运行该脚本”。
脚本中唯一的一行将 $SHELL
环境变量中保存的值写入终端屏幕。这证实了使用 Bash 来执行脚本。
./script1.sh

作为一个小技巧,我们可以证明脚本被传递给了我们选择的任何解释器。
#!/bin/cat
All the lines of text are passed to the cat command
and are printed in the terminal window. That includes
the shebang line.
script2.sh

此脚本由当前 shell 启动并传递给 cat
命令。 cat
命令“运行”脚本。
像这样编写 shebang 假设您知道 shell 或其他解释器在目标机器上的位置。 99% 的情况下,这很好。但是有些人喜欢对冲他们的赌注,这样写他们的 shebang:
#!/usr/bin/env bash
echo Running in $SHELL
script3.sh

启动脚本时,shell 搜索 指定 shell 的位置。如果 shell 恰好位于非标准位置,这种方法可以避免“错误的解释器”错误。
别听,他在说谎!
在 Linux 中,剥猫皮或证明作者错误的方法总是不止一种。完全真实的是,有一种方法可以在没有 shebang 的情况下运行脚本,并且无需使它们可执行。
如果启动要执行脚本的 shell 并将脚本作为命令行参数传递,则 shell 将启动并运行脚本——无论脚本是否可执行。因为您在命令行上选择了 shell,所以不需要 shebang。
这是整个脚本:
echo "I've been executed by" $SHELL
我们将使用 ls
来查看脚本是否确实不可执行,并使用脚本的名称启动 Bash:
ls
bash script4.sh

还有一种方法可以让脚本由当前 shell 运行,而不是专门为执行脚本而启动的 shell。如果您使用 source
命令(可以缩写为单个句点“.
”),您的脚本将由当前 shell 执行。
因此,要在没有 shebang、没有可执行文件权限且不启动另一个 shell 的情况下运行脚本,您可以使用以下任一命令:
source script4.sh
. script4.sh

虽然这是可能的,但不建议将其作为通用解决方案。有缺点。
如果脚本不包含 shebang,则无法判断它是为哪个 shell 编写的。一年后你还记得吗?并且在脚本没有设置可执行权限的情况下,ls
命令不会将其识别为可执行文件,也不会使用颜色来区分脚本和纯文本文件。
2.打印文字
向终端写入文本是一个常见的需求。一些视觉反馈有很长的路要走。
对于简单消息,echo
命令就足够了。它允许对文本进行一些格式化,也允许您使用变量。
#!/bin/bash
echo This is a simple string.
echo "This is a string containing 'single quotes' so it's wrapped in double quotes."
echo "This prints the user name:" $USER
echo -e "The -e option lets us use\nformatting directives\nto split the string."
./script5.sh

printf
命令为我们提供了更多的灵活性和更好的格式化功能,包括数字转换。
此脚本使用三个不同的数字基数打印相同的数字。十六进制版本也被格式化为以大写形式打印,前导零和三位宽度。
#!/bin/bash
printf "Decimal: %d, Octal: %o, Hexadecimal: %03X\n" 32 32 32
./script6.sh

请注意,与 echo
不同,您必须告诉 printf
使用“\n
”标记开始新行。
3. 创建和使用变量
变量允许您在程序中存储值并操作和使用它们。您可以创建自己的变量或使用环境变量作为系统值。
#!/bin/bash
millennium_text="Years since the millennium:"
current_time=$( date '+%H:%M:%S' )
todays_date=$( date '+%F' )
year=$( date '+%Y' )
echo "Current time:" $current_time
echo "Today's date:" $todays_date
years_since_Y2K=$(( year - 2000 ))
echo $millennium_text $years_since_Y2K
此脚本创建一个名为 millennium_text
的字符串变量。它包含一行文本。
然后它创建三个数值变量。
current_time
变量被初始化为脚本执行的时间。todays_date
变量设置为脚本运行的日期。year
变量保存当前年份。
要访问存储在变量中的值,请在其名称前加上美元符号“$”。
./script7.sh

该脚本打印时间和日期,然后计算自千禧年以来经过了多少年,并将其存储在 years_since_Y2K
变量中。
最后,它打印包含在 millennium_text
变量中的字符串和存储在 years_since_Y2K
中的数值。
4. 处理用户输入
要允许用户输入脚本将使用的值,您需要能够捕获用户的键盘输入。 Bash read
命令允许 ut 这样做。这是一个简单的例子。
#!/bin/bash
echo "Enter a number and hit \"Enter\""
read user_number1;
echo "Enter another number and hit \"Enter\""
read user_number2;
printf "You entered: %d and %d\n" $user_number1 $user_number2
printf "Added together they make: %d\n" $(( user_number1 + user_number2))
该脚本提示输入两个数字。它们从键盘读取并存储在两个变量 user_number1
和 user_number2
中。
该脚本将数字打印到终端窗口,将它们相加,然后打印总数。
./script8.sh

我们可以使用 -p
(提示)选项将提示组合到 read
命令中。
#!/bin/bash
read -p "Enter a number and hit \"Enter\" " user_number1;
read -p "Enter another number and hit \"Enter\" " user_number2;
printf "You entered: %d and %d\n" $user_number1 $user_number2
printf "Added together they make: %d\n" $(( user_number1 + user_number2))
这使事情更整洁,更容易阅读。易于阅读的脚本也更易于调试。
./script9.sh

现在脚本的行为略有不同。用户输入与提示位于同一行。
要捕获键盘输入而不将其回显到终端窗口,请使用 -s
(静音)选项。
#!/bin/bash
read -s -p "Enter your secret PIN and hit \"Enter\" " secret_PIN;
printf "\nShhh ... it is %d\n" $secret_PIN
./script10.sh

输入值被捕获并存储在一个名为 secret_PIN
的变量中,但当用户输入时它不会回显到屏幕上。之后你用它做什么取决于你。
5.接受参数
有时接受用户输入作为命令行参数比让脚本等待输入更方便。将值传递给脚本很容易。它们可以在脚本中被引用,就像它们是任何其他变量一样。
第一个参数变为变量 $1
,第二个参数变为变量 $2
,依此类推。变量 $0
始终保存脚本的名称,变量 $#
保存命令行上提供的参数数量。变量 $@
是一个包含所有命令行参数的字符串。
#!/bin/bash
printf "This script is called: %s\n" $0
printf "You used %d command line parameters\n" $#
# loop through the variables
for param in "$@"; do
echo "$param"
done
echo "Parameter 2 was:" $2
此脚本使用 $0
和 $#
打印一些信息。然后使用 ?@
遍历所有命令行参数。它使用 $2
来展示如何访问单个特定参数值。
./script11.sh

将几个单词用引号“””括起来,将它们组合成一个参数。
6. 从文件中读取数据
知道如何从文件中读取数据是一项很棒的技能。我们可以在 Bash 中使用 while 循环来做到这一点。
#!/bin/bash
LineCount=0
while IFS='' read -r LinefromFile || [[ -n "${LinefromFile}" ]]; do
((LineCount++))
echo "Reading line $LineCount: ${LinefromFile}"
done < "$1"
我们将希望脚本处理的文件的名称作为命令行参数传递。这将是唯一的参数,因此在脚本中 $1
将保存文件名。我们将该文件重定向到 while
循环中。
while
循环使用 IFS=
赋值将内部字段分隔符设置为空字符串。这可以防止 read
命令在空格处拆分行。只有一行末尾的回车被认为是该行的真正结束。
[[ -n \$ {LinefromFile}\ ]]
子句迎合了文件中最后一行不以回车符结尾的可能性。即使没有,最后一行也会被正确处理,并被视为符合 POSIX 标准的常规行。
./script12.sh twinkle.txt

7. 使用条件测试
如果您希望您的脚本针对不同的条件执行不同的操作,则需要执行条件测试。双括号测试语法一开始提供了大量的选项。
#!/bin/bash
price=$1
if [[ price -ge 15 ]];
then
echo "Too expensive."
else
echo "Buy it!"
fi
Bash 提供了一整套比较运算符,让您可以确定诸如文件是否存在、是否可以读取、是否可以写入以及目录是否存在等事项。
它还具有等于 -qe
、大于 -gt
、小于或等于 -le
等的数值测试,尽管您可以也使用熟悉的 ==
、 >=
、 <=
表示法。
./script13.sh 13
./script13.sh 14
./script13.sh 15
./script13.sh 16

8.for循环的威力
一遍又一遍地重复动作最好使用循环来完成。 for
循环可让您多次运行循环。这可能达到一个特定的数字,或者可能直到循环完成一个项目列表。
#!/bin/bash
for (( i=0; i<=$1; i++ ))
do
echo "C-style for loop:" $i
done
for i in {1..4}
do
echo "For loop with a range:" $i
done
for i in "zero" "one" "two" "three"
do
echo "For loop with a list of words:" $i
done
website="How To Geek"
for i in $website
do
echo "For loop with a collection of words:" $i
done
所有这些循环都是 for
循环,但它们使用不同类型的循环语句和数据。
./script14.sh 3

第一个循环是经典的 C 风格 for
循环。循环计数器 i
初始化为零,并随着循环的每个循环而递增。当 i
的值小于或等于 $1
中的值时,循环将继续运行。
第二个循环遍历从 1 到 4 的数字范围。第三个循环遍历单词列表。虽然有更多的词要处理,但循环不断重复。
最后一个循环遍历字符串变量中的单词列表。
九、功能
函数允许您将代码段封装到命名例程中,可以从脚本中的任何位置调用这些例程。
假设我们希望从文件中读取行的脚本对每一行进行某种处理。将该代码包含在函数中会很方便。
#!/bin/bash
LineCount=0
function count_words() {
printf "%d words in line %d\n" $(echo $1 | wc -w) $2
}
while IFS='' read -r LinefromFile || [[ -n "${LinefromFile}" ]]; do
((LineCount++))
count_words "$LinefromFile" $LineCount
done < "$1"
count_words "This isn't in the loop" 99
我们修改了文件读取程序,添加了一个名为 count_words
的函数。它是在我们需要使用它之前定义的。
函数定义以单词 function
开头。这之后是我们函数的唯一名称,后跟括号“()
”。函数体包含在大括号“{}”中。
函数定义不会导致执行任何代码。在函数被调用之前,函数中的任何内容都不会运行。
count_words
函数打印一行文本中的单词数和行号。这两个参数被传递到函数中就像参数被传递到脚本中一样。第一个参数成为函数变量$1
,第二个参数成为函数变量$2
,以此类推。
while
循环从文件中读取每一行并将其与行号一起传递给 count_words
函数。为了展示我们可以从脚本中的不同位置调用该函数,我们在 while
循环之外再次调用它。
./script15.sh twinkle.txt

不要害怕学习曲线
脚本是有益且有用的,但很难进入。一旦您掌握了一些可重用的技术,您将能够相对轻松地编写有价值的脚本。然后你可以研究更高级的功能。
先走再跑,花点时间享受这段旅程。