Shell学习笔记

2024/9/4 Linux

# Shell 脚本

Shell 脚本的第一行一般是:

#!/bin/bash
1

#! 是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种 Shell。

Linux 的 Shell 种类众多,常见的有:

  • Bourne Shell(/usr/bin/sh或/bin/sh)
  • Bourne Again Shell(/bin/bash)
  • C Shell(/usr/bin/csh)
  • K Shell(/usr/bin/ksh)
  • Shell for Root(/sbin/sh)
  • ……

# 运行 Shell 脚本的两种方法

# 作为可执行程序

chmod +x ./test.sh # 使脚本具有执行权限
./test.sh # 执行脚本
1
2

注意,一定要写成 ./test.sh,而不是 test.sh,运行其它二进制的程序也一样,直接写 test.sh,linux 系统会去 PATH 里寻找有没有叫 test.sh 的,而只有 /bin, /sbin, /usr/bin,/usr/sbin 等在 PATH 里,你的当前目录通常不在 PATH 里,所以写成 test.sh 是会找不到命令的,要用 ./test.sh 告诉系统说,就在当前目录找。

# 作为解释器参数

/bin/sh ./test.sh # 执行脚本
sh ./test.sh # 执行脚本
1
2

# Shell 脚本名称及路径操作

变量 作用
$(basename $0) 获取当前正在执行的脚本名称
$(cd $(dirname $0); pwd) 获取当前正在执行的脚本的父目录的绝对路径

# pwd

Linux pwd(英文全拼:print work directory) 命令用于显示工作目录。

执行 pwd 指令可立刻得知您目前所在的工作目录的绝对路径名称。

# basename

basename 用于获取文件路径中的文件名部分。

basename 命令的语法如下:

basename NAME [SUFFIX]
basename OPTION... NAME...
1
2

其中,OPTION 是可选的参数,NAME 是要处理的文件名或路径。

basename 命令会将 NAME 中的路径部分去除,只保留文件名部分,并将结果输出。

以下是一些常用的 basename 命令的示例:

# 输出结果为file.txt,去除了路径部分
basename /path/to/file.txt

# 输出结果为directory,去除了路径部分,并且保留了末尾的斜杠
basename /path/to/directory/

# 输出结果为file,去除了路径部分,并且去除了指定的文件扩展名
basename /path/to/file.txt .txt
1
2
3
4
5
6
7
8

# dirname

dirname 用于获取指定路径的父目录,返回指定路径中最后一个斜杠(/)之前的部分。当指定的路径不存在父目录时,dirname命令会返回根目录(/)

# Shell 变量

https://www.runoob.com/linux/linux-shell-passing-arguments.html (opens new window)

Shell 变量名只能包含字母、数字和下划线,不能以数字开头。

变量名与等号之间不能有空格。等号两侧避免使用空格:

# 正确的赋值
variable_name=value

# 有可能会导致错误
variable_name = value
1
2
3
4
5

使用一个定义过的变量,只要在变量名前面加美元符号即可,如:

echo $variable_name
echo ${variable_name}
1
2

变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界。推荐给所有变量加上花括号,避免不必要的错误,如:

for skill in Java Python Shell; do
	# 解释器会把$skillScript当成一个变量(其值为空)
    echo "I am good at $skillScript" 
    echo "I am good at ${skill}Script"
done
1
2
3
4
5

已定义的变量,可以被重新定义,如:

variable_name=new_value # 使用变量的时候才加美元符($)
1

# 字符串变量

字符串变量:使用单引号 ' 或双引号 " 来定义字符串,例如:

my_string='Hello, World!'
或者
my_string="Hello, World!"
1
2
3

单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的。

双引号里可以有变量,可以出现转义字符。

拼接字符串:

your_name="runoob"
# 使用双引号拼接
greeting_1="hello, "$your_name" !"
greeting_2="hello, ${your_name} !"
echo $greeting_1  $greeting_2

