一文详解shell编程(shell编程笔记)

Posted 非晚非晚

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一文详解shell编程(shell编程笔记)相关的知识,希望对你有一定的参考价值。

本文为快速入门教程,如果你所在的行业需要用到很深入的shell脚本知识,可参考书籍《Linux命令行与shell脚本编程大全》,豆瓣链接:https://book.douban.com/subject/26854226/

图片名称

1. 简介

常见的编程语言分为两类:一个是编译型语言,它们运行前全部一起要经过编译器的编译。另一个解释型语言,执行时,需要使用解释器一行一行地转换为代码,相关的语言分类如下:
在这里插入图片描述

真正能够控制计算机硬件(CPU、内存、显示器等)的只有操作系统内核(Kernel),图形界面和命令行只是架设在用户和内核之间的一座桥梁
在这里插入图片描述
在Linux下,这个命令行程序叫做Shell。由此可见,Shell 是将内核、程序和用户连接了起来。常见的 Shell 有 sh、bash、csh、tcsh、ash 等。

  • sh:sh 的全称是 Bourne shell,由 AT&T 公司的 Steve Bourne开发,为了纪念他,就用他的名字命名了。sh 是 UNIX 上的标准 shell,很多 UNIX 版本都配有 sh。sh 是第一个流行的 Shell。
  • bash:全称Bourne Again shell,bash shell 是 Linux 的默认 shell。
  • csh:sh 之后另一个广为流传的 shell 是由柏克莱大学的 Bill Joy 设计的,这个 shell 的语法有点类似C语言,所以才得名为 C shell ,简称为 csh。
  • tcsh:tcsh 是 csh 的增强版,加入了命令补全功能,提供了更加强大的语法支持。
  • ash:一个简单的轻量级的 Shell,占用资源少,适合运行于低内存环境。

可以通过命令 cat /etc/shells命令来查看自己电脑的兼容的shell版本,另外,还可以通过命令echo $SHELL来查看自己电脑默认的SHELL环境。

$ cat  /etc/shells
/bin/sh
/bin/bash
/bin/rbash
/bin/dash
$ echo $SHELL
/bin/bash

2. 运行shell脚本的几种方式

在你的工作空间新建一个文本test.sh(扩展名并不影响脚本执行,扩展名.sh不写,或者随便取也可以,只不过约定俗成用.sh),然后输入以下内容:

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

第一行为注释,表示使用bash的shell,不写也无所谓。在终端输入以下命令都可以运行该脚本:

./test.sh #方式1
source test.sh # 方式2
sh test.sh # 方式3
bash test.sh # 方式4
/bin/sh test.sh #方式5
/bin/bash test.sh # 方式6

3. 注释

  • 单行注释
#--------------------------------------------
# 这是一个注释
# 这是一个注释
# 这是一个注释
# 这是一个注释
#--------------------------------------------
  • 多行注释
:<<EOF
注释内容...
注释内容...
注释内容...
EOF

其中EOF可以用其他符号代替

4. 变量

变量的命名规则与C/C++等类似,它的规则如下:

  • 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头
  • 不能使用bash里的关键字(可用终端输入help命令查看保留关键字)。

4.1 系统变量

系统变量也称为环境变量,所有的程序都可以访问环境变量,一般用大写字母表示,例如$HOME、$PWD、$SHELL、$USER等。例如:

$ echo $HOME
/home/myComputer

4.2 局部变量

局部变量也称为自定义变量,局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。

1)定义和使用变量

$ my_name="QLee" #定义变量,=号左右两侧不能有空格
$ echo $my_name #变量的使用
$ echo ${my_name} #变量的使用
QLee
QLee

变量外的括号,是可选的,加花括号是为了帮助解释器识别变量的边界。

2)只读变量

#!/bin/bash
my_name="QLee"
readonly my_name #指定只读变量,不用$
my_name="lisa"

输出:

test.sh: 行 4: my_name: 只读变量

3)删除变量

#!/bin/bash
my_name="QLee"
unset my_name #删除变量,不用$
echo $my_name

