MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Bash脚本基础入门:从Hello World开始

2023-05-261.9k 阅读

一、Bash脚本简介

Bash,即 Bourne Again SHell,是 Linux 和 Unix 系统中最常用的 shell 之一。它不仅是用户与操作系统交互的接口,也是一种强大的编程语言。Bash 脚本允许用户将一系列的命令组合成一个可执行文件,从而自动化执行复杂的任务。这种脚本编程方式极大地提高了系统管理和日常任务处理的效率。

Bash 脚本的起源可以追溯到早期的 Unix 系统。最初的 shell 是由 Stephen Bourne 开发的 Bourne Shell(sh)。后来,GNU 项目对其进行了扩展和改进,开发出了 Bash。Bash 不仅兼容 Bourne Shell 的语法,还添加了许多新功能,如命令历史、命令补全、别名等,使其更易于使用和编程。

二、第一个Bash脚本:Hello World

2.1 创建脚本文件

在开始编写脚本之前,我们需要先创建一个文件来存放脚本内容。在 Linux 系统中,通常使用文本编辑器来创建和编辑脚本文件。常用的文本编辑器有 vivimnano。这里我们以 nano 为例,创建一个名为 hello.sh 的脚本文件:

nano hello.sh

2.2 编写Hello World脚本

在打开的 nano 编辑器中,输入以下内容:

#!/bin/bash
echo "Hello, World!"

第一行 #!/bin/bash 被称为 shebang 行,它告诉系统使用 /bin/bash 程序来解释执行这个脚本。echo 是 Bash 中的一个命令,用于在标准输出上打印文本。在这里,我们使用 echo 命令打印出 "Hello, World!"。

2.3 保存并退出

nano 编辑器中,按下 Ctrl + X,然后按 Y 确认保存,最后按 Enter 键退出。

2.4 赋予执行权限

在 Linux 系统中,默认情况下,新创建的脚本文件是没有执行权限的。我们需要使用 chmod 命令为其赋予执行权限:

chmod +x hello.sh

chmod 是改变文件权限的命令,+x 表示为文件添加可执行权限。

2.5 执行脚本

现在,我们可以执行这个脚本了。在终端中输入以下命令:

./hello.sh

./ 表示当前目录,因为当前目录通常不在系统的 PATH 环境变量中,所以需要加上 ./ 来指定脚本的位置。执行脚本后,你将在终端中看到输出 "Hello, World!"。

三、Bash脚本基础语法

3.1 变量

在 Bash 脚本中,变量是用于存储数据的容器。变量名由字母、数字和下划线组成,并且必须以字母或下划线开头。变量的赋值语法如下:

variable_name=value

例如:

name="John"
age=30

注意,在赋值时,等号两边不能有空格。要使用变量的值,可以在变量名前加上美元符号 $

echo "My name is $name and I am $age years old."

3.2 环境变量

环境变量是由系统或用户设置的变量,它们在整个系统或特定的 shell 会话中起作用。一些常见的环境变量包括 PATH(用于指定可执行文件的搜索路径)、HOME(用户的主目录)等。我们可以在脚本中访问和使用这些环境变量:

echo "My home directory is $HOME"

3.3 命令替换

命令替换允许我们将一个命令的输出赋值给一个变量。有两种方式进行命令替换:

  1. 使用反引号
date_result=`date`
echo "The current date is $date_result"
  1. 使用 $( )
date_result=$(date)
echo "The current date is $date_result"

3.4 算术运算

Bash 支持基本的算术运算。我们可以使用 let 命令、(( ))$(( )) 来进行算术运算。

使用 let 命令:

let result=2+3
echo $result

使用 (( ))

((result=2+3))
echo $result

使用 $(( ))

result=$((2+3))
echo $result

3.5 字符串操作

  1. 字符串拼接: 在 Bash 中,字符串拼接非常简单,只需将两个字符串放在一起即可:
str1="Hello"
str2=" World"
result=$str1$str2
echo $result
  1. 获取字符串长度: 使用 ${#variable_name} 可以获取字符串的长度:
str="Hello, World!"
length=${#str}
echo "The length of the string is $length"
  1. 字符串截取: 可以使用 ${variable_name:start:length} 来截取字符串,其中 start 是起始位置(从 0 开始),length 是截取的长度:
str="Hello, World!"
sub_str=${str:0:5}
echo $sub_str

四、流程控制语句

4.1 if语句

if 语句用于根据条件执行不同的代码块。基本语法如下:

if [ condition ]; then
    commands
elif [ another_condition ]; then
    commands
else
    commands
fi

例如,判断一个数是否大于 10:

num=15
if [ $num -gt 10 ]; then
    echo "$num is greater than 10"
else
    echo "$num is less than or equal to 10"
fi

if 语句中,[ condition ] 是条件判断部分。-gt 是比较运算符,表示大于。其他常用的比较运算符还有 -lt(小于)、-eq(等于)、-ne(不等于)等。

4.2 case语句

case 语句用于根据不同的值执行不同的代码块,类似于其他编程语言中的 switch - case 语句。基本语法如下:

case value in
    pattern1)
        commands
        ;;
    pattern2)
        commands
        ;;
    *)
        commands
        ;;
