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

Bash基础语法入门指南

2023-03-211.9k 阅读

1. Bash 简介

Bash(Bourne Again SHell)是 Linux 和 macOS 系统中最常用的 shell 程序,它是 Bourne Shell 的一个增强版本。Shell 是用户与操作系统内核之间的接口,通过命令行输入,Bash 解释并执行用户的指令,实现对系统的各种操作。

2. 变量

2.1 变量定义

在 Bash 中定义变量非常简单,语法为:变量名=变量值。例如:

name="John"

注意,等号两边不能有空格。变量名一般遵循以下规则:

  • 由字母、数字和下划线组成。
  • 不能以数字开头。

2.2 变量引用

引用变量时,在变量名前加上 $ 符号,例如:

name="John"
echo $name

上面的代码会输出 John。如果要在字符串中引用变量,可以使用双引号,例如:

name="John"
echo "Hello, $name"

这会输出 Hello, John。如果使用单引号,变量不会被替换,例如:

name="John"
echo 'Hello, $name'

输出为 Hello, $name

2.3 环境变量

环境变量是具有特殊意义的变量,会影响 shell 以及运行在其中的程序的行为。常见的环境变量有 PATHHOME 等。可以使用 export 命令将普通变量提升为环境变量,例如:

myvar="test"
export myvar

也可以在定义变量时直接声明为环境变量:

export myenvvar="envtest"

可以通过 echo $环境变量名 查看环境变量的值,例如 echo $PATH 会显示系统的可执行文件搜索路径。

2.4 只读变量

使用 readonly 命令可以将变量设置为只读,一旦设置为只读,就不能再修改其值。例如:

readonly myreadonlyvar="readonlytest"
myreadonlyvar="newvalue"  # 这会报错

运行上述代码,会提示 bash: myreadonlyvar: readonly variable

3. 数据类型

3.1 字符串

Bash 中的字符串无需特别声明类型,直接定义即可。字符串可以用单引号或双引号括起来。单引号内的所有字符都按字面意思处理,双引号内的变量会被替换,并且支持转义字符。例如:

single_str='This is a single - quoted string with $name'
double_str="This is a double - quoted string with $name"
echo $single_str
echo $double_str

输出结果:

This is a single - quoted string with $name
This is a double - quoted string with John

3.2 数组

Bash 支持一维数组,定义数组的方式有多种。例如:

# 方式一
my_array=(element1 element2 element3)
# 方式二
my_array[0]=element1
my_array[1]=element2
my_array[2]=element3

访问数组元素使用 ${数组名[索引]},例如:

echo ${my_array[0]}

这会输出 element1。获取数组所有元素使用 ${数组名[@]}${数组名[*]},例如:

echo ${my_array[@]}

会输出 element1 element2 element3。获取数组长度使用 ${#数组名[@]},例如:

echo ${#my_array[@]}

输出 3,即数组的元素个数。

4. 运算符

4.1 算术运算符

Bash 支持基本的算术运算,如加、减、乘、除等。但是需要使用特定的语法。例如,使用 $((运算表达式))let "变量=运算表达式"

a=5
b=3
# 使用 $((运算表达式))
result1=$((a + b))
echo $result1
# 使用 let
let "result2=a - b"
echo $result2

上述代码分别输出 82。支持的运算符有 +(加)、-(减)、*(乘)、/(除)、%(取余)、**(幂运算)等。

4.2 比较运算符

比较运算符用于比较两个数的大小等关系。常用的有 -eq(等于)、-ne(不等于)、-gt(大于)、-lt(小于)、-ge(大于等于)、-le(小于等于)。这些运算符一般用于条件判断语句中。例如:

a=5
b=3
if [ $a -gt $b ]; then
    echo "$a 大于 $b"
fi

上述代码会输出 5 大于 3

4.3 逻辑运算符

逻辑运算符有 &&(逻辑与)、||(逻辑或)、!(逻辑非)。例如:

a=5
b=3
if [ $a -gt $b ] && [ $a -lt 10 ]; then
    echo "$a 大于 $b 且小于 10"
fi

上述代码会输出 5 大于 3 且小于 10

4.4 字符串运算符

字符串运算符用于对字符串进行比较等操作。例如 =(字符串相等)、!=(字符串不相等)、-z(字符串长度为 0)、-n(字符串长度不为 0)。例如:

str1="hello"
str2="world"
if [ $str1 != $str2 ]; then
    echo "字符串不相等"
fi

上述代码会输出 字符串不相等

5. 条件判断

5.1 if 语句

if 语句是最基本的条件判断语句,语法如下:

if [ 条件判断 ]; then
    命令1
    命令2
    ...
elif [ 条件判断2 ]; then
    命令3
    命令4
    ...
else
    命令5
    命令6
    ...
fi

例如:

num=10
if [ $num -gt 15 ]; then
    echo "数字大于 15"
elif [ $num -gt 5 ]; then
    echo "数字大于 5 小于等于 15"
else
    echo "数字小于等于 5"
fi

上述代码会输出 数字大于 5 小于等于 15

5.2 case 语句

case 语句用于多分支选择,语法如下:

case 变量 in
模式1)
    命令1
    命令2
    ;;
模式2)
    命令3
    命令4
    ;;