以上echo不会有输出,因为my_name被删除。另外,注意不能删除只读变量

4)字符串

shell只有数字和字符串的数据类型,所以这里介绍一下字符串类型。

  • 单引号形式
str='this is a string'

单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的。单引号只能成对的出现,即使使用转义符,也不能单独出现。

  • 双引号形式
my_name='QLee'
str="Hello,my name is \\"$my_name\\"! " #拼接
echo $str

输出:

Hello,my name is "QLee"!
  • 字符串拼接
my_name="QLee"
# 使用双引号拼接
greeting="hello, "$my_name" !"
greeting_1="hello, ${my_name} !"
echo $greeting  $greeting_1
# 使用单引号拼接
greeting_2='hello, '$my_name' !'
greeting_3='hello, ${my_name} !' # 原样输出!!!
echo $greeting_2  $greeting_3

输出:

hello, QLee ! hello, QLee !
hello, QLee ! hello, ${my_name} !
  • 获取字符串长度
string="abcd"
echo ${#string} #输出 4
  • 提取字符串

从位置1开始,提取4个字符

string="my name is QLee"
echo ${string:1:4} # 从第1个字符开始截取4个字符,输出y na
  • 查找字符串

以下表示查找n或i的第一个位置

string="my name is QLee"
echo `expr index "$string" ni`  # 输出4

5. shell数组

bash中只有一维数组,它的下标从0开始,数组的大小没有限定,定义数组时用空格分开,例如:

array_name=(value0 value1 value2 value3)# 数组的定义

或者:

array_name=(
value0
value1
value2
value3
)

或者:

array_name[0]=value0
array_name[1]=value1
array_name[n]=valuen

5.1 读取数组

my_array=(A B "C" D)

echo "第一个元素为: ${my_array[0]}"
echo "第二个元素为: ${my_array[1]}"
echo "第三个元素为: ${my_array[2]}"
echo "第四个元素为: ${my_array[3]}"

输出:

第一个元素为: A
第二个元素为: B
第三个元素为: C
第四个元素为: D

5.2 获取数组长度

# 取得数组元素的个数
length=${#array_name[@]} # @或*可以获取数组中的所有元素。
# 或者
length=${#array_name[*]}
# 取得数组单个元素的长度
lengthn=${#array_name[n]}

6. 基本运算符

原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用。expr 是一款表达式计算工具,使用它能完成表达式的求值操作。

关于运算符需要注意以下两点:

  • 表达式和运算符之间要有空格,例如 2+2 是不对的,必须写成 2 + 2;
  • 完整的表达式要被 包含,注意这个字符不是常用的单引号,在 Esc 键下边。

6.1 算数运算

下表列出了常用的算术运算符,假定变量 a 为 10,变量 b 为 20:

运算符说明举例
+加法`expr $a + $b` 结果为 30。
-减法`expr $a - $b` 结果为 -10。
*乘法`expr $a \\* $b` 结果为 200。
/除法`expr $b / $a` 结果为 2。
%取余`expr $b % $a` 结果为 0。
=赋值a=$b 将把变量 b 的值赋给 a。
==相等。用于比较两个数字,相同则返回 true。[ $a == $b ] 返回 false。
!=不相等。用于比较两个数字,不相同则返回 true。[ $a != $b ] 返回 true。

注意:

  • *的前面需要用"\\",其他符号不用
  • 条件表达式要放在方括号之间,并且要有空格,例如: [$a==$b] 是错误的,必须写成 [ $a == $b ]。

6.2 关系运算符

关系运算符只支持数字,不支持字符串,除非字符串的值是数字。下表列出了常用的关系运算符,假定变量 a 为 10,变量 b 为 20:

运算符单词说明举例
-eqequal相等[ $a -eq $b ] 返回 false。
-nenot equal不相等[ $a -ne $b ] 返回 true。
-gtgreat than大于[ $a -gt $b ] 返回 false。
-ltless than小于[ $a -lt $b ] 返回 true。
-gegreat than or equal大于或等于[ $a -ge $b ] 返回 false。
-leless than or equal小于等于[ $a -le $b ] 返回 true。

6.3 布尔运算符

下表列出了常用的布尔运算符,假定变量 a 为 10,变量 b 为 20:

运算符说明举例
!非运算[ ! false ] 返回 true。
-o或运算[ $a -lt 20 -o $b -gt 100 ] 返回 true。
-a与运算[ $a -lt 20 -a $b -gt 100 ] 返回 false。

6.4 逻辑运算符

以下介绍 Shell 的逻辑运算符,假定变量 a 为 10,变量 b 为 20:

运算符说明举例
&&逻辑"与"[[ $a -lt 100 && $b -gt 100 ]] 返回 false
||逻辑"或"[[ $a -lt 100 || $b -gt 100 ]] 返回 true

注意:必须两个"[ ]"

6.5 字符串运算符

下表列出了常用的字符串运算符,假定变量 a 为 “abc”,变量 b 为 “efg”:

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

6.6 文件测试运算符

文件测试运算符用于检测 Unix 文件的各种属性。属性检测描述如下:

操作符说明举例
-b file检测文件是否是块设备文件,如果是,则返回 true。[ -b $file ]
-c file检测文件是否是字符设备文件,如果是,则返回 true。[ -c $file ]
-d file检测文件是否是目录,如果是,则返回 true。[ -d $file ]
-f file检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。[ -f $file ]
-g file检测文件是否设置了 SGID 位,如果是,则返回 true。[ -g $file ]
-k file检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。[ -k $file ]
-p file检测文件是否是有名管道,如果是,则返回 true。[ -p $file ]
-u file检测文件是否设置了 SUID 位,如果是,则返回 true。[ -u $file ]
-r file检测文件是否可读,如果是,则返回 true。[ -r $file ]
-w file检测文件是否可写,如果是,则返回 true。[ -w $file ]
-x file检测文件是否可执行,如果是,则返回 true。[ -x $file ]
-s file检测文件是否为空(文件大小是否大于0),不为空返回 true。[ -s $file ]
-e file检测文件(包括目录)是否存在,如果是,则返回 true。[ -e $file ]

7. echo命令

echo为显示命令,它有下列几种用法。

  • 1)显示普通字符串
echo "It is a test" # It is a test
  • 2)显示转移字符
echo "\\"It is a test\\"" # "It is a test"
  • 3)显示变量
read name # 从标准输入读取一行给name
echo "$name It is a test" 
  • 4)显示换行
