一文搞定Linux shell脚本编程( 史上最全汇总 )

本文详细介绍了Linux Shell脚本编程,包括Shell的基本概念、编写第一个脚本、编程语法如变量、字符串、数组、流程控制、内建命令、函数等,并提供了丰富的示例。此外,还讲解了如何运行和调试Shell脚本,以及如何在实际应用中使用Shell编程技巧。
摘要由CSDN通过智能技术生成


当命令或程序语句不在命令行下执行,而是通过一个程序文件来执行时,该程序文件就被称为Shell脚本。 在Shell脚本里内置了很多命令、语句及循环控制,然后将这些命令一次性执行完毕,这种通过文件执行脚本的方式称为非交互的方式。 Shell脚本语言很适合用于处理纯文本型的数据,而Linux系统中几乎所有的配置文件、日志文件,以及绝大对数的启动文件都是纯文本类型的文件。

业界所说的 Shell 通常都是指 Shell 脚本, “Shell编程” 通常都是指 Shell 脚本编程,不是指开发 shell 自身。

Shell是一种脚本语言

什么是脚本语言?

任何代码最终都要被“翻译”成二进制的形式才能在计算机中执行。

有的编程语言,如 C/C++、Pascal、Go语言、汇编等,必须在程序运行之前将所有代码都翻译成二进制形式,也就是生成可执行文件,用户拿到的是最终生成的可执行文件,看不到源码。

这个过程叫做编译(Compile),这样的编程语言叫做编译型语言,完成编译过程的软件叫做编译器(Compiler)。

而有的编程语言,如 Shell、JavaScript、Python、PHP等,需要一边执行一边翻译,不会生成任何可执行文件,用户必须拿到源码才能运行程序。程序运行后会即时翻译,翻译完一部分执行一部分,不用等到所有代码都翻译完。

这个过程叫做解释,这样的编程语言叫做解释型语言或者脚本语言(Script),完成解释过程的软件叫做解释器。

编译型语言的优点是执行速度快、对硬件要求低、保密性好,适合开发操作系统、大型应用程序、数据库等。

脚本语言的优点是使用灵活、部署容易、跨平台性好,非常适合 Web 开发以及小工具的制作。

Shell 就是一种脚本语言,我们编写完源码后不用编译,直接运行源码即可。

第一个Shell脚本

打开文本编辑器(可以使用 vi/vim 命令来创建文件),新建一个文件 test.sh,扩展名为 sh(sh代表shell),扩展名并不影响脚本执行,见名知意就好,如果你用 php 写 shell 脚本,扩展名就用 php 好了。

输入一些代码,第一行一般是这样:

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

“#!” 是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即 使用哪一种Shell。echo命令用于向窗口输出文本。

运行 Shell 脚本的方法:


1、作为可执行程序

将上面的代码保存为 test.sh,并 cd 到相应目录:

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

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

使用ls -l选项可以查看到新建的test.sh在chmod前后的权限差别:
在这里插入图片描述


2、作为解释器参数

这种运行方式是,直接运行解释器,其参数就是 shell 脚本的文件名,如:

/bin/sh test.sh
/bin/php test.php
/bin/bash test.sh
bash test.sh

这种方式运行的脚本,不需要在第一行指定解释器信息,写了也没用。
该方法不需要使脚本具备可执行权限。
/bin/bash test.sh 和 bash test.sh本质是一样的,都是运行 Bash 解释器,第一种写法给出了绝对路径,会直接运行 Bash 解释器;第二种写法通过 bash 命令找到 Bash 解释器所在的目录,然后再运行,只不过多了一个查找的过程而已。


3、使用source 命令
source 是 Shell 内置命令的一种,它会读取脚本文件中的代码,并依次执行所有语句。你也可以理解为,source 命令会强制执行脚本文件中的全部命令,而忽略脚本文件的权限。
source 命令的用法为:

source filename

也可以简写为:

. filename

两种写法的效果相同。对于第二种写法,注意点号.和文件名中间有一个空格。
例如,使用 source 运行 test.sh:

[mozhiyan@localhost ~]$ cd demo              #切换到test.sh所在的目录
[mozhiyan@localhost demo]$ source ./test.sh  #使用source
Hello World !
[mozhiyan@localhost demo]$ source test.sh    #使用source
Hello World !
[mozhiyan@localhost demo]$ . ./test.sh       #使用点号
Hello World !
[mozhiyan@localhost demo]$ . test.sh         #使用点号
Hello World !

注意:使用 source 命令不用给脚本增加执行权限,并且写不写./都行。


对于以上3种方式还需注意的是,方法1和方法2是在新的进程中运行脚本,方法3是在当前的进程中运行脚本。

Shell编程语法

Shell变量

在 Bash shell 中,每一个变量的值都是字符串,无论你给变量赋值时有没有使用引号,值都会以字符串的形式存储。
这意味着,Bash shell 在默认情况下不会区分变量类型,即使你将整数和小数赋值给变量,它们也会被视为字符串。

定义Shell变量
Shell 支持以下三种定义变量的方式:

variable=value
variable=‘value’
variable=“value”

variable 是变量名,value 是赋给变量的值。
如果 value 不包含任何空白符(例如空格、Tab 缩进等),那么可以不使用引号;如果 value 包含了空白符,那么就必须使用引号包围起来。
以单引号’ '包围变量的值时,单引号里面是什么就输出什么,即使内容中有变量和命令(命令需要反引起来)也会把它们原样输出。
以双引号" "包围变量的值时,输出时会先解析里面的变量和命令,而不是把双引号中的变量名和命令原样输出。双引号是最常用的
注意,赋值号=的两边不能有空格

Shell 变量的命名规范
变量名由数字、字母、下划线组成;(不能包含空格,标点符号)
必须以字母或者下划线开头;(尤其注意不能以数字开头)
不能使用 Shell 里的关键字(通过 help 命令可以查看保留关键字)。

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

your_name="qinjx"
echo $your_name
echo ${your_name}

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

只读变量
使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。(注意,readonly 是一个命令)

variable=value
readonly variable

删除变量
使用 unset 命令可以删除变量。语法:

unset variable_name

变量被删除后不能再次使用。unset 命令不能删除只读变量。

变量作用域
运行shell时,会同时存在三种变量:

  1. 局部变量 局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。
  2. 环境变量 所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也可以定义环境变量。
  3. shell变量 shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了shell的正常运行
    前面我们讲的,在脚本中定义的,或是在命令行中定义的都是局部变量。

字符串

前面讲变量的时候已经讲过了,在 Bash shell 中,每一个变量的值都是字符串。

变量定义的时候有三种方式,三者之间的区别:

  1. 由单引号’ '包围的字符串:
    任何字符都会原样输出,在其中使用变量是无效的。
    字符串中不能出现单引号,即使对单引号进行转义也不行。

  2. 由双引号" "包围的字符串:
    如果其中包含了某个变量,那么该变量会被解析(得到该变量的值),而不是原样输出。
    字符串中可以出现双引号,只要它被转义了就行。

  3. 不被引号包围的字符串
    不被引号包围的字符串中出现变量时也会被解析,这一点和双引号" "包围的字符串一样。
    字符串中不能出现空格,否则空格后边的字符串会作为其他变量或者命令解析。

示例:

#!/bin/bash
n=74
str1=abc$n 
str2="a \"bc\" $n"
str3='abc $n'
echo $str1
echo $str2
echo $str3

结果:

abc74
a "bc" 74
abc $n

str1 中包含了$n,它被解析为变量 n 的引用。$n后边有空格,紧随空格的是 str2;Shell 将 str2 解释为一个新的变量名,而不是作为字符串 str1 的一部分。

str2 中包含了引号,但是被转义了(由反斜杠\开头的表示转义字符)。str2 中也包含了$n,它也被解析为变量 n 的引用。

str3 中也包含了$n,但是仅仅是作为普通字符,并没有解析为变量 n 的引用。

获取字符串长度
在 Shell 中获取字符串长度很简单,具体方法如下:

${#string_name}

string_name 表示字符串名字。
示例:

string="abcd"
echo ${#string} #输出 4

字符串拼接
在 Shell 中,将两个字符串并排放在一起就能实现拼接。

#!/bin/bash
str11="abc"
str22="def"
str1=$str11$str22  #中间不能有空格
str2="$str11 $str22"  #如果被双引号包围,那么中间可以有空格
str3=$str11": "$str22  #中间可以出现别的字符串
str4="$str11: $str22"  #这样写也可以 同上
str5="${str11}hhh: ${str22}hhh"  #这个时候需要给变量名加上大括号
echo $str1
echo $str2
echo $str3
echo $str4
echo $str5

运行结果:

acbdef
abc def
abc: def
abc: def
abchhh: defhhh

截取字符串
大致分为从指定位置开始截取从指定字符(子字符串)开始截取

格式说明
${string: start :length}从 string 字符串的左边第 start 个字符开始,向右截取 length 个字符。
${string: start}从 string 字符串的左边第 start 个字符开始截取,直到最后。
${string: 0-start :length}从 string 字符串的右边第 start 个字符开始,向右截取 length 个字符。
${string: 0-start}从 string 字符串的右边第 start 个字符开始截取,直到最后。
${string#*chars}从 string 字符串第一次出现 *chars 的位置开始,截取 *chars 右边的所有字符。
${string##*chars}从 string 字符串最后一次出现 *chars 的位置开始,截取 *chars 右边的所有字符。
${string%chars*}从 string 字符串第一次出现 chars* 的位置开始,截取 chars* 左边的所有字符。
${string%%chars*}从 string 字符串最后一次出现 chars* 的位置开始,截取 chars* 左边的所有字符。

示例:

#!/bin/bash

str="abc/def//g-g-"

#从字符串左边第2个开始,向右截取3个字符(从左边开始,从 0 开始计数)
echo ${str: 2: 3} 
#length省略的话表示直到字符串的末尾
echo ${str: 2} 
#从 string 字符串的右边第 5 个字符开始,向右截取 3个字符
echo ${str: 0-5: 3}
#省略 length,直接截取到字符串末尾
echo ${str: 0-5}
#从 string 字符串第一次出现 *bc 的位置开始,截取 *bc 右边的所有字符
echo ${str#*bc}
#不用通配符*
echo ${str#abc}
#从 string 字符串最后一次出现 */的位置开始,截取 */右边的所有字符
echo ${str##*/}
#从 string 字符串第一次出现 /*的位置开始,截取 /*左边的所有字符,注意这里的第一次出现是从右往左开始看
echo ${str%/*}
#从 string 字符串最后一次出现 /*的位置开始,截取 /*左边的所有字符,注意这里的最后一次出现是从右往左开始看
echo ${str%%/*}

运行结果:
在这里插入图片描述

Shell数组

bash支持一维数组(不支持多维数组),并且没有限定数组的大小。
类似于 C 语言,数组元素的下标由 0 开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于 0。

定义数组
在 Shell 中,用括号( )来表示数组,数组元素之间用空格来分隔。赋值号=两边不能有空格,必须紧挨着数组名和数组元素。

array_name=(ele1  ele2  ele3 ... elen)

注意点:
1、Shell 是弱类型的,它并不要求所有数组元素的类型必须相同,可以同时有数字和字符串
2、Shell 数组的长度不是固定的,定义之后还可以增加元素。
3、无需逐个元素地给数组赋值,可以只给特定元素赋值。
如:ages=([3]=24 [5]=19 [10]=12)

读取数组
获取数组元素的值,一般使用下面的格式:

${array_name[index]}

使用@或*可以获取数组中的所有元素,例如:
${nums[*]}
${nums[@]}
两者都可以得到 nums 数组的所有元素。

获取数组长度
所谓数组长度,就是数组元素的个数。(注意,不是指内存字节大小)
利用@或*,可以将数组扩展成列表,然后使用#来获取数组元素的个数,格式如下:
${#array_name[@]}
${#array_name[*]}

删除数组元素
使用unset
如:
unset nums[1] #删除nums数组的1号元素
unset nums #删除整个nums数组

位置参数

我们可以在执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为:$n。n 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推……
给脚本传递参数
示例:

#!/bin/bash
echo "Shell 传递参数实例!";
echo "执行的文件名:$0";
echo "第一个参数为:$1";

执行脚本,并传参:

$ ./test.sh 1 2

输出:
Shell 传递参数实例!
执行的文件名:./test.sh
第一个参数为:1
第二个参数为:2

特殊变量

变量含义
$0当前脚本的文件名。
$n(n≥1)传递给脚本或函数的参数。n 是一个数字,表示第几个参数。例如,第一个参数是 $1,第二个参数是 $2。
$#传递给脚本或函数的参数个数。
$*传递给脚本或函数的所有参数。
$@传递给脚本或函数的所有参数。与$*相同,但是使用时加引号,并在引号中返回每个参数。
$?上个命令的退出状态,
$$当前 Shell 进程 ID。

内建命令

echo

echo 是一个 Shell 内建命令,用来在终端输出字符串,并在最后默认加上换行符
输出字符串
这是最简单的应用,用它可以实现我们梦的开始。

echo “Hello World!

也可以不加双引号

echo Hello World!

输出字符串不换行
echo默认是换行的,如果需要不换行可以参考下面的做法:
1、使用-n选项
示例:

#!/bin/bash
echo -n "abc"
echo "def"

输出:

abcdef

2、使用\c 转义字符:
示例:

#!/bin/bash
echo -e "OK! \c" # -e 开启转义 \c 不换行
echo "It is a test"

输出:

OK! It is a test

显示转义字符
比如需要显示双引号

echo "\"It is a test\""

输出:

“It is a test”

开启转义字符
默认情况下,echo 不会解析以反斜杠\开头的转义字符。使用-e选项开启转义字符解析。
如:

#!/bin/bash
echo "hello \nworld"
echo -e "hello \nworld"

输出:

hello \nworld
hello
world

显示结果定向至文件

echo "It is a test" > file # file如果已存在则输出到该文件,flie如果不存在则会自动新建在当前工作目录
echo `date` > file # date是命令,注意是反引号,命令的结果输出到file

输出变量的值

value=abc
echo "$value"
read

read 是 Shell 内置命令,用来从标准输入中读取数据并赋值给变量。如果没有进行重定向,默认就是从键盘读取用户输入的数据;如果进行了重定向,那么可以从文件中读取数据。

read 命令的用法为:

read [-options] [variables]
options表示选项,如下表所示;variables表示用来存储数据的变量,可以有一个,也可以有多个。
options和variables都是可选的,如果没有提供变量名,那么读取的数据将存放到环境变量 REPLY 中。

选项说明
-a array把读取的数据赋值给数组 array,从下标 0 开始。
-d delimiter用字符串 delimiter 指定读取结束的位置,而不是一个换行符(读取到的数据不包括 delimiter)。
-e在获取用户输入的时候,对功能键进行编码转换,不会直接显式功能键对应的字符。
-n num读取 num 个字符,而不是整行字符。
-p prompt显示提示信息,提示内容为 prompt。
-r原样读取(Raw mode),不把反斜杠字符解释为转义字符。
-s静默模式(Silent mode),不会在屏幕上显示输入的字符。当输入密码和其它确认信息的时候,这是很有必要的。
-t seconds设置超时时间,单位为秒。如果用户没有在指定时间内输入完成,那么 read 将会返回一个非 0 的退出状态,表示读取失败。
-u fd使用文件描述符 fd 作为输入源,而不是标准输入,类似于重定向。

示例:

read -n 1  char #读取单个字符赋值给char

read value1 value2 value3 #连续读取多个字符给多个变量赋值

read -p "please input:" #读取之前打印提示
printf

printf 命令模仿 C 程序库(library)里的 printf() 程序。
printf 命令的语法:

printf format-string [arguments…]

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

与C语言中的printf应用大致相同,不一样的是C语言中有函数括号,有参数逗号分隔,有分号结尾。
示例:

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

echo "Hello, Shell"的效果等同于printf “Hello, Shell\n”,只是printf要加入’\n’实现换行,而echo会自动在结尾加换行符。

exit

exit 是一个 Shell 内置命令,用来退出当前 Shell 进程,并返回一个退出状态;
exit 命令可以接受一个整数值作为参数,代表退出状态。如果不指定,默认状态值是 0。
exit 退出状态只能是一个介于 0~255 之间的整数,其中只有 0 表示成功,其它值都表示失败。

注意,exit 表示退出当前 Shell 进程,我们必须在新进程中运行 test.sh,否则当前 Shell 会话(终端窗口)会被关闭,我们就无法看到输出结果了。(不要用source)

示例:

exit 0 #写在最后

test

Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试。
test 通常和 if 语句一起使用,并且大部分 if 语句都依赖 test。
Shell test 命令的用法为:

test expression

当 test 判断 expression 成立时,退出状态为 0,否则为非 0 值。

test 命令也可以简写为[ ],它的用法为:

[ expression ]

注意[]和expression之间的空格,这两个空格是必须的,否则会导致语法错误。[]的写法更加简洁,比 test 使用频率高。test 和 [ ] 是等价的

数值测试

选 项作 用
num1 -eq num2判断 num1 是否和 num2 相等。
num1 -ne num2判断 num1 是否和 num2 不相等。
num1 -gt num2判断 num1 是否大于 num2 。
num1 -lt num2判断 num1 是否小于 num2。
num1 -ge num2判断 num1 是否大于等于 num2。
num1 -le num2判断 num1 是否小于等于 num2。

示例:

#!/bin/bash
read age #输入年龄
if test $age -le 2; then #小于等于2
    echo "婴儿"
elif test $age -ge 3 && test $age -le 8; then #大于等于3 && 小于等于8
    echo "幼儿"
elif [ $age -ge 9 ] && [ $age -le 17 ]; then
    echo "少年"
elif [ $age -ge 18 ] && [ $age -le 25 ]; then
    echo "成年"
elif test $age -ge 26 && test $age -le 40; then
    echo "青年"
elif test $age -ge 41 && [ $age -le 60 ]; then
    echo "中年"
else
    echo "老年"
fi

字符串测试

选 项作 用
-z str判断字符串 str 是否为空。
-n str判断宇符串 str 是否为非空。
str1 = str2
str1 ==str2
=和==是等价的,都用来判断 str1 是否和 str2 相等。
str1 != str2判断 str1 是否和 str2 不相等。
str1 > str2判断 str1 是否大于 str2。>是>的转义字符,这样写是为了防止>被误认为成重定向运算符。
str1 < str2判断 str1 是否小于 str2。同样,<也是转义字符。

示例:

#!/bin/bash
str1="abc"
str2="abcd"
#检测字符串是否为空
if [ -z "$str1" ] || [ -z "$str2" ]
then
    echo "字符串不能为空"
    exit 0
fi
#比较字符串
if [ $str1 = $str2 ]
then
    echo "两个字符串相等"
else
    echo "两个字符串不相等"
fi

if [ $str1 \< $str2 ]
then
    echo "$str1小于$str2"
else
    echo "$str1大于等于$str2"
fi

输出:

两个字符串不相等
abc小于abcd

注意:
不管是比较数字还是字符串,Shell 都不支持 >= 和 <= 运算符
字符串比较大小是按字典序比较大小的,排在后面的比排在前面的大,如果一个字符串是另一个字符串的前缀,则前一个字符串小于后一个字符串。
"$str1"用双引号包起来是为了防止 $str1 是空字符串时出现错误。

文件测试
文件类型判断

选 项作 用
-b filename判断文件是否存在,并且是否为块设备文件。
-c filename判断文件是否存在,并且是否为字符设备文件。
-d filename判断文件是否存在,并且是否为目录文件。
-e filename判断文件是否存在。
-f filename判断文件是否存在,井且是否为普通文件。
-L filename判断文件是否存在,并且是否为符号链接文件。
-p filename判断文件是否存在,并且是否为管道文件。
-s filename判断文件是否存在,并且是否为非空。
-S filename判断该文件是否存在,并且是否为套接字文件。
文件权限判断
选 项作 用
:-:-
-r filename判断文件是否存在,并且是否拥有读权限。
-w filename判断文件是否存在,并且是否拥有写权限。
-x filename判断文件是否存在,并且是否拥有执行权限。
-u filename判断文件是否存在,并且是否拥有 SUID 权限。
-g filename判断文件是否存在,并且是否拥有 SGID 权限。
-k filename判断该文件是否存在,并且是否拥有 SBIT 权限。

文件比较

选 项作 用
filename1 -nt filename2判断 filename1 的修改时间是否比 filename2 的新。
filename -ot filename2判断 filename1 的修改时间是否比 filename2 的旧。
filename1 -ef filename2判断 filename1 是否和 filename2 的 inode 号一致,可以理解为两个文件是否为同一个文件。这个判断用于判断硬链接是很好的方法

示例:
先新建一个文件为file.txt

#!/bin/bash
filename="file.txt"
text="hello"
if test -w $filename && test -n $text #filename已存在且可写  text非空
then
    echo $text > $filename
    echo "写入成功"
else
    echo "写入失败"
fi

运行后file.txt中被写入hello

流程控制

if…else

if 语句语法格式

if condition
then
    command1 
    command2
    ...
    commandN 
fi

condition是判断条件,如果 condition 成立(返回“真”),那么 then 后边的语句将会被执行;如果 condition 不成立(返回“假”),那么不会执行任何语句。
因为有fi 来结尾,所以即使有多条语句也不需要用{ }包围起来。

if else 语法格式

if condition
then
    command1 
    command2
    ...
    commandN
else
    command
fi

如果 condition 成立,那么 then 后边的语句将会被执行;否则,执行 else 后边的语句。

if else-if else 语法格式

if  condition1
then
   statement1
elif condition2
then
    statement2
elif condition3
then
    statement3
……
else
   statementn
fi

注意,if 和 elif 后边都得跟着 then。

for循环

Shell for 循环有两种使用形式,C语言风格的 for 循环和Python风格的for循环。

C语言风格的 for 循环的用法如下:

for((exp1; exp2; exp3))
do
    statements
done

运行过程为:

  1. 先执行 exp1。

  2. 再执行 exp2,如果它的判断结果是成立的,则执行循环体中的语句,否则结束整个 for 循环。

  3. 执行完循环体后再执行 exp3。

  4. 重复执行步骤 2) 和 3),直到 exp2 的判断结果不成立,就结束循环。
    基本与C语言的for循环执行过程一样。

注意点:
exp1和exp2和exp3都是可省的,但分号不可省。
使用break可以立即退出循环。

Python 风格的 for in 循环
Python 风格的 for in 循环的用法如下:

for variable in value_list
do
    statements
done

variable 表示变量,value_list 表示取值列表,in 是 Shell 中的关键字。
每次循环都会从 value_list 中取出一个值赋给变量 variable,然后进入循环体(do 和 done 之间的部分),执行循环体中的 statements。直到取完 value_list 中的所有值,循环就结束了。

value_list 的形式有多种:

  1. 直接给出具体的值
    可以是数字,字符串
    例:
#!/bin/bash
for str in "shell" "非常" "强大" 
do
    echo $str
done
  1. 给出一个取值范围
    具体格式为:

{start…end}

注意中间用两个点号相连,而不是三个点号。这种形式只支持数字和字母。
3) 使用命令的执行结果

  1. 使用 Shell 通配符

  2. 使用特殊变量

while循环

Shell while 循环的用法如下:

while condition
do
    statements
done

condition表示判断条件,statements表示要执行的语句(可以只有一条,也可以有多条)

例:
计算从 1 加到 100 的和。

#!/bin/bash
i=1
sum=0
while ((i <= 100))
do
    ((sum += i))
    ((i++))
done
echo "The sum is: $sum"
until循环

unti 循环和 while 循环恰好相反,当判断条件不成立时才进行循环,一旦判断条件成立,就终止循环。
这与C语言的do…while循环不一样

Shell until 循环的用法如下:

until condition
do
    statements
done

condition表示判断条件,statements表示要执行的语句(可以只有一条,也可以有多条)

case in

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

case expression in
    pattern1)
        statement1
        ;;
    pattern2)
        statement2
        ;;
    pattern3)
        statement3
        ;;
    ……
    *)
        statementn
esac

注意点:

  • 取值后面必须为单词 in,每一模式必须以右括号结束。

  • 取值可以为变量或常数,匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;

  • 取值将检测匹配的每一个模式。一旦模式匹配,则执行完匹配模式相应命令后不再继续其他模式。如果无一匹配模式,使用星号 * 捕获该值,再执行后面的命令。

  • 可以没有*)部分。如果 expression 没有匹配到任何一个模式,那么就不执行任何操作。

  • 除最后一个分支外(这个分支可以是普通分支,也可以是*)分支),其它的每个分支都必须以;;结尾,;;代表一个分支的结束,不写的话会有语法错误。最后一个分支可以写;;,也可以不写。
    例:

#!/bin/bash
echo '输入 1 到 4 之间的数字:'
echo '你输入的数字为:'
read aNum
case $aNum in
    1)  echo '你选择了 1'
    ;;
    2)  echo '你选择了 2'
    ;;
    3)  echo '你选择了 3'
    ;;
    4)  echo '你选择了 4'
    ;;
    *)  echo '你没有输入 1 到 4 之间的数字'
    ;;
esac
break和continue

使用 while、until、for、select 循环时,如果想提前结束循环(在不满足结束条件的情况下结束循环),可以使用 break 或者 continue 关键字。
Shell 中的 break 和 continue 却能够跳出多层循环,也就是说,内层循环中的 break 和 continue 能够跳出外层循环。这一点与C语言不一样。
【break】
Shell break 关键字的用法为:

break n

n 表示跳出循环的层数,如果省略 n,则表示跳出当前的整个循环。

【continue】
Shell continue 关键字的用法为:

continue n

n 表示循环的层数:
如果省略 n,则表示 continue 只对当前层次的循环语句有效,遇到 continue 会跳过本次循环,忽略本次循环的剩余代码,直接进入下一次循环。
如果带上 n,比如 n 的值为 2,那么 continue 对内层和外层循环语句都有效,不但内层会跳过本次循环,外层也会跳过本次循环,其效果相当于内层循环和外层循环同时执行了不带 n 的 continue。

break 和 continue 的区别
break 用来结束所有循环,循环语句不再有执行的机会;continue 用来结束本次循环,直接跳到下一次循环,如果循环条件成立,还会继续循环。

(( ))和[[ ]]

(( ))的用法
双小括号 (( )) 的语法格式为:

((表达式))

表达式可以只有一个,也可以有多个,多个表达式之间以逗号,分隔。对于多个表达式的情况,以最后一个表达式的值作为整个 (( )) 命令的执行结果。

使用$获取 (( )) 命令的结果,不用$则不能获取到结果

在 (( )) 中使用变量无需加上$前缀,(( )) 会自动解析变量名。

运算操作符/运算命令说明
((a=10+66)
((b=a-15))
((c=a+b))
((c=a+b))
这种写法可以在计算完成后给变量赋值。以 ((b=a-15)) 为例,即将 a-15 的运算结果赋值给变量 c。
注意,使用变量时不用加$前缀,(( )) 会自动解析变量名。
a=$((10+66)
b=$((a-15))
c=$((a+b))
可以在 (( )) 前面加上 符号获取 ( ( ) ) 命令的执行结果,也即获取整个表达式的值。以 c = 符号获取 (( )) 命令的执行结果,也即获取整个表达式的值。以 c= 符号获取(())命令的执行结果,也即获取整个表达式的值。以c=((a+b)) 为例,即将 a+b 这个表达式的运算结果赋值给变量 c。
注意,类似 c=((a+b)) 这样的写法是错误的,不加$就不能取得表达式的结果。
((a>7 && b==c))(( )) 也可以进行逻辑运算,在 if 语句中常会使用逻辑运算。
echo $((a+10))需要立即输出表达式的运算结果时,可以在 (( )) 前面加$符号。
((a=3+5, b=a+10))对多个表达式同时进行计算。

示例:

$ echo $((6-3))

$ i=5
$ ((i=i*2))  #可以简写为 ((i*=2))。
$ echo $i

echo $((1+2**3-4%3)) #也可以直接将表达式的结果输出,注意不要丢掉 $ 符号
8

$ a=10
$ echo $((a++))  #如果++在a的后面,那么在输出整个表达式时,会输出a的值,因为a为10,所以表达式的值为10。
10

$ ((a=3+5, b=a+10))  #先计算第一个表达式,再计算第二个表达式
$ echo $a $b
8 18

$ c=$((4+8, a+b))  #以最后一个表达式的结果作为整个(())命令的执行结果
$ echo $c
26

示例2:
依次输入两个数计算和

#!/bin/bash

read -p "first num:" a
read -p "second num:" b

total=$((a+b))  #不需要写成total=$(($a+$b)) 但写成这样不会出问题

echo "$a + $b = $total"

[[ ]]的用法
[[ ]]是 Shell 内置关键字,不是命令。它和 test 命令类似,也用来检测某个条件是否成立。
可以认为 [[ ]] 是 test 的升级版,对细节进行了优化,并且扩展了一些功能。

[[ ]] 的用法为:

[[ expression ]]

当 [[ ]] 判断 expression 成立时,退出状态为 0,否则为非 0 值。注意[[ ]]和expression之间的空格,这两个空格是必须的,否则会导致语法错误。

[[ ]] 相对于[]的提升
不需要把变量名用双引号""包围起来,即使变量是空值,也不会出错。因为[[]]是关键字不是函数。
不需要、也不能对 >、< 进行转义。
[[ ]] 支持逻辑运算符写在里面。
[[ ]] 支持正则表达式。

示例:

#!/bin/bash
read str1
read str2
if [[ -z $str1 ]] || [[ -z $str2 ]]  #不需要对变量名加双引号  [[ ]] 支持逻辑运算符写在里面
then
    echo "字符串不能为空"
elif [[ $str1 < $str2 ]]  #不需要也不能对 < 进行转义
then
    echo "str1 < str2"
else
    echo "str1 >= str2"
fi

read tel
if [[ $tel =~ ^1[0-9]{10}$ ]] #判断开头是1,后面连续10位都是0-9之间
then
    echo "你输入的是手机号码"
else
    echo "你输入的不是手机号码"
fi

^1[0-9]{10}$的说明:

  • ^匹配字符串的开头(一个位置);
  • [0-9]{10}匹配连续的十个数字;
  • $匹配字符串的末尾(一个位置)。

Shell函数

Shell 函数的本质是一段可以重复使用的脚本代码,这段代码被提前编写好了,放在了指定的位置,使用时直接调取即可。

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

[ function ] funname [()]
{
    action;
    [return int;]
}

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

示例:

#!/bin/bash
#函数调用
fun  #可以将调用放在定义的前面,这种用法要注意

#函数定义
function fun(){
    echo "第一个shell函数"
}

#函数调用
fun

#计算所有参数的和
function getsum(){
    local sum=0
    for n in $@
    do
         ((sum+=n))
    done
    return $sum
}
getsum 10 20 55 15  #调用函数并传递参数
echo $?

$@表示函数的所有参数,$?表示函数的退出状态(返回值)。

注释

单行注释
以 # 开头的行就是注释。类似C语言里的//

# 注释1
# 注释2
# 注释3

多行注释

:<<EOF
注释内容...
注释内容...
注释内容...
EOF

EOF可以换成其他的字符串,字符,数字等,只要前后统一即可

:<<EOF222
注释内容...
注释内容...
注释内容...
EOF222

:<<0
注释内容...
注释内容...
注释内容...
0

综合应用

在指定时间内输入密码。

#!/bin/bash
if
    read -t 20 -sp "Enter password in 20 seconds(once) > " pass1 && printf "\n" &&  #第一次输入密码
    read -t 20 -sp "Enter password in 20 seconds(again)> " pass2 && printf "\n" &&  #第二次输入密码
    [ $pass1 == $pass2 ]  #判断两次输入的密码是否相等
then
    echo "Valid password"
else
    echo "Invalid password"
fi

参考:
https://www.runoob.com/linux/linux-shell.html
http://c.biancheng.net/view/706.html
https://blog.csdn.net/qq_36119192/article/details/82964713

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吾爱技术圈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值