# 使用单引号拼接
greeting_3='hello, '$your_name' !'
greeting_4='hello, ${your_name} !' # 原样输出hello, ${your_name} !
echo $greeting_3  $greeting_4
1
2
3
4
5
6
7
8
9
10

获取字符串长度:

string="abcd"
echo ${#string} # 等价于 ${#string[0]}
1
2

# 整数变量

整数变量: 在一些Shell中,你可以使用 declaretypeset 命令来声明整数变量。

declare -i my_integer=42
1

# 数组变量

数组变量: Shell 也支持数组,允许你在一个变量中存储多个值。

在 Shell 中,用括号来表示数组,数组元素用"空格"符号分割开。定义数组的一般形式为:

数组名=(值1 值2 ... 值n)
1

例如:

array_name=(value0 value1 value2 value3)
1

或者:

array_name=(
value0
value1
value2
value3
)
1
2
3
4
5
6

整数索引数组:

my_array=(1 2 3 4 5)
1

Bash 支持关联数组,可以使用任意的字符串、或者整数作为下标来访问数组元素。

关联数组使用 declare 命令来声明,语法格式如下:

declare -A array_name
1

-A 选项就是用于声明一个关联数组。

关联数组的键是唯一的。

declare -A associative_array
associative_array["name"]="John"
associative_array["age"]=30
1
2
3

读取数组元素值的一般格式是:

${数组名[下标]}
1

例如:

valuen=${array_name[n]}
1

数组常用脚本:

array_name[0]=A
array_name[1]=B
array_name[2]=C
array_name[3]=D

# 获取数组中的所有元素
echo "数组的元素为: ${array_name[*]}"
echo "数组的元素为: ${array_name[@]}"

# 在数组前加一个感叹号 ! 可以获取数组的所有键,例如:
echo "数组的键为: ${!array_name[*]}"
echo "数组的键为: ${!array_name[@]}"

# 取得数组元素的个数
echo "数组元素的个数为: ${#array_name[*]}"
echo "数组元素的个数为: ${#array_name[@]}"

# 取得数组单个元素的长度
echo "第n个数组元素的长度为: ${#array_name[n]}"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 环境变量

环境变量: 这些是由操作系统或用户设置的特殊变量,用于配置 Shell 的行为和影响其执行环境。

例如,PATH 变量包含了操作系统搜索可执行文件的路径:

echo $PATH
1

# 特殊变量

特殊变量: 有一些特殊变量在 Shell 中具有特殊含义。例如:

特殊变量 说明
$0 脚本或函数的名称
$0 为脚本执行时传入的脚本路径名,一般在shell中执行文件都用绝对路径
$1...$2 $1 表示脚本或函数的第一个参数,$2 表示脚本或函数的第二个参数,当n>=10时,需要使用${n}来获取参数。。
$# 传递到脚本或函数的参数个数
$* 传递给脚本或函数的全部参数。以一个单字符串显示所有传递到脚本或函数的参数
$$ 脚本运行的当前进程ID号
$! 后台运行的最后一个进程的ID号
$@ 传递给脚本或函数的全部参数。
$- 显示Shell使用的当前选项,与set命令功能相同。
$? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。在Linux shell脚本中,可以使用特殊变量$?来获取上一个命令的退出状态码(exit status code)。这个状态码是上一个命令执行后退出的时候返回的数值。一般来说,如果命令执行成功,状态码为0;如果命令执行失败,状态码非0。退出状态码是一个0到255的整数。一般来说,正数的退出状态码是由程序自己定义的,通常用于表示不同的错误类型。而负数的退出状态码通常是由于shell内部错误或者是信号导致的。在实际应用中,你应该尽量避免返回负的退出状态码,除非这是由于shell内部的特定错误。如果你的程序需要返回错误代码,应当使用0以上的正数值。
退出码 解释
0 命令成功执行
1 通用错误代码
2 命令(或参数)使用不当
126 权限被拒绝(或)无法执行
127 未找到命令,或 PATH 错误
128+n 命令被信号从外部终止,或遇到致命错误
130 通过 Ctrl+C 或 SIGINT 终止(终止代码 2 或键盘中断)
143 通过 SIGTERM 终止(默认终止)
255/* 退出码超过了 0-255 的范围,因此重新计算(LCTT 译注:超过 255 后,用退出码对 256 取模)
Exit Code Number Meaning Example Comments
1 Catchall for general errors var1 = 1/0 Miscellaneous errors, such as "divide by zero" and other impermissible operations
126 Command invoked cannot execute /dev/null Permission problem or command is not an executable
127 "command not found" illegal_command Possible problem with $PATH or a typo
128 Invalid argument to exit exit 3.14159 exit takes only integer args in the range 0 - 255 (see first footnote)
128+n Fatal error signal "n" kill -9 $PPID of script $? returns 137 (128 + 9)
130 Script terminated by Control-C Ctl-C Control-C is fatal error signal 2, (130 = 128 + 2, see above)
255* Exit status out of range exit -1 exit takes only integer args in the range 0 - 255

以下是一个简单的示例脚本,它检查最后一个命令的退出状态码,并打印出相应的消息:

#!/bin/bash
 
# 执行一个命令
ls /some/directory
 
# 检查命令的退出状态码
if [ $? -eq 0 ]; then
    echo "命令执行成功"
else
    echo "命令执行失败,退出状态码为 $?"
fi
1
2
3
4
5
6
7
8
9
10
11

在实际使用中,通常会直接在命令后面进行检查,而不是先执行命令,再检查$?

#!/bin/bash
 
# 直接在命令后检查状态码
if ls /some/directory; then
    echo "命令执行成功"
else
    echo "命令执行失败,退出状态码为 $?"
fi
1
2
3
4
5
6
7
8

知识扩展——return和exit的使用:

  1. return是一个关键字; exit是一个函数。
  2. return是编程语言级别,它表示调用堆栈的返回;exit是系统调用级别,它表示了一个进程的结束。
  3. return是函数的退出(返回);exit是进程的退出,exit用于退出整个shell脚本进程。

# 局部变量

local一般用于局部变量声明,多在在函数内部使用。

(1)shell脚本中定义的变量是global的,其作用域从被定义的地方开始,到shell结束或被显示删除的地方为止。

(2)shell函数定义的变量默认是global的,其作用域从“函数被调用时执行变量定义的地方”开始,到shell结束或被显示删除处为止。函数定义的变量可以被显示定义成local的,其作用域局限于函数内。但请注意,函数的参数是local的。

(3)如果同名,Shell函数定义的local变量会屏蔽脚本定义的global变量。

# 高级变量

在bash shell中,$算符会触发3种扩展。基本形式如下表:

基本型 扩展种类 范例
${变量名称} 变量扩展 ${filename}
$(命令) 命令扩展 $(ls /)
$((算术式)) 算术扩展 $((9+9))

# 变量扩展

在Shell中,变量扩展修饰符用于指定如何扩展变量。这些修饰符可以与参数替换一起使用,以实现特定的扩展行为。

语法 说明
${待测变量-默认值} 条件判断。测试空值。若变量不存在,返回默认值。
${待测变量:-默认值} 条件判断。测不存在。若变量不存在或为空,返回默认值。
${待测变量:=默认值} 条件判断。设默认值。若变量不存在或为空,给变量设置一个默认值并返回。
${待测变量:?提示信息} 条件判断。检查问题。若变量不存在或为空,则打印提示信息并退出。
${待测变量:+真值} 条件判断。测试存在。若变量存在且其值非空,返回变量的值。
${#变量} 字符串长度。计算字符串长度。
${#数组[@]}
${#数组[*]}
数组长度。取得数组元素个数。
${变量:位置起点} 字符串切片。由指定位置开始,截取子字符串到字符串结束。
${变量:位置起点:长度} 字符串切片。由指定位置开始,截取指定长度的子字符串。
${变量#样式} 最短匹配删除。由最左边(前面)开始,对比变量值,删除最短相符的字符串。
filename=/path/to/file
echo ${filename#/*/} 打印 to/file
${变量##样式} 最长匹配删除。由最左边(前面)开始,对比变量值,删除最长相符的字符串。
filename=/path/to/file
echo ${filename##/*/} 打印 file
${变量%样式} 最短匹配删除。由最右边(后面)开始,对比变量值,删除最短相符的字符串。
filename=/path/to/file
echo ${filename%/*} 打印 /path/to
${变量%%样式} 最长匹配删除。由最右边(后面)开始,对比变量值,删除最长相符的字符串。
filename=/path/to/file
echo ${filename%%/*} 空白
${变量/样式/替换字符串} 取代字符串。只替换第一个对比符合的字符串。若变量中,有符合样式的字符串(取最长的),则使用替换字符串予以取代。
${变量//样式/替换字符串} 取代字符串。替换全部对比符合的字符串。若变量中,有符合样式的字符串(取最长的),则使用替换字符串全部予以替换。
${变量/样式/} 删除字符串。只删除一个。删除第一个符合样式的字符串。
${变量//样式/} 删除字符串。删除全部。删除所有符合样式的字符串。

# Shell 基本运算符

https://www.runoob.com/linux/linux-shell-basic-operators.html (opens new window)

# 算数运算符

原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用。

expr 是一款表达式计算工具,使用它能完成表达式的求值操作。

#!/bin/bash

val=`expr 2 + 2`
echo "两数之和为 : $val"
1
2
3
4

两点注意:

  • 表达式和运算符之间要有空格,例如 2+2 是不对的,必须写成 2 + 2,这与我们熟悉的大多数编程语言不一样。
  • 完整的表达式要被 `` 包含,注意这个字符不是常用的单引号,在 Esc 键下边。

# 自增和自减操作符

使用 $(( )) 进行算术运算

#!/bin/bash

# 初始化变量
num=5

# 自增
num=$((num + 1))

# 自减
num=$((num - 1))

echo $num
1
2
3
4
5
6
7
8
9
10
11
12

# 关系运算符(数值)

运算符 说明 举例
-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。

如果使用 ((...)) 作为判断语句,大于和小于可以直接使用 > 和 <,其他如此类推。

运算符 说明 举例
== 检测两个数是否相等,相等返回 true。 (( $a == $b )) 返回 false。
!= 检测两个数是否不相等,不相等返回 true。 (( $a != $b )) 返回 true。
> 检测左边的数是否大于右边的,如果是,则返回 true。 (( $a > $b )) 返回 false。
< 检测左边的数是否小于右边的,如果是,则返回 true。 (( $a < $b )) 返回 true。
>= 检测左边的数是否大于等于右边的,如果是,则返回 true。 (( $a >= $b )) 返回 false。
<= 检测左边的数是否小于等于右边的,如果是,则返回 true。 (( $a <= $b )) 返回 true。

# 字符串运算符

运算符 说明 举例
= 检测两个字符串是否相等,相等返回 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。

# 文件测试运算符

操作符 说明 举例
-b file 检测文件是否是块设备文件,如果是,则返回 true。 [ -b $file ] 返回 false。
-c file 检测文件是否是字符设备文件,如果是,则返回 true。 [ -c $file ] 返回 false。
-d file 检测文件是否是目录,如果是,则返回 true。 [ -d $file ] 返回 false。
-f file 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 [ -f $file ] 返回 true。
-g file 检测文件是否设置了 SGID 位,如果是,则返回 true。 [ -g $file ] 返回 false。
-k file 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。 [ -k $file ] 返回 false。
-p file 检测文件是否是有名管道,如果是,则返回 true。 [ -p $file ] 返回 false。
-u file 检测文件是否设置了 SUID 位,如果是,则返回 true。 [ -u $file ] 返回 false。
-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。
-S file 判断某文件是否 socket。
-L file 检测文件是否存在并且是一个符号链接。

# 布尔运算符

运算符 说明 举例
! 非运算,表达式为 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
|| 逻辑的 OR [[ $a -lt 100 || $b -gt 100 ]] 返回 true

# Shell test 命令

Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试。

在Shell脚本中,‌test命令和[](‌方括号表达式)‌都用于条件测试,‌但它们在使用上有一些区别。‌

  • test命令:‌这是一个独立的命令,‌用于测试条件表达式的值,‌并根据测试结果返回0(‌真)‌或非0值(‌假)‌。‌例如,‌test -f file.txt会检查file.txt是否存在并且是一个普通文件。‌如果条件为真,‌则返回0;‌否则返回非0值。‌
  • 方括号表达式:‌[ ]用于在脚本中进行条件测试,‌它实际上是一个命令替换的语法,‌可以与逻辑运算符一起使用。‌例如,‌[ -f file.txt ]test -f file.txt是等价的。‌方括号表达式也返回0或非0值,‌表示条件的真假。‌

在Shell脚本中,‌test命令和[]通常可以互换使用,‌但有一些细微的差别。‌例如,‌当使用逻辑运算符如&&||时,‌[]不支持旧式的-a-o运算符,‌而推荐使用&&||。‌此外,‌对于模式匹配和正则表达式匹配,‌通常使用[[ ]](‌双左方括号)‌,‌而基本的条件测试则可以使用[ ]test命令。‌

总的来说,‌虽然test命令和[]在功能上是相似的,‌但它们的使用场景和语法有所不同。‌在编写Shell脚本时,‌应根据具体的需求和上下文选择合适的方法进行条件测试。

# Shell 括号的用法

在Shell中,‌括号有小括号、‌中括号和大括号。‌每种括号都有其特定的用途和语法规则。‌

  • 小括号:‌

    • 单小括号:

      • 用于命令替换。‌例如,‌echo $(ls)会执行ls命令,‌并将输出作为echo命令的参数,等同于echo `ls`。‌

      • 用于命令组合和执行,‌可以创建一个子shell来顺序执行命令。‌例如,‌(pwd ; ls ; cd /etc ; pwd ; cd ; pwd ; ls) 会创建一个子shell并执行其中的命令。‌

      • 用于初始化数组。‌例如,‌array=(a b c d)会创建一个包含元素a、‌b、‌c、‌d的数组。‌

    • 双小括号:

      • 用于算术运算和比较。‌((表达式))常用于算术运算比较,双括号中的变量可以不使用$符号前缀。

        #!/bin/bash
        #求100以内的偶数
        num=2
        while ((num<100))    #数值与运算符可以没有空格,变量的使用时也可以不使用$num
        do
        	echo "$num"
        	((num=num*2))
        done
        
        1
        2
        3
        4
        5
        6
        7
        8
      • 用于循环语句。‌例如,‌for((i=0;i<5;i++))可以在双小括号内进行循环变量的递增。‌

  • 中括号:‌

    • 单中括号:

      • 用于条件表达式,算术比较,例如,‌[ $i -ge 1 ] 进行数值比较。
      • 用于条件表达式,文件测试,例如,[ -d $file ] 检测文件是否是目录。
      • 字符范围。用作正则表达式的一部分,描述一个匹配的字符范围。例如,cat 1.txt|grep "[0-9]"
      • 引用数组中每个元素。例如,${array_name[2]}
    • 双中括号:

      • 用于条件表达式,‌字符串比较,例如,[[ hello == hell? ]] 进行字符串模式匹配和正则表达式匹配。‌在进行字符串比较时,最好使用双中括号 [[ ]]. 因为单中括号可能会导致一些错误,因此最好避开它们。
  • 大括号:‌

    • 帮助解释器识别变量的边界。
    • 用于变量替换,‌如 ${var:-string} 当变量为空时,‌将string赋值给var。‌
    • 在序列生成中,‌如 {1..4} 用于生成数字序列。‌
    • 用于文件名扩展,‌如 {1,4}.txt 可以生成1.txt和4.txt两个文件,cp /path/to/file.txt{,.backup} 可以在/path/to目录下生成备份文件file.txt.backup。‌

在Shell脚本中,‌单中括号[ ]和双中括号[[ ]]各有其用途,‌选择使用哪种括号取决于特定的需求和场景。‌

  • 单中括号[ ]:‌
    • 主要用于测试条件表达式的返回值,‌它是一个测试命令,‌返回0表示真,‌非0表示假。‌
    • 在进行算术比较或算术运算时,‌单中括号[ ]更为合适。‌例如,‌进行数值比较或执行算术运算时,‌应使用单中括号。‌
    • 单中括号内的表达式需要特别注意空格的使用,‌例如[ $a -eq $b ],‌缺少空格可能会导致错误。‌
  • 双中括号[[ ]]:‌
    • 提供了更强大的字符串处理能力,‌支持模式匹配和正则表达式,‌对于字符串和文件名的处理更为灵活。‌
    • 双中括号在条件判断时可以不用引号包围变量,‌也可以支持逻辑操作符&&、‌||,‌并且不需要转义字符。‌
    • 双中括号在语法上更接近于其他编程语言,‌提供了一定的编程便利性。‌

总结来说,‌当需要进行算术比较或运算时,‌应使用单中括号[ ];‌而当需要进行字符串处理、‌模式匹配或提供更友好的语法时,‌应使用双中括号[[ ]]。‌在实际应用中,‌根据具体的需求选择合适的括号可以提高脚本的健壮性和可读性。

# Shell 引号的用法

# Shell 流程控制

# 条件结构

# if 条件结构

if condition
then
    command1 
    command2
    ...
    commandN 
fi

# 写成一行
if condition; then command1; command2… fi
1
2
3
4
5
6
7
8
9
10

# if else 条件结构

if condition
then
    command1 
    command2
    ...
    commandN
else
    command
fi
1
2
3
4
5
6
7
8
9

# if else-if else 条件结构

if condition1
then
    command1
elif condition2 
then 
    command2
else
    commandN
fi
1
2
3
4
5
6
7
8
9

# case ... esac 多选择条件结构

case ... esac 为多选择语句,与其他语言中的 switch ... case 语句类似,是一种多分支选择结构,每个 case 分支用右圆括号开始,用两个分号 ;; 表示 break,即执行结束,跳出整个 case ... esac 语句,esac(就是 case 反过来)作为结束标记。

可以用 case 语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令。

case ... esac 语法格式:

casein
模式1)
    command1
    command2
    ...
    commandN
    ;;
模式2)
    command1
    command2
    ...
    commandN
    ;;
esac
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 循环结构

# for 循环结构

for var in item1 item2 ... itemN
do
    command1
    command2
    ...
    commandN
done

# 写成一行
for var in item1 item2 ... itemN; do command1; command2… done;

# 无限循环语法格式
for (( ; ; ))
do
    command
done
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# while 循环结构

while condition
do
    command
done

# 无限循环语法格式一
while :
do
    command
done

# 无限循环语法格式二
while true
do
    command
done
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# until 循环结构

until 循环执行一系列命令直至条件为 true 时停止。

until 循环与 while 循环在处理方式上刚好相反。

until condition
do
    command
done
1
2
3
4

# break 和 continue

在循环过程中,有时候需要在未达到循环结束条件时强制跳出循环,Shell 使用两个命令来实现该功能:breakcontinue

break 命令允许跳出所有循环(终止执行后面的所有循环)。

continue 命令与 break 命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。

# Shell 函数

Shell 函数定义语法格式:

[ function ] funname [()]
{
    action;
    [return int;]
}
1
2
3
4
5

说明:

  1. 可以带 function fun() 定义,也可以直接 fun() 定义,不带任何参数。
  2. 所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至shell解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可。
  3. 在函数体内部,通过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数,获取第十个参数需要${10}。当n>=10时,需要使用${n}来获取参数。
  4. 函数返回值,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。 return 语句只能返回一个介于 0 到 255 之间的整数。
  5. 函数返回值在调用该函数后通过 $? 来获得。

# Shell 命令连接

在Shell中,命令连接可以使用多种符号,常见的有以下几种:

类型 符号 说明
分号 ; 无论前一个命令执行成功与否,都会执行下一条命令。
& 多个命令同时执行,不管命令是否执行成功。
逻辑与 && 只有前一个命令执行成功时,才会执行下一条命令。
逻辑或 || 只有前一个命令执行失败时,才会执行下一条命令。
管道 | 将前一个命令的输出作为后一个命令的输入。

# Shell 标准输入/输出和重定向

执行一个shell命令行,系统有三个默认的文件描述符供程序使用。标准输入文件(STDIN),通常对应终端的键盘;标准输出文件(STDOUT)和标准错误输出文件(STDERR),这两个文件都对应终端的屏幕。

使用符合 “>” 将屏幕的输出导入到文件,屏幕不再有显示,“>” 会覆盖之前的内容。

使用符合 “>>” 将屏幕的输出追加到文件,屏幕不再有显示,“>>” 不会覆盖之前的内容。

标准输入用“0”表示。

标准正确输出用 “1” 表示。“1”可以省略。>file等同于1>file>>file等同于1>>file

标准错误输出用 “2” 表示。

类型 操作符 用途
重定向输入 < 从指定的文件读取数据,而不是从键盘输入
重定向输出 > 将输出结果保存到指定的文件(覆盖原有内容)
重定向输出 >> 将输出结果追加到指定的文件尾部
标准错误输出 2> 将错误信息保存到指定的文件(覆盖原有内容)
标准错误输出 2>> 将错误信息追加到指定的文件尾部
混合输出 &> 将标准正确输出、标准错误输出的内容保存到同一文件(覆盖原有内容)
混合输出 &>> 将标准正确输出、标准错误输出的内容追加到指定的文件尾部
nohup command >/dev/null 2>&1 &
1

nohup 英文全称 no hang up(不挂起),用于在系统后台不挂断地运行命令,退出终端不会影响程序的运行。

nohup 命令,在默认情况下(非重定向时),会输出一个名叫 nohup.out 的文件到当前目录下,如果当前目录的 nohup.out 文件不可写,输出重定向到 $HOME/nohup.out 文件中。

nohup 命令语法格式:

nohup command [arg...] &
1

命令 /dev/null 2>&1 & 的主要作用是将程序的标准输出和错误输出都重定向到 /dev/null,从而屏蔽这些输出,使得运行该命令的用户不会看到任何输出信息。

这个命令组合可以分解为几个部分来理解:

  • /dev/null 代表空设备文件,是一个特殊的文件,所有写入它的内容都会被永久丢弃,因此被称为“黑洞”或“垃圾桶”。
  • >/dev/null等同于1>/dev/null,将标准错误输出保存到空设备文件。
  • 2>&1 是一个shell重定向技巧,它将标准错误输出(文件描述符2)重定向到标准输出(文件描述符1)当前指向的位置。在这个上下文中,由于标准输出也被重定向到了 /dev/null,因此标准错误输出也随之被丢弃。
  • & 在命令的末尾表示将命令放到后台执行,这样用户可以在不等待命令执行完成的情况下继续使用终端。‌

Linux tee命令用于读取标准输入的数据,并将其内容输出成文件。语法格式:

# 执行command脚本时将错误输出2以及标准输出1追加到nohup.log文件的后面
command 2>&1 | tee -a /path/to/nohup.log
1
2

# Shell 设置字体颜色

在shell中设置文本颜色,可以使用特殊的转义序列,这些序列会被终端解释为控制指令。常见的转义序列包括ANSI escape codes。

以下是一些基本的ANSI escape codes示例,用于改变文本颜色:

# 语法: \033[<code>m
# 其中<code>是颜色代码
 
# 文本颜色
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
 
# 文本背景颜色
RED_BG='\033[0;41m'
GREEN_BG='\033[0;42m'
YELLOW_BG='\033[0;43m'
BLUE_BG='\033[0;44m'
 
# 重置所有文本属性
RESET='\033[0m'
 
# 使用示例
echo -e "${RED}这是红色文本${RESET}"
echo -e "${GREEN}这是绿色文本${RESET}"
echo -e "${YELLOW}这是黄色文本${RESET}"
echo -e "${BLUE}这是蓝色文本${RESET}"
 
echo -e "${RED_BG}红色背景的文本${RESET}"
echo -e "${GREEN_BG}绿色背景的文本${RESET}"
echo -e "${YELLOW_BG}黄色背景的文本${RESET}"
echo -e "${BLUE_BG}蓝色背景的文本${RESET}"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

语法格式:

echo -e "\033[属性;属性m 需要改变的字符串 \033[0m"
1

-e 表示启用转义字符的解析。

常用ANSI控制码总结如下:

  • \033[0m:关闭所有属性

  • \033[1m:设置高亮度

  • \033[4m:下划线

  • \033[5m:闪烁

  • \033[7m:反显,撞色显示,显示为白底黑字,或者显示为黑底白字

  • \033[8m:消隐,字符颜色将会与背景颜色相同

  • \033[30m -- \33[37m:设置字符颜色

    • 30:黑色
    • 31:红色
    • 32:绿色
    • 33:黄色
    • 34:蓝色
    • 35:紫色
    • 36:浅蓝色
    • 37:灰色
  • \033[40m -- \33[47m:设置背景色

    • 40:黑色
    • 41:红色
    • 42:绿色
    • 43:黄色
    • 44:蓝色
    • 45:紫色
    • 46:浅蓝色
    • 47:灰色
  • \033[K:清除从光标到行尾的内容

# source 和 export

在Shell脚本中,可以使用source或者.命令来包含(或者说执行)外部的Shell脚本。这样可以很方便的封装一些公用的代码作为一个独立的文件。

语法格式:

source /path/to/filename
或
. /path/to/filename   # 注意点号(.)和文件名中间有一空格
1
2
3

source 命令用于在当前 shell 环境中加载和执行指定的脚本文件,并确保其中的变量、函数等在当前 shell 中可用,而不是启动一个新的子 shell 来执行脚本。

Linux export 命令用于设置或显示环境变量。在 shell 中执行程序时,shell 会提供一组环境变量。export 可新增,修改或删除环境变量,供后续执行的程序使用。export 的效力仅限于该次登陆操作。

语法格式:

export [-fnp] [变量名称]=[变量设置值]

# 导出变量1
export 变量名称=变量设置值
# 导出变量2
变量名称=变量设置值
export 变量名称
# 导出函数
export -f 函数名称
1
2
3
4
5
6
7
8
9

参数说明:

  • -f  代表[变量名称]中为函数名称。
  • -n  删除指定的变量。变量实际上并未删除,只是不会输出到后续指令的执行环境中。
  • -p  列出所有的shell赋予程序的环境变量。

示例代码:

# 定义变量
my_variable="This is my variable"

# 定义函数
my_function() {
  echo "This is my function"
}

# 导出变量
export my_var="This is my var"
export my_variable
 
# 导出函数
export -f my_function
1
2
3
4
5
6
7
8
9
10
11
12
13
14

export 命令用于将变量设置为环境变量,使其在当前 shell 中可用,并且在当前 shell 中启动的任何子进程中也可用。

# 相关资料

https://www.runoob.com/linux/linux-shell.html (opens new window)