echo -e "OK! \\n" # -e 开启转义
echo "It is a test"

输出:

OK!

It is a test
  • 5)不显示换行
echo -e "OK! \\c" # -e 开启转义 \\c 不换行
echo "It is a test"

输出:

OK! It is a test
  • 6)显示结果定向至文件
echo "It is a test" > myfile
  • 原样输出字符串,不进行转义或取变量(用单引号)
echo '$name\\"' #输出:$name\\"
  • 7)显示命令执行结果
echo `date`# `为反引号,显示现在的时间

8. printf命令

它的使用形式:

printf  format-string  [arguments...]

说明:

  • format-string: 为格式控制字符串
  • arguments: 为参数列表。

默认 printf 不会像 echo 自动添加换行符,我们可以手动添加 \\n。

$ echo "Hello, Shell"
Hello, Shell
$ printf "Hello, Shell\\n"
Hello, Shell

格式输出中,%s 输出一个字符串,%d 整型输出,%c 输出一个字符,%f 输出实数,以小数形式输出。举例说明如下:

printf "%-10s %-8s %-4s\\n" name 性别 体重kg  
printf "%-10s %-8s %-4.2f\\n" QLee 男 65.3
printf "%-10s %-8s %-4.2f\\n" lisa 男 75.6
printf "%-10s %-8s %-4.2f\\n" yoyo 女 45.39

输出:

在这里插入图片描述

printf 的转义序列

