【Linux】bash脚本使用

文章目录

    • [0 前言](#0 前言)
    • [1 变量](#1 变量)
      • [1.1 基本使用方式](#1.1 基本使用方式)
      • [1.2 特殊变量](#1.2 特殊变量)
      • [1.3 数组变量](#1.3 数组变量)
    • [2 符号](#2 符号)
      • [2.1 字符串符号](#2.1 字符串符号)
      • [2.2 分号](#2.2 分号)
      • [2.3 运算符号](#2.3 运算符号)
      • [2.4 输入输出重定向符号](#2.4 输入输出重定向符号)
    • [3 条件语句](#3 条件语句)
      • [3.1 if-else](#3.1 if-else)
      • [3.2 switch-case](#3.2 switch-case)
    • [4 循环语句](#4 循环语句)
      • [4.1 for循环](#4.1 for循环)
      • [4.2 while循环](#4.2 while循环)
      • [4.3 循环结束](#4.3 循环结束)
    • [5 函数](#5 函数)
    • [6 常用基本命令](#6 常用基本命令)
      • [6.1 echo](#6.1 echo)
      • [6.2 printf](#6.2 printf)
      • [6.3 export](#6.3 export)
      • [6.4 source](#6.4 source)
      • [6.5 read](#6.5 read)

0 前言

因为工作需要频繁接触Linux,同时工作内容也和Linux相关,感觉有必要系统学习一下bash脚本的语法,特此记录。

本文以菜鸟教程为基础,提炼最常用内容,并结合工作经历进行补充,适合有一定编程基础的新手入门。

bash脚本基本格式

bash脚本第一行都需要加上#!/bin/bash,这个是用来指定脚本默认的执行程序的,所以理论上也可以选择其他的执行程序,比如#!/bin/sh

1 变量

1.1 基本使用方式

  • 定义变量val="test"

    变量可以被重复定义,直接多次赋值即可,不需要其他操作。注意,等号两边不能加空格(不是语法问题,是可能导致值错误)。常用的变量主要有两种类型,一个是数值型,还有就是字符串型,后者要加引号。

  • 使用变量echo $val 或者 echo ${val}

    变量使用时需要加上一个美元符号,或者是使用大括号括起来,这样可以更加清晰地界定变量名称的范围。

  • 删除变量unset val

    删除该变量就不能再使用,如果要使用就需要重新定义。

  • 只读变量readonly val

    这样可以设置该变量为只读变量,不能对该变量重新定义修改。

1.2 特殊变量

  • $1, $2:类似这样一个美元符号加上一个数字的组合,是表示脚本执行时的参数。如果是在函数体内部,则表示函数的参数。如果数字大于10,则需要加大括号,如${10}
  • $0:表示这个脚本文件名(包含路径)。
  • $?:这个表示该指令上一条指令执行返回的结果。

1.3 数组变量

定义一个数组变量的基本格式如下

bash 复制代码
array_name=(value0 value1 value2 value3)

注意,这里元素之间分隔是不要逗号的,直接用空格分隔,或者换行分隔也行。另外还可以单独定义数组的各个分量,比如:

bash 复制代码
array_name[0]=value0
array_name[1]=value1
array_name[n]=valuen

而且这个数组的下标范围不需要像C语言那样先设置好,这个是没有数字范围限制的。

如果要取数组中的元素,则可以通过${数组名[下标]}的方式来读取。这里面的中括号两侧不需要加空格。如果要获取数组的长度,可以按照下面这些方式来实现。

bash 复制代码
# 取得数组元素的个数
length=${#array_name[@]}
# 或者
length=${#array_name[*]}
# 取得数组单个元素的长度
length=${#array_name[n]}

这里如果不加#,单纯使用${array_name[*]}或者${array_name[@]}得到的是数组中所有的元素。

这里顺便补充一下这个#的作用,除了可以获取数组的长度外,还可以获取字符串的长度,比如

bash 复制代码
string="abcd"
echo ${#string}   # 输出 4

关联数组/字典

bash还支持关联数组或者说字典,即索引内容可以是任意的字符串或者其他整数,比如

bash 复制代码
site["google"]="www.google.com"

但这里要提前声明这个数组是关联数组,使用declare -A的方式。所以一般使用的格式如下所示。

bash 复制代码
declare -A site
site["google"]="www.google.com"
site["runoob"]="www.runoob.com"
site["taobao"]="www.taobao.com"

然后和上面获取所有数组元素一样,这里也可以获取所有key:

bash 复制代码
declare -A site
site["google"]="www.google.com"
site["runoob"]="www.runoob.com"
site["taobao"]="www.taobao.com"

echo "数组的键为: ${!site[*]}"
echo "数组的键为: ${!site[@]}"

2 符号

2.1 字符串符号

当需要给变量赋字符串时,就需要用引号引起来,既可以使用单引号,也可以使用双引号。如果使用单引号的话,字符串会原样输出,里面的变量和转义符号无效。而双引号则可以使用变量和转义符号等。

此外,字符串也可以直接在赋值的时候就拼接,这里既可以使用单引号也可以使用双引号。比如

bash 复制代码
name="test"
greeting="hello, "$name" !"
# hello, test !

2.2 分号

bash代码不需要行末以分号结尾,但有时候仍然可以看到使用分号的情况,其实就是分隔命令,这样可以实现一行写好几条指令,方便在命令终端直接执行。

另外如果是两个分号;;,主要是用在case语句中,用来表示一种case结束,相当于C语言中的break

2.3 运算符号

算术运算符

bash脚本是不支持数学运算的,如果要进行数学运算,需要使用指令,最常用的就是expr,举个例子

bash 复制代码
val=`expr 2 + 2`
echo "两数之和为:$val"

这里有几个注意点:

  • 表达式和运算符之间要有空格,即2 + 2
  • 表达式要使用 反引号给括起来,注意不是单引号

关系运算符

主要是比较数值的大小或判断是否相等,也就是形成条件表达式,主要有以下几种

运算符 说明 举例
-eq 检测两个数是否相等,相等返回 true。 [ a -eq b ] 返回 false。
-ne 检测两个数是否不相等,不相等返回 true。 [ a -ne b ] 返回 true。
-gt 检测左边的数是否大于右边的,如果是,则返回 true。 [ a -gt b ] 返回 false。
-lt 检测左边的数是否小于右边的,如果是,则返回 true。 [ a -lt b ] 返回 true。
-ge 检测左边的数是否大于等于右边的,如果是,则返回 true。 [ a -ge b ] 返回 false。
-le 检测左边的数是否小于等于右边的,如果是,则返回 true。 [ a -le b ] 返回 true。

同样,这里也有几个注意点:

  • 和前面运算符一样,这里的中括号是需要加空格的。结合前面的内容,可以总结为符号两边都要加空格,除了赋值和美元符号(变量相关)
  • 记忆方式:eq是equal的缩写,然后带e的都表示有等号,lt是lower than的缩写,gt是greater than的缩写,那自然可以得到le是小于等于,ge是大于等于。

另外,如果想用最简单的< > <= >=这些符号,可以使用(( )),把比较表达式放在双小括号内,但记得也需要加空格。 比如 (( $a > 10 ))

布尔运算符

运算符 说明 举例
! 非运算,表达式为 true 则返回 false,否则返回 true。 [ ! false ] 返回 true。
-o 或运算,有一个表达式为 true 则返回 true。 [ a -lt 20 -o b -gt 100 ] 返回 true。
-a 与运算,两个表达式都为 true 才返回 true。 [ a -lt 20 -a b -gt 100 ] 返回 false。

逻辑运算符

运算符 说明 举例
&& 逻辑的 AND [[ a -lt 100 \&\& b -gt 100 ]] 返回 false
` `

字符串运算符

运算符 说明 举例
= 检测两个字符串是否相等,相等返回 true。 [ a = b ] 返回 false。
!= 检测两个字符串是否不相等,不相等返回 true。 [ a != b ] 返回 true。
-z 检测字符串长度是否为0,为0返回 true。 [ -z $a ] 返回 false。
-n 检测字符串长度是否不为 0,不为 0 返回 true。 [ -n "$a" ] 返回 true。
$ 检测字符串是否不为空,不为空返回 true。 [ $a ] 返回 true。

除了=判断字符串是否相等外,还可以使用双等号==,但一般需要加双中括号,[[ $s == "test.bin" ]]在单中括号中使用双等号是兼容性差的写法,部分sh可能不支持。 此外,双括号内还可以使用模式匹配和正则表达式。比如

bash 复制代码
[[ $var == *.txt ]]  # 模式匹配
[[ $var =~ ^[0-9]+$ ]]  # 正则表达式

文件测试运算符

操作符 说明 举例
-d file 检测文件是否是目录,如果是,则返回 true。 [ -d $file ] 返回 false。
-f file 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 [ -f $file ] 返回 true。
-r file 检测文件是否可读,如果是,则返回 true。 [ -r $file ] 返回 true。
-w file 检测文件是否可写,如果是,则返回 true。 [ -w $file ] 返回 true。
-x file 检测文件是否可执行,如果是,则返回 true。 [ -x $file ] 返回 true。
-s file 检测文件是否为空(文件大小是否大于0),不为空返回 true。 [ -s $file ] 返回 true。
-e file 检测文件(包括目录)是否存在,如果是,则返回 true。 [ -e $file ] 返回 true。

2.4 输入输出重定向符号

输出重定向

bash 复制代码
command1 > file1

这个是指将command1指令执行的结果保存在file1中,而且是完全覆盖;

如果是这样:

bash 复制代码
command1 >> file1

则是指将内容追加在file1后面。

输入重定向

bash 复制代码
command1 < file1

把文件作为参数传入,然后再执行指令。

特殊重定向

如果希望执行某个指令,但又不希望在屏幕上显示输出结果,就可以将输出重定向到/dev/null,他是一个特殊的文件,写入到他的内容都会被丢弃。

bash 复制代码
$ command > /dev/null

3 条件语句

类比C语言,条件语句主要是两种,一个是if-else语句,一个是switch-case语句,bash脚本中也都有,下面分别介绍。

3.1 if-else

基本的语法格式如下所示。

bash 复制代码
if condition1
then
    command1
elif condition2 
then 
    command2
else
    commandN
fi

可以看到,条件选择语句没有大括号限定范围,而是使用fi,即if倒着写来表示语句的结束,后面要介绍的case语句也是一样。另外elifelse部分是可选的部分,不一定有。也可以写在同一行,不过语句后面要加上分号;

bash 复制代码
if [ $(ps -ef | grep -c "ssh") -gt 1 ]; then echo "true"; fi

3.2 switch-case

其基本的语法格式如下所示。

bash 复制代码
case 值 in
模式1)
    command1
    command2
    ...
    commandN
    ;;
模式2)
    command1
    command2
    ...
    commandN
    ;;
esac

注意,这里每个选项都需要加一个右括号,然后每个case最后用两个分号分隔,表示break语句,最后要加上esac和前面的case呼应,表示一个区域,等同于大括号的作用。

4 循环语句

和C语言一样,循环语句也具有两种,一种是for循环,一种是while循环。

4.1 for循环

其基本的代码格式如下所示。

bash 复制代码
for var in item1 item2 ... itemN
do
    command1
    command2
    ...
    commandN
done

这里do...done就是用来表示大括号的,中间是循环体。

上面in后面的内容可以是多个变量,也可以是一个数组变量,或者通配符,比如

bash 复制代码
for file in *
do
    echo "处理: $file"
done

4.2 while循环

基本的语法格式如下所示

bash 复制代码
while condition
do
    command
done

4.3 循环结束

在bash中也有break和continue指令。

bash 复制代码
#!/bin/bash
while :
do
    echo -n "输入 1 到 5 之间的数字:"
    read aNum
    case $aNum in
        1|2|3|4|5) echo "你输入的数字为 $aNum!"
        ;;
        *) echo "你输入的数字不是 1 到 5 之间的! 游戏结束"
            break
        ;;
    esac
done

注意这里的break指令不是跳出switch-case语句,而是跳出外面的while循环语句。

5 函数

shell脚本中也可以自定义函数,定义好之后就可以在shell脚本中随便调用。函数定义格式如下所示。

bash 复制代码
[ function ] funname ()
{
    action;
    [return int;]
}

注意,这里中括号的部分是可选的,也就是说定义函数时可以不写 function ,也可以没有返回值。

函数调用时,直接使用函数名字即可,不需要加括号。

6 常用基本命令

6.1 echo

这个应该是最常用的bash指令了。除了最常见的输出信息到终端外,这个指令还可以结合其他符号实现一些高级功能。

常用参数

  • -e:识别转义字符,包括终端指定颜色的指令也需要这个
  • -n:不换行输出,指这一行末尾不加换行符,默认是会加换行符号的。

结合管道运算符|

有时候需要调用函数或者另外的脚本且需要传递参数时,就可以使用echo para | other_script.sh这种格式来实现调用其他脚本并传递参数。

结合输入输出重定向符号>,>>

结合输入输出重定向符号,可以实现文本文件的写入和添加。

6.2 printf

除了echo指令外,printf也可以用来输出内容到终端,而且支持更加复杂的格式化输出,其用法和C语言中的printf差不多。

bash 复制代码
# 整数
printf "Decimal: %d\nHex: %x\nOctal: %o\n" 255 255 255

# 浮点数
printf "Float: %f\nScientific: %e\n" 3.14159 3.14159

# 字符串
printf "Name: %s\n" "Bob"

# 字符
printf "First letter: %c\n" "A"

6.3 export

export 用于声明一个环境变量:export variable=value,该环境变量只在本进程和其子进程中可以访问。

如果是在系统级的配置文件中,如/etc/profile中 export 一个变量,那么这个变量就会在整个系统运行期间都起作用。即使新开shell会话连接甚至,重启了服务器仍可生效。

如果在用户级 ~/.bash_profile 之类的文件中,则每次启动shell 都会去读这个文件,所以每次打开shell也是可以取到这个值的。

如果在某一次运行中,手动 export 一个变量,则只在这次shell的使用中,才能访问这个变量,或者在该shell启动的启动程序中,也是可以访问这个变量的,因为它们是这个shell的子进程。新开shell连接及重启服务器都会失效。

总之,export 作用就是 把本地变量变成全局变量(实际中叫环境变量)

参考链接

6.4 source

使shell读入指定的shell脚本文件并依次执行文件中的所有语句。

source命令通常用于重新执行刚修改的初始化文件,使之立即生效,而不必注销并重新登录。

bash 复制代码
source filename 
. filename # 和上面等价

另外还需要注意source和直接执行脚本./other.sh的区别,后者会开启一个子shell,会继承父shell的环境变量,但子shell执行得到的环境变量不影响父shell。而source指令是把脚本复制到本窗口继续执行,不会新建shell,所以环境变量都是共用的,也会互相影响。

参考链接

6.5 read

如果脚本中需要输入一些内容,就可以使用read指令,最常见的用法就是

bash 复制代码
read -p "提示信息" val

指将输入的内容存在val变量中。其他用法可以参考这个链接

相关推荐
i学长的猫1 小时前
PM2 管理 Cloudflared 隧道 Neo-mac 及后台运行
linux·编辑器·vim
GIOTTO情1 小时前
2026小红书投流新规下,基于Infoseek API的媒介投放自动化方案
java·linux·开发语言
FIT2CLOUD飞致云1 小时前
新增QQ频道与vLLM管理功能,1Panel v2.1.4版本发布
linux·服务器·ai·开源·1panel
wbs_scy1 小时前
Linux 基础 IO 初步解析:从 C 库函数到系统调用,理解文件操作本质
linux·运维·服务器
暴力求解2 小时前
Linux---磁盘与文件系统(三)
linux·运维·服务器
deng-c-f2 小时前
Linux C/C++ 学习日记(80):Kafka(八):topic会自动创建吗?
linux·c++·学习·karfka
rain_in_spring2 小时前
十、项目:营销中心
linux·运维·服务器
U盘失踪了2 小时前
Debian 使用 Xfce 桌面
linux·运维
北冥湖畔的燕雀2 小时前
Linux Shell开发实战:从零打造命令行工具
linux·运维·服务器