*)
    命令5
    命令6
    ;;
esac

例如:

fruit="apple"
case $fruit in
apple)
    echo "这是苹果"
    ;;
banana)
    echo "这是香蕉"
    ;;
*)
    echo "不知道这是什么水果"
    ;;
esac

上述代码会输出 这是苹果

6. 循环

6.1 for 循环

for 循环用于按指定次数执行一组命令。语法有两种常见形式。 形式一:

for 变量 in 列表; do
    命令1
    命令2
    ...
done

例如:

for num in 1 2 3 4 5; do
    echo $num
done

上述代码会依次输出 1 2 3 4 5

形式二:

for (( 初始值; 条件判断; 增量 )); do
    命令1
    命令2
    ...
done

例如:

for ((i = 1; i <= 5; i++)); do
    echo $i
done

同样会依次输出 15

6.2 while 循环

while 循环只要条件为真就会一直执行。语法如下:

while [ 条件判断 ]; do
    命令1
    命令2
    ...
done

例如:

num=1
while [ $num -le 5 ]; do
    echo $num
    num=$((num + 1))
done

上述代码会输出 15

6.3 until 循环

until 循环与 while 循环相反,只要条件为假就会一直执行,直到条件为真停止。语法如下:

until [ 条件判断 ]; do
    命令1
    命令2
    ...
done

例如:

num=1
until [ $num -gt 5 ]; do
    echo $num
    num=$((num + 1))
done

同样会输出 15

7. 函数

7.1 函数定义

在 Bash 中定义函数的语法如下:

函数名() {
    命令1
    命令2
    ...
    [return 返回值]
}

例如:

print_hello() {
    echo "Hello, World!"
}

7.2 函数调用

定义好函数后,可以直接通过函数名调用,例如:

print_hello

上述代码会输出 Hello, World!

7.3 函数参数

函数可以接受参数,在函数内部通过 $1$2 等变量来访问参数,$0 表示脚本本身的名字(如果在脚本中定义函数)。例如:

add_numbers() {
    result=$(( $1 + $2 ))
    echo "两数之和为: $result"
}
add_numbers 3 5

上述代码会输出 两数之和为: 8

7.4 函数返回值

可以使用 return 语句返回一个值,返回值范围是 0 到 255。例如:

square() {
    result=$(( $1 * $1 ))
    return $result
}
square 5
return_value=$?
echo "返回值为: $return_value"

上述代码会输出 返回值为: 25$? 用于获取上一个命令(这里是函数)的返回值。

8. 文件操作

8.1 文件创建

使用 touch 命令可以创建一个空文件,例如:

touch newfile.txt

8.2 文件读取

可以使用 cat 命令读取文件内容并输出到终端,例如:

cat newfile.txt

如果文件较大,可以使用 less 命令,它支持分页查看,例如:

less newfile.txt

q 键可以退出 less 查看。

8.3 文件写入

使用 echo 配合重定向符号 >>> 可以写入文件。> 会覆盖原有内容,>> 会追加内容。例如:

echo "这是写入的内容" > newfile.txt
echo "这是追加的内容" >> newfile.txt

8.4 文件删除

使用 rm 命令可以删除文件,例如:

rm newfile.txt

如果要删除目录及目录下所有文件,使用 rm -r,例如:

rm -r mydirectory

注意,使用 rm 命令要谨慎,因为删除后数据难以恢复。

8.5 文件权限操作

使用 chmod 命令可以改变文件或目录的权限。权限分为读(r)、写(w)、执行(x)。例如,给文件 testfile 添加可执行权限:

chmod +x testfile

可以使用数字表示权限,例如 755 表示所有者有读、写、执行权限,组用户和其他用户有读和执行权限。设置文件权限为 755

chmod 755 testfile

9. 脚本执行

9.1 直接执行

将一系列 Bash 命令保存到一个文件中,例如 myscript.sh,然后给文件添加可执行权限:

chmod +x myscript.sh

之后可以直接执行脚本:

./myscript.sh

9.2 使用 bash 命令执行

也可以不添加可执行权限,直接使用 bash 命令执行脚本,例如:

bash myscript.sh

在脚本开头添加 #!/bin/bash 称为 shebang,它指定了执行该脚本的程序,这样在添加可执行权限后就可以直接执行脚本,而不需要在命令前加上 bash。例如:

#!/bin/bash
echo "这是一个脚本"

保存为 myscript.sh,添加可执行权限后,直接执行 ./myscript.sh 即可。

10. 命令替换

10.1 反引号方式

可以使用反引号(`)将命令括起来,Bash 会执行反引号中的命令,并将输出结果替换到原位置。例如:

current_date=`date`
echo "当前日期是: $current_date"

上述代码会输出当前日期,例如 当前日期是: Fri Aug 11 15:30:00 CST 2023

10.2 $( ) 方式

更推荐使用 $( ) 方式进行命令替换,例如:

current_dir=$(pwd)
echo "当前目录是: $current_dir"

这会输出当前所在目录路径。

11. 输入输出重定向

11.1 输出重定向

前面提到过使用 > 覆盖输出和 >> 追加输出到文件。例如:

ls > filelist.txt  # 将 ls 命令的输出覆盖写入 filelist.txt
echo "追加内容" >> filelist.txt  # 追加内容到 filelist.txt

11.2 输入重定向

使用 < 可以进行输入重定向,将文件内容作为命令的输入。例如,假设有一个 input.txt 文件内容为 Hello,可以这样使用:

read line < input.txt
echo "读取的内容是: $line"

上述代码会输出 读取的内容是: Hello

11.3 错误输出重定向

使用 2> 可以将错误输出重定向到文件。例如,执行一个不存在的命令:

nonexistentcommand 2> error.log

命令执行产生的错误信息会被写入 error.log 文件。也可以同时重定向标准输出和错误输出,例如:

command 2>&1 > output.log

这样标准输出和错误输出都会被写入 output.log 文件。

12. 管道

管道(|)可以将一个命令的输出作为另一个命令的输入。例如,要查看当前目录下文件数量,可以这样:

ls | wc -l

ls 命令列出目录下的文件,wc -l 统计行数,这里就是文件数量。再比如,要在当前目录及子目录中查找包含特定字符串的文件,可以使用:

grep -r "特定字符串" . | less

grep -r "特定字符串" . 会递归查找当前目录(.)下包含特定字符串的文件及行,| less 将结果通过 less 分页显示。

13. 信号处理

13.1 捕获信号

在脚本中可以使用 trap 命令捕获信号并执行相应的处理程序。例如,捕获 SIGINT(通常是 Ctrl+C)信号,在用户按下 Ctrl+C 时输出一条提示信息:

#!/bin/bash
trap 'echo "你按下了 Ctrl+C,程序即将退出。"' SIGINT
while true; do
    echo "程序正在运行..."
    sleep 1
done

上述脚本在运行时,当用户按下 Ctrl+C,会输出提示信息后退出。

13.2 发送信号

使用 kill 命令可以向进程发送信号。例如,要终止一个进程,首先通过 ps 命令找到进程 ID,假设进程 ID 为 1234,可以这样发送终止信号:

kill 1234

如果要发送其他信号,可以使用 -信号编号-信号名 的方式,例如发送 SIGTERM 信号:

kill -SIGTERM 1234

14. 调试

14.1 调试选项

在脚本开头添加 set -x 可以开启调试模式,Bash 会在执行每一条命令前打印命令及其参数。例如:

#!/bin/bash
set -x
a=5
b=3
result=$((a + b))
echo "结果是: $result"

运行脚本时,会看到类似如下输出:

+ a=5
+ b=3
+ result=8
+ echo '结果是: 8'
结果是: 8

+ 开头的行就是调试信息,显示了实际执行的命令。调试完成后,可以使用 set +x 关闭调试模式。

14.2 错误处理

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

#!/bin/bash
set -e
nonexistentcommand  # 这会导致错误
echo "这行不会被执行"

上述脚本运行到 nonexistentcommand 时会报错并停止执行,不会输出 这行不会被执行

通过掌握以上这些 Bash 基础语法,你可以编写各种自动化脚本,提高在 Linux 或 macOS 系统下的工作效率,无论是系统管理、文件处理还是其他各种任务,Bash 脚本都能发挥巨大的作用。在实际应用中,不断练习和尝试,结合具体需求编写合适的脚本,进一步提升自己在命令行环境下的操作能力。