序列说明
\\a警告字符,通常为ASCII的BEL字符
\\b后退
\\c不显示输出结果中任何结尾的换行字符(只在%b格式指示符控制下的参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略
\\f换页(formfeed)
\\n换行
\\r回车(Carriage return)
\\t水平制表符
\\v垂直制表符
\\\\一个字面上的反斜杠字符
\\ddd表示1到3位数八进制值的字符。仅在格式字符串中有效
\\0ddd表示1到3位的八进制值字符

9. 流程控制

9.1 if语句

  • 情况1:if
if condition
then
    command1 
    command2
    ...
    commandN 
fi
  • 情况2:if else
if condition
then
    command1 
    command2
    ...
    commandN
else
    command
fi
  • 情况3:if else-if else
if condition1
then
    command1
elif condition2 
then 
    command2
else
    commandN
fi

9.2 for循环

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

举例:

for loop in 1 2 3 4 5
do
    echo "The value is: $loop"
done

输出:

The value is: 1
The value is: 2
The value is: 3
The value is: 4
The value is: 5
for str in This is a string
do
    echo $str
done

输出:

This
is
a
string

9.3 while语句

语法格式:只要condition为true,则继续运行

while condition
do
    command
done

无限循环:

while :
do
    command
done

或者:

while true
do
    command
done

或者

for (( ; ; ))

9.4 until循环

语法格式:直至条件为 true 时停止

until condition
do
    command
done

9.5 case … esac多分支语句

每个 case 分支用右圆括号开始,用两个分号 ;; 表示执行结束。

基本语法:

casein
模式1)
    command1
    command2
    ...
    commandN
    ;;
模式2)
    command1
    command2
    ...
    commandN
    ;;
esac

9.6 break和continue

和c语言的break和continue语句一样使用。

  • break:跳出整个循环
  • continue:跳出这一次的循环,继续执行一次循环。

10. 函数

shell中函数的定义格式如下:

[ function ] funname [()]
{
    action;

    [return int;]
}

说明:

  • 可以带function fun() 定义,也可以直接fun() 定义,不带任何参数。
  • 参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。

10.1 无参数、无返回值的函数

demoFun(){
    echo "这是我的第一个 shell 函数!"
}
echo "-----函数开始执行-----"
demoFun
echo "-----函数执行完毕-----"

输出:

-----函数开始执行-----
这是我的第一个 shell 函数!
-----函数执行完毕-----

10.2 无参数、有返回值的函数

funWithReturn(){
    echo "这个函数会对输入的两个数字进行相加运算..."
    echo "输入第一个数字: "
    read aNum
    echo "输入第二个数字: "
    read anotherNum
    echo "两个数字分别为 $aNum$anotherNum !"
    return $(($aNum+$anotherNum))
}
funWithReturn
echo "输入的两个数字之和为 $? !"

输出:

这个函数会对输入的两个数字进行相加运算...
输入第一个数字: 
1
输入第二个数字: 
2
两个数字分别为 1 和 2 !
输入的两个数字之和为 3 !

说明:

  • 函数返回值在调用该函数后通过 $? 来获得。
  • 所有函数在使用前必须定义

10.3 有参数的函数

funWithParam(){
    echo "第一个参数为 $1 !"
    echo "第二个参数为 $2 !"
    echo "第十个参数为 $10 !"
    echo "第十个参数为 ${10} !"
    echo "第十一个参数为 ${11} !"
    echo "参数总数有 $# 个!"
    echo "作为一个字符串输出所有参数 $* !"
}
funWithParam 1 2 3 4 5 6 7 8 9 34 73

输出:

第一个参数为 1 !
第二个参数为 2 !
第十个参数为 10 !
第十个参数为 34 !
第十一个参数为 73 !
参数总数有 11 个!
作为一个字符串输出所有参数 1 2 3 4 5 6 7 8 9 34 73 !

注意:当n>=10时,需要使用${n}来获取参数。

还有几个特殊字符用来处理参数:

参数处理说明
$#传递到脚本或函数的参数个数
$*以一个单字符串显示所有向脚本传递的参数
$$脚本运行的当前进程ID号
$!后台运行的最后一个进程的ID号
$@与$*相同,但是使用时加引号,并在引号中返回每个参数。
$-显示Shell使用的当前选项,与set命令功能相同。
$?显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。

11. 输入/输出重定向

默认情况下,标准输入和输出的地方都是终端,但是我们可以通过命令对输入/输出进行重定向。

重定向命令列表如下:

命令说明
command > file将输出重定向到 file。
command < file将输入重定向到 file。
command >> file将输出以追加的方式重定向到 file。
n > file将文件描述符为 n 的文件重定向到 file。
n >> file将文件描述符为 n 的文件以追加的方式重定向到 file。
n >& m将输出文件 m 和 n 合并。
n <& m将输入文件 m 和 n 合并。
<< tag将开始标记 tag 和结束标记 tag 之间的内容作为输入。

需要注意的是文件描述符 0 通常是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。

  • Here Document

Here Document 是 Shell 中的一种特殊的重定向方式,用来将输入重定向到一个交互式 Shell 脚本或程序。

例如:

cat << EOF
hello, everybody
my name is QLee
EOF

打印如下:

hello, everybody
my name is QLee
  • /dev/null 文件

/dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到"禁止输出"的效果。

如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null:

$ command > /dev/null

如果希望屏蔽 stdout 和 stderr,可以这样写:

$ command > /dev/null 2>&1 # 0 是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。

12. 文件包含

与C语言中的#include语句类似,shell中也有自己的包含命令,它的语法格式如下:

. filename   # 注意点号(.)和文件名中间有一空格source filename

因为是解释语言,所以文件包含的意义,其实就是写一条命令运行被包含的文件。

13. 其他命令

(1)data

显示当前时间,格式如下:

date [选项] [+时间格式] #使用(+)号开始的参数用来指定时间格式

例如:

nowTime=`date +%Y-%M-%d-%H:%M:%S`
echo $nowTime

date的格式有以下几种:

%H 小时(以00-23来表示)。
%I 小时(以01-12来表示)。
%K 小时(以0-23来表示)。
%l 小时(以0-12来表示)。
%M 分钟(以00-59来表示)。
%P AM或PM。
%r 时间(含时分秒,小时以12小时AM/PM来表示)。
%s 总秒数。起算时间为1970-01-01 00:00:00 UTC。
%S 秒(以本地的惯用法来表示)。
%T 时间(含时分秒,小时以24小时制来表示)。
%X 时间(以本地的惯用法来表示)。
%Z 市区。
%a 星期的缩写。
%A 星期的完整名称。
%b 月份英文名的缩写。
%B 月份的完整英文名称。
%c 日期与时间。只输入date指令也会显示同样的结果。
%d 日期(以01-31来表示)。
%D 日期(含年月日)。
%j 该年中的第几天。
%m 月份(以01-12来表示)。
%U 该年中的周数。
%w 该周的天数,0代表周日,1代表周一,异词类推。
%x 日期(以本地的惯用法来表示)。
%y 年份(以00-99来表示)。
%Y 年份(以四位数来表示)。
%n 在显示时,插入新的一行。
%t 在显示时,插入tab。
MM 月份(必要)
DD 日期(必要)
hh 小时(必要)
mm 分钟(必要)
ss 秒(选择性)

(2)后台运行脚本

test.sh &

对于基础应用,以上知识已经足够了。由于本人所在行业原因,暂时还不需要那么高深的shell知识,如果后续有深入研究,会继续补充更新。

给大家推荐一个很赞的Linux下的命令行网址:https://www.linuxcool.com/

以上是关于一文详解shell编程(shell编程笔记)的主要内容,如果未能解决你的问题,请参考以下文章

一文彻底弄懂Linux-Shell编程

一文彻底弄懂Linux-Shell编程

一文彻底弄懂Linux-Shell编程

Linux shell脚本编程详解及应用实例

Shell编程笔记

shell脚本编程学习笔记-函数