esac

例如,根据用户输入的选项执行不同的操作:

echo "Enter an option (a, b, or c):"
read option
case $option in
    a)
        echo "You chose option a"
        ;;
    b)
        echo "You chose option b"
        ;;
    c)
        echo "You chose option c"
        ;;
    *)
        echo "Invalid option"
        ;;
esac

在这个例子中,read 命令用于从用户获取输入。case 语句根据用户输入的 option 值来执行相应的代码块。*) 表示默认情况,当 option 不匹配任何前面的模式时,执行这部分代码。

4.3 for循环

for 循环用于重复执行一段代码。有两种常见的 for 循环语法。

  1. 传统的C风格for循环
for (( i=0; i<5; i++ )); do
    echo $i
done

在这个例子中,(( i=0; i<5; i++ )) 是循环的初始化、条件判断和增量部分。dodone 之间的代码块会被重复执行,直到条件 i<5 不成立。

  1. 遍历列表的for循环
fruits=("apple" "banana" "cherry")
for fruit in ${fruits[@]}; do
    echo "I like $fruit"
done

这里,fruits 是一个数组,${fruits[@]} 表示数组的所有元素。for 循环会依次将数组中的每个元素赋值给 fruit 变量,并执行代码块。

4.4 while循环

while 循环会在条件为真时重复执行代码块。基本语法如下:

while [ condition ]; do
    commands
done

例如,计算 1 到 10 的和:

sum=0
i=1
while [ $i -le 10 ]; do
    sum=$((sum + i))
    i=$((i + 1))
done
echo "The sum from 1 to 10 is $sum"

在这个例子中,[ $i -le 10 ] 是循环条件,-le 表示小于等于。只要 i 小于等于 10,循环就会继续执行,每次循环将 i 加到 sum 中,并将 i 加 1。

4.5 until循环

until 循环与 while 循环相反,它会在条件为假时重复执行代码块。基本语法如下:

until [ condition ]; do
    commands
done

例如,计算 1 到 10 的和(使用 until 循环):

sum=0
i=1
until [ $i -gt 10 ]; do
    sum=$((sum + i))
    i=$((i + 1))
done
echo "The sum from 1 to 10 is $sum"

这里,[ $i -gt 10 ] 是循环条件,只要 i 不大于 10,循环就会继续执行。

五、函数

5.1 函数定义

在 Bash 脚本中,函数是一段可重复使用的代码块。函数的定义语法如下:

function_name() {
    commands
    [ return value ]
}

例如,定义一个简单的函数来打印问候语:

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

在这个函数中,$1 是函数的第一个参数。当我们调用这个函数时,可以传递一个名字作为参数,函数会打印出相应的问候语。

5.2 函数调用

要调用函数,只需使用函数名并传递相应的参数(如果有):

greet "John"

这将输出 "Hello, John!"。

5.3 函数返回值

函数可以通过 return 语句返回一个值。返回值是一个整数,范围是 0 到 255。0 通常表示成功,其他值表示不同的错误情况。例如:

add() {
    result=$(( $1 + $2 ))
    return $result
}

add 2 3
return_value=$?
echo "The result of the addition is $return_value"

在这个例子中,add 函数将两个参数相加并返回结果。$? 用于获取上一个命令(这里是函数调用)的返回值。

六、处理命令行参数

6.1 位置参数

在 Bash 脚本中,可以通过位置参数来获取命令行传递给脚本的参数。位置参数用 $1$2$3 等表示,其中 $1 是第一个参数,$2 是第二个参数,以此类推。例如,创建一个脚本 args.sh

#!/bin/bash
echo "The first argument is $1"
echo "The second argument is $2"

然后在终端中执行脚本并传递参数:

./args.sh apple banana

输出将是:

The first argument is apple
The second argument is banana

6.2 特殊参数

  1. $#:表示传递给脚本的参数个数。
#!/bin/bash
echo "The number of arguments is $#"
  1. $@:表示所有参数,每个参数都是独立的字符串。
#!/bin/bash
for arg in "$@"; do
    echo $arg
done
  1. $*:也表示所有参数,但它将所有参数作为一个字符串。
#!/bin/bash
echo "All arguments as one string: $*"

6.3 处理选项

在实际应用中,脚本通常需要处理各种选项。可以使用 getopts 命令来处理简单的选项。例如,创建一个脚本 options.sh

#!/bin/bash
while getopts ":a:b:c" opt; do
    case $opt in
        a)
            echo "Option a with value $OPTARG"
            ;;
        b)
            echo "Option b with value $OPTARG"
            ;;
        c)
            echo "Option c"
            ;;
        \?)
            echo "Invalid option: -$OPTARG"
            ;;
    esac
done

在这个脚本中,while getopts ":a:b:c" opt; do 表示处理选项。a:b: 表示选项 ab 后面需要跟一个值,c 表示选项 c 不需要跟值。OPTARG 用于获取选项后面的值。\? 用于处理无效选项。执行脚本时,可以这样传递选项:

./options.sh -a value1 -b value2 -c

输出将是:

Option a with value value1
Option b with value value2
Option c

七、文件操作

7.1 读取文件

在 Bash 脚本中,可以使用 while read 循环来逐行读取文件内容。例如,创建一个文件 example.txt 并写入一些内容:

line1
line2
line3

然后编写一个脚本来读取这个文件:

#!/bin/bash
while read line; do
    echo $line
done < example.txt

在这个脚本中,while read line; do 循环每次从 example.txt 文件中读取一行,并将其赋值给 line 变量,然后打印出来。

7.2 写入文件

可以使用重定向符号 >>> 来写入文件。> 会覆盖文件内容,>> 会追加到文件末尾。例如:

echo "This is a new line" > new_file.txt
echo "This is another line" >> new_file.txt

7.3 文件和目录操作命令

  1. 创建目录:使用 mkdir 命令,例如 mkdir new_directory
  2. 删除目录:使用 rmdir 命令(目录必须为空),例如 rmdir empty_directory。如果目录不为空,可以使用 rm -r 命令,例如 rm -r non_empty_directory
  3. 复制文件:使用 cp 命令,例如 cp source_file destination_file
  4. 移动文件:使用 mv 命令,例如 mv old_file new_filemv file directory(将文件移动到目录中)。

八、错误处理

8.1 检查命令返回值

在 Bash 中,每个命令执行后都会返回一个状态码。0 表示命令成功执行,非 0 表示出现错误。我们可以通过 $? 变量来获取上一个命令的返回值。例如:

ls non_existent_file
if [ $? -ne 0 ]; then
    echo "The file does not exist"
fi

在这个例子中,ls non_existent_file 命令尝试列出一个不存在的文件,它会返回一个非 0 的状态码。if [ $? -ne 0 ]; then 检查状态码是否不为 0,如果是,则打印出错误信息。

8.2 set -e

set -e 命令可以使脚本在遇到任何错误(即命令返回非 0 状态码)时立即停止执行。例如:

#!/bin/bash
set -e
ls non_existent_file
echo "This line will not be printed if the previous command fails"

在这个脚本中,由于 ls non_existent_file 会失败,并且设置了 set -e,脚本会在这一行停止执行,后面的 echo 语句不会被执行。

8.3 trap命令

trap 命令用于捕获信号并执行相应的处理程序。例如,我们可以捕获 SIGINT 信号(通常由用户按下 Ctrl + C 产生)并执行自定义的清理操作:

#!/bin/bash
trap "echo 'Cleaning up...'; exit" SIGINT

# 脚本的主要内容
while true; do
    echo "Press Ctrl + C to stop"
    sleep 1
done

在这个脚本中,trap "echo 'Cleaning up...'; exit" SIGINT 表示当捕获到 SIGINT 信号时,执行 echo 'Cleaning up...'; exit 这两个命令,即打印清理信息并退出脚本。

九、Bash脚本调试

9.1 使用set -x

set -x 命令可以在脚本执行时打印出每一条执行的命令及其参数,这对于调试脚本非常有帮助。例如:

#!/bin/bash
set -x
num1=10
num2=20
result=$((num1 + num2))
echo "The result is $result"

执行这个脚本时,你会看到每一条命令及其执行结果被打印出来,这样可以方便地检查脚本的执行流程和变量的值。

9.2 使用bash -n

bash -n 选项可以用于检查脚本的语法错误,而不实际执行脚本。例如:

bash -n script.sh

如果脚本 script.sh 存在语法错误,bash -n 会输出错误信息,帮助你定位问题。

9.3 使用echo调试

在脚本中适当的位置使用 echo 命令打印变量的值和执行状态,也是一种简单有效的调试方法。例如:

#!/bin/bash
num1=10
echo "num1 is $num1"
num2=20
echo "num2 is $num2"
result=$((num1 + num2))
echo "The result of num1 + num2 is $result"

通过打印变量的值,可以确保变量被正确赋值,并且计算结果符合预期。

通过以上对 Bash 脚本基础的介绍,你已经可以开始编写各种实用的脚本,自动化处理各种系统管理和日常任务。随着对 Bash 脚本的深入学习,你会发现它在系统管理、自动化测试、数据处理等领域有着广泛的应用。不断实践和探索,你将能够编写出更复杂、更高效的 Bash 脚本。