前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >shell 教程,这次我要步步详解,学会了以后能早点下班

shell 教程,这次我要步步详解,学会了以后能早点下班

作者头像
看、未来
发布2022-05-06 14:53:43
4.2K0
发布2022-05-06 14:53:43
举报
文章被收录于专栏:CSDN搜“看,未来”

文章目录

shell 变量

在 Bash shell 中,每一个变量的值都是字符串,无论你给变量赋值时有没有使用引号,值都会以字符串的形式存储。

当然,如果有必要,你也可以使用 Shell declare 关键字显式定义变量的类型,但在一般情况下没有这个需求,Shell 开发者在编写代码时自行注意值的类型即可。

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

代码语言:javascript
复制
variable=value
variable='value'
variable="value"

variable 是变量名,value 是赋给变量的值。如果 value 不包含任何空白符(例如空格、Tab 缩进等),那么可以不使用引号;如果 value 包含了空白符,那么就必须使用引号包围起来。使用单引号和使用双引号也是有区别的,稍后我们会详细说明。

注意,赋值号=的周围不能有空格,这可能和你熟悉的大部分编程语言都不一样。

Shell 变量的命名规范和大部分编程语言都一样:

代码语言:javascript
复制
变量名由数字、字母、下划线组成;
必须以字母或者下划线开头;
不能使用 Shell 里的关键字(通过 help 命令可以查看保留关键字)。

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

代码语言:javascript
复制
author="长生"
echo $author
echo ${author}

变量名外面的花括号{ }是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,比如下面这种情况:

代码语言:javascript
复制
skill="Java"
echo "I am good at ${skill}Script"

如果不给 skill 变量加花括号,写成echo “I am good at $skillScript”,解释器就会把 $skillScript 当成一个变量(其值为空),代码执行结果就不是我们期望的样子了。

推荐给所有变量加上花括号{ },这是个良好的编程习惯。

已定义的变量,可以被重新赋值

第二次对变量赋值时不能在变量名前加,只有在使用变量时才能加

前面我们还留下一个疑问,定义变量时,变量的值可以由单引号’ '包围,也可以由双引号" "包围,它们到底有什么区别呢?不妨以下面的代码为例来说明:

以单引号’ '包围变量的值时,单引号里面是什么就输出什么,即使内容中有变量和命令(命令需要反引起来)也会把它们原样输出。这种方式比较适合定义显示纯字符串的情况,即不希望解析变量、命令等的场景。

以双引号" "包围变量的值时,输出时会先解析里面的变量和命令,而不是把双引号中的变量名和命令原样输出。这种方式比较适合字符串中附带有变量和命令并且想将其解析后再输出的变量定义。

Shell 也支持将命令的执行结果赋值给变量,常见的有以下两种方式:

代码语言:javascript
复制
variable=`command`
variable=$(command)

第一种方式把命令用反引号 ``(位于 Esc 键的下方)包围起来,反引号和单引号非常相似,容易产生混淆,所以不推荐使用这种方式;第二种方式把命令用$()包围起来,区分更加明显,所以推荐使用这种方式。

例如,我在 demo 目录中创建了一个名为 log.txt 的文本文件,用来记录我的日常工作。下面的代码中,使用 cat 命令将 log.txt 的内容读取出来,并赋值给一个变量,然后使用 echo 命令输出。

代码语言:javascript
复制
$ log=$(cat log.txt)
$ echo $log
$ log=`cat log.txt`
$ echo $log

使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。

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

代码语言:javascript
复制
unset variable_name

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

shell 有一些内置的变量,混个眼熟:

代码语言:javascript
复制
$0:当前脚本的文件名
$num:位置参数。num为从1开始的数字,$1是第一个参数,$2是第二个参数,${10}是第十个参数
$#:传入脚本的参数的个数
$*:所有的位置参数(作为单个字符串)
$@:所有的位置参数(每个都作为独立的字符串)。
$?:当前shell进程中,上一个命令的返回值,如果上一个命令成功执行则$?的值为0,否则为其他非零值,常用做if语句条件
$$:当前shell进程的pid
$!:后台运行的最后一个进程的pid
$_:之前命令的最后一个参数

shell 中的变量还有一点特性,可以设置默认值,即为了防止一些意外导致变量成为空值而导致脚本崩溃。

(1)${var-default}:如果变量var没有被声明,那么就使用默认值,否则就是用var初始化的值

代码语言:javascript
复制
echo ${a-default} #结果为default
代码语言:javascript
复制
a=
echo ${a-default} #结果为空
代码语言:javascript
复制
a="test"
echo ${a-default} #结果为test

(2)${var:-default}:如果变量var没有被声明或者已经声明,但是赋值为空,那么就使用默认值,否则就使用var初始化的值

代码语言:javascript
复制
echo ${a:-default} #结果为default
代码语言:javascript
复制
a=
echo ${a:-default} #结果为default
代码语言:javascript
复制
a="test"
echo ${a:-default} #结果为test

(另外的写法:${parameter=default}${parameter:=default}与上述的两个是一样的)

(1)${var+alt_value}:如果变量var被声明了, 无论var的值为空或者var初始化为一个值,都使用alt_value, 如果没有声明就为空.

(2)${var:+alt_value}:如果变量var被初始化为一个非空的值, 那么就使用alt_value, 如果没有被声明或者已声明但初始化为空值就为空

(1)${var?err_msg}:如果var已经被声明,那么就使用设置的值,否则打印err_msg错误消息。

(2)${parameter:?err_msg}:如果parameter已经被初始化为一个非空的值, 那么就使用设置的值, 否则打印err_msg错误消息。

要看吐了吧,一个变量就这么多东西?别急。还有最后一个尾巴。

前期要慢一点,后期要快一点。

打基本功的时候不要嫌烦。

Shell 变量的作用域可以分为三种:

代码语言:javascript
复制
有的变量只能在函数内部使用,这叫做局部变量(local variable);
有的变量可以在当前 Shell 进程中使用,这叫做全局变量(global variable);
而有的变量还可以在子进程中使用,这叫做环境变量(environment variable)。

Shell 也支持自定义函数,但是 Shell 函数和 C++、Java、C# 等其他编程语言函数的一个不同点就是:在 Shell 函数中定义的变量默认也是全局变量,它和在函数外部定义变量拥有一样的效果。要想变量的作用域仅限于函数内部,可以在定义时加上local命令,此时该变量就成了局部变量。

所谓全局变量,就是指变量在当前的整个 Shell 进程中都有效。每个 Shell 进程都有自己的作用域,彼此之间互不影响。在 Shell 中定义的变量,默认就是全局变量

需要强调的是,全局变量的作用范围是当前的 Shell 进程,而不是当前的 Shell 脚本文件,它们是不同的概念。打开一个 Shell 窗口就创建了一个 Shell 进程,打开多个 Shell 窗口就创建了多个 Shell 进程,每个 Shell 进程都是独立的,拥有不同的进程 ID。在一个 Shell 进程中可以使用 source 命令执行多个 Shell 脚本文件,此时全局变量在这些脚本文件中都有效。

全局变量只在当前 Shell 进程中有效,对其它 Shell 进程和子进程都无效。如果使用export命令将全局变量导出,那么它就在所有的子进程中也有效了,这称为“环境变量”。

环境变量被创建时所处的 Shell 进程称为父进程,如果在父进程中再创建一个新的进程来执行 Shell 命令,那么这个新的进程被称作 Shell 子进程。当 Shell 子进程产生时,它会继承父进程的环境变量为自己所用,所以说环境变量可从父进程传给子进程。不难理解,环境变量还可以传递给孙进程。

注意,两个没有父子关系的 Shell 进程是不能传递环境变量的,并且环境变量只能向下传递而不能向上传递,即“传子不传父”。

创建 Shell 子进程最简单的方式是运行 bash 命令,如图所示:

在这里插入图片描述
在这里插入图片描述

通过exit命令可以一层一层地退出 Shell。

通过 export 导出的环境变量只对当前 Shell 进程以及所有的子进程有效,如果最顶层的父进程被关闭了,那么环境变量也就随之消失了,其它的进程也就无法使用了,所以说环境变量也是临时的。

有读者可能会问,如果我想让一个变量在所有 Shell 进程中都有效,不管它们之间是否存在父子关系,该怎么办呢?

只有将变量写入 Shell 配置文件中才能达到这个目的!Shell 进程每次启动时都会执行配置文件中的代码做一些初始化工作,如果将变量放在配置文件中,那么每次启动进程都会定义这个变量。

shell 的运行方式

上面提到了,这里就补一下吧。

代码语言:javascript
复制
./script.sh     # 利用小数点来执行
代码语言:javascript
复制
sh script.sh 或 bash script.sh      #  利用bash(sh)来执行脚本

前面两种方式其实都是一样的:都是在当前父进程下的子进程中执行,子进程完成后,子进程中的各项变量或操作将会结束而不会传回到父进程中。

代码语言:javascript
复制
source script.sh

而通过第三种方式执行(source test.sh)的话,在父进程中就起作用了:

这就是直接执行与用source命令执行的区别,前者只作用于子进程本身,后者则作用于整个父进程。

因此:如要想不注销系统,并让全局配置文件生效,则必须用source命令。

注意:在执行时要赋权限

补充知识点:

一个规范的Shell脚本在第一行会指出由哪个程序(解释器)来执行脚本中的内容,这一行内容在Linux bash的编程一般为:

代码语言:javascript
复制
#!/bin/bash

代码语言:javascript
复制
#!/bin/sh

sh为bash的软链接,大多数情况下,脚本的开头使用“#! /bin/bash”和“#! /bin/sh”是没有区别的。

shell 运算

先看一下整数运算,这个相对来说不用花很多的时间。

直接上图,不想说话、

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

这里面选一个自己喜欢的用就好了,我选 (())

在这里插入图片描述
在这里插入图片描述

在 (( )) 中使用变量无需加上$前缀,(( )) 会自动解析变量名,这使得代码更加简洁,也符合程序员的书写习惯。

关系运算符和逻辑运算符等其他的运算符也先看一下,后面分支循环的时候才开始演示。

关系运算符:

在这里插入图片描述
在这里插入图片描述

逻辑运算符:

在这里插入图片描述
在这里插入图片描述

字符串运算符:

在这里插入图片描述
在这里插入图片描述

文件测试运算符:

在这里插入图片描述
在这里插入图片描述

Shell 注释

以 # 开头的行就是注释,会被解释器忽略。

多行注释还可以使用以下格式:

代码语言:javascript
复制
:<<EOF
注释内容...
注释内容...
注释内容...
EOF

EOF 也可以使用其他符号:

代码语言:javascript
复制
:<<'
注释内容...
注释内容...
注释内容...
'

上点轻松的,接下来是一道硬菜。

shell 字符串处理

echo 是一个 Shell 内建命令,用来在终端输出字符串,并在最后默认加上换行符。请看下面的例子:

代码语言:javascript
复制
#!/bin/bash
name="看,未来"
url="https://blog.csdn.net/qq_43762191"
echo "读者,你好!"  #直接输出字符串
echo $url  #输出变量
echo "${name}的网址是:${url}"  #双引号包围的字符串中可以解析变量
echo '${name}的网址是:${url}'  #单引号包围的字符串中不能解析变量

echo 命令输出结束后默认会换行,如果不希望换行,可以加上-n参数。

默认情况下,echo 不会解析以反斜杠\开头的转义字符。比如,\n表示换行,echo 默认会将它作为普通字符对待,我们可以添加-e参数来让 echo 命令解析转义字符。例如:

shell 转义字符表:

代码语言:javascript
复制
\\  输入\
\a  输出警告音
\b  退格,即向左删除一个字符
\c  取消输出行末的换行符,和-n选项一致
\e  Esc
\f  换页符
\n  换行
\r  回车
\t  制表,即Tab
\v  垂直制表符
\0nnn 按照八进制ASCII码表输出字符,其中0为数字零,nnn是三位八进制数
\xhh  按照十六进制ASCII码表输出字符,其中hh是两位十六进制数

先看一下,纯纯的基操:

在这里插入图片描述
在这里插入图片描述

对于字符串提取:

从指定位置开始截取 这种方式需要两个参数:除了指定起始位置,还需要截取长度,才能最终确定要截取的字符串。

既然需要指定起始位置,那么就涉及到计数方向的问题,到底是从字符串左边开始计数,还是从字符串右边开始计数。答案是 Shell 同时支持两种计数方式。

1、从字符串左边开始计数 如果想从字符串的左边开始计数,那么截取字符串的具体格式如下:

代码语言:javascript
复制
${string: start :length}

其中,string 是要截取的字符串,start 是起始位置(从左边开始,从 0 开始计数),length 是要截取的长度(省略的话表示直到字符串的末尾)。

2、 从右边开始计数 如果想从字符串的右边开始计数,那么截取字符串的具体格式如下:

代码语言:javascript
复制
${string: 0-start :length}

同第 1) 种格式相比,第 2) 种格式仅仅多了0-,这是固定的写法,专门用来表示从字符串右边开始计数。

这里需要强调两点:

代码语言:javascript
复制
从左边开始计数时,起始数字是 0(这符合程序员思维);从右边开始计数时,起始数字是 1(这符合常人思维)。计数方向不同,起始数字也不同。
不管从哪边开始计数,截取方向都是从左到右。

从指定字符(子字符串)开始截取 这种截取方式无法指定字符串长度,只能从指定字符(子字符串)截取到字符串末尾。Shell 可以截取指定字符(子字符串)右边的所有字符,也可以截取左边的所有字符。

1、 使用 # 号截取右边字符 使用#号可以截取指定字符(或者子字符串)右边的所有字符,具体格式如下:

代码语言:javascript
复制
${string#*chars}

其中,string 表示要截取的字符,chars 是指定的字符(或者子字符串),*是通配符的一种,表示任意长度的字符串。*chars连起来使用的意思是:忽略左边的所有字符,直到遇见 chars(chars 不会被截取)。

如果不需要忽略 chars 左边的字符,那么也可以不写*,例如:

注意,以上写法遇到第一个匹配的字符(子字符串)就结束了。例如:

如果希望直到最后一个指定字符(子字符串)再匹配结束,那么可以使用##,具体格式为:

代码语言:javascript
复制
${string##*chars}

2、 使用 % 截取左边字符 使用%号可以截取指定字符(或者子字符串)左边的所有字符,具体格式如下:

代码语言:javascript
复制
${string%chars*}

请注意*的位置,因为要截取 chars 左边的字符,而忽略 chars 右边的字符,所以*应该位于 chars 的右侧。其他方面 % 和 # 的用法相同,这里不再赘述。

最后,我们对以上 8 种格式做一个汇总,请看下表:

在这里插入图片描述
在这里插入图片描述

注意,以上所有操作皆不会对原字符串造成任何实质性影响。若要保留结果,请赋值给另一个变量。

对于字符串替换: 命令已经在上面了,我这里就直接放示例吧:

代码语言:javascript
复制
$ greet='hello,world,hello,linux'
$ echo $greet
hello,world,hello,linux
$ echo ${greet/hello/welcome to}
welcome to,world,hello,linux
$ echo ${greet//hello/welcome to}
welcome to,world,welcome to,linux
$ echo $greet
hello,world,hello,linux
代码语言:javascript
复制
$ echo $greet
hello,world,hello,linux
$ echo ${greet/#world/php}
hello,world,hello,linux
$ echo ${greet/#hello/welcome to}
welcome to,world,hello,linux
$ echo ${greet/%linux/php}
hello,world,hello,php 

关于字符串比较:

代码语言:javascript
复制
    [[ "a.txt" == a* ]]        # 逻辑真 (pattern matching)  
    [[ "a.txt" =~ .*\.txt ]]   # 逻辑真 (regex matching)  
    [[ "abc" == "abc" ]]       # 逻辑真 (string comparision)   
    [[ "11" < "2" ]]           # 逻辑真 (string comparision), 按ascii值比较  

关于字符串拼接:

在 Shell 中你不需要使用任何运算符,将两个字符串并排放在一起就能实现拼接,非常简单粗暴。请看下面的例子:

代码语言:javascript
复制
#!/bin/bash
name="Shell"
url="https://editor.csdn.net/md?not_checkout=1&articleId=123036200"
str1=$name$url  #中间不能有空格
str2="$name $url"  #如果被双引号包围,那么中间可以有空格
str3=$name": "$url  #中间可以出现别的字符串
str4="$name: $url"  #这样写也可以
str5="${name}Script: ${url}index.html"  #这个时候需要给变量名加上大括号
echo $str1
echo $str2
echo $str3
echo $str4
echo $str5

是不是觉得 shell 的字符串也就仅此而已?那就大错特错了。因为我还没上那些专业的工具呢。后头会上。

shell 条件测试

常用的测试命令有以下三中方式,其中使用最多的是第二种。

代码语言:javascript
复制
test Expression
[ Expression ]
[[ Expression ]]

以上表达式需要注意,Expression 指需要测试的表达式, 且前后必须要有空格,否则视为语法错误。 [] 和 [[]] 有所不同, [] 是命令,[[]] 是 Linux 中的关键字。

在 Linux 中可以使用 echo $? 查看上一条命令是否执行成功,其中 0 表示成功, 1-255 表示失败,每一个数字表示不同的失败原因。

代码语言:javascript
复制
判断上条语句执行是否成功:

shell中使用符号“$?”来显示上一条命令执行的返回值,如果为0则代表执行成功,其他表示失败。

```bash
if [ $? -ne 0 ]; then
    echo "failed"
else
    echo "succeed"
fi

关于那些运算符都在前面已经贴出。

shell 分支循环

if 分支:

代码语言:javascript
复制
if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ else
COMMANDS; ] fi

单分支结构:

代码语言:javascript
复制
if 判断条件
then
    条件为真的分支代码
fi

# 或者

if 判断条件;then
    条件为真的分支代码
fi

双分支结构:

代码语言:javascript
复制
if 判断条件; then 
    条件为真的分支代码
else
    条件为假的分支代码
fi

多分支结构:

代码语言:javascript
复制
if 判断条件1; then 
    条件1为真的分支代码
elif 判断条件2; then 
    条件2为真的分支代码
elif 判断条件3; then 
    条件3为真的分支代码
...
else
    以上条件都为假的分支代码
fi

分支嵌套我就不贴了哈,

也可以把 then 单独放一行,那就不需要分号:

代码语言:javascript
复制
if 判断条件
then 
    条件为真的分支代码
else
    条件为假的分支代码
fi

for 循环

代码语言:javascript
复制
#!/bin/bash  
  
for value in {1..5}  
#for value in 1 2 3 4 5  
do  
     echo "Now value is $value..."  
done 

do 和 done 之间的命令称为循环体,执行次数和list列表中常数或字符串的个数相同。for循环,首先将in 后 list 列表的第一个常数或字符串赋值给循环变量,然后执行循环体,以此执行 list;最后执行do 命令后的命令序列。 shell 支持列表 for 循环使用略写的计数方式,1~5 的范围用 {1…5} 表示(大括号不能去掉,否则会当作一个字符串处理)。 shell 中还支持按规定的步数进行跳跃的方式实现列表 for 循环,例如计算1~100内所有的奇数之和。

代码语言:javascript
复制
#!/bin/bash
sum=0

for i in {1..100..2}
do
    let "sum+=i"
done    
echo "sum=$sum"

字符串for循环和数字for循环没有本质区别,只是形式有细微差别而已。下面列出几种常见的用法。

显示参数列表的所有单词:

代码语言:javascript
复制
#!/bin/bash  

for i in v1 v2 v3 ;  
do  
echo value is: $i;  
done 

显示list中的所有单词:

代码语言:javascript
复制
#!/bin/bash

list="Earth is the Home of Human! ";
for i in $list;
do
echo word is $i;
done

传入参数列表:

代码语言:javascript
复制
#!/bin/bash  
  
for i in $* ;  
do  
echo $i is input value\! ;  
done

路径查找for循环: 通过for循环可以查找指定目录下的文件列表,并且可以使用通配符:

代码语言:javascript
复制
#!/bin/bash

for file in $( ls )
do
   echo "file: $file"
done

通配符查找指定路径:

代码语言:javascript
复制
#!/bin/bash  
  
for file in /root/*;  
do  
echo $file;  
done

通配符查找指定路径下符合指定扩展名的文件路径:

代码语言:javascript
复制
#!/bin/bash  
  
for file in /root/study/shell/*.sh;  
do  
echo $file;  
done

类C风格的for循环,和C没有太大的区别,这里只进行示例,不再赘述。

代码语言:javascript
复制
#!/bin/bash

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

或者

代码语言:javascript
复制
#!/bin/bash

awk 'BEGIN{
for(i=1; i<=5; i++) 
print (i*i+1)
}'

结果:

计算1~5中数字的平方+1

while 循环

我直接上实例吧: 1.利用while循环计算1到100的和:

代码语言:javascript
复制
#!/bin/bash
i=1
sum=0
while [ $i -le 100 ]
do
	let sum=sum+$i
	let i++
done
echo $sum

示例代码2:利用while循环计算1到100之间所有奇数之和

代码语言:javascript
复制
#!/bin/bash
i=1
sum=0
while [ $i -le 100 ]
do
	let sum=sum+$i
	let i+=2
done
echo $sum

示例代码3:利用while循环计算1到100之间所有偶数之和

代码语言:javascript
复制
#!/bin/bash
i=2
sum=0
while [ $i -le 100 ]
do
	let sum=sum+$i
	let i+=2
done
echo $sum

2.利用while循环打印**

示例代码:利用while循环打印一个5x5的*

代码语言:javascript
复制
#!/bin/bash
i=1
j=1
while [ $i -le 5 ]
do
 while [ $j -le 5 ]
 do
   echo -n "* "
   let j++
 done
 echo
 let i++
 let j=1
done

3.使用read结合while循环读取文本文件:

示例代码1:

代码语言:javascript
复制
#!/bin/bash
file=$1         #将位置参数1的文件名复制给file
if [ $# -lt 1 ];then   #判断用户是否输入了位置参数
 echo "Usage:$0 filepath"
 exit
fi
while read -r line  #从file文件中读取文件内容赋值给line
#(使用参数r会屏蔽文本中的特殊符号,只做输出不做转译)
do
 echo $line    #输出文件内容
done  < $file

示例2:按列读取文件内容

代码语言:javascript
复制
#!/bin/bash
file=$1
if [[ $# -lt 1 ]]
then
 echo "Usage: $0 please enter you filepath"
 exit
fi
while read -r f1 f2 f3  #将文件内容分为三列
do
 echo "file 1:$f1 ===> file 2:$f2 ===> file 3:$f3"  #按列输出文件内容
done < "$file"

4.while循环中的死循环:

示例:利用死循环,让用户做选择,根据客户的选择打印相应结果

代码语言:javascript
复制
#!/bin/bash
#打印菜单
while :
do
 echo "********************"
 echo "    menu    "
 echo "1.tima and date"
 echo "2.system info"
 echo "3.uesrs are doing"
 echo "4.exit"
 echo "********************"
 read -p "enter you choice [1-4]:" choice
#根据客户的选择做相应的操作
 case $choice in
  1)
  echo "today is `date +%Y-%m-%d`"
  echo "time is `date +%H:%M:%S`"
  read -p "press [enter] key to continue..." Key  #暂停循环,提示客户按enter键继续
  ;;
  2)
  uname -r
  read -p "press [enter] key to continue..." Key
  ;;
  3)
  w
  read -p "press [enter] key to continue..." Key
  ;;
  4)
  echo "Bye!"
  exit 0
  ;;
  *)
  echo "error"
  read -p "press [enter] key to continue..." Key
  ;;
 esac
done

这里的 swich 有点超纲,不过不急,马上就来:

case…in…
代码语言:javascript
复制
case $变量名 in
"值 1")
如果变量的值等于值 1,则执行程序 1
;;

"值 2")
如果变量的值等于值 2,则执行程序 2
;; …省略其他分支…
*)
如果变量的值都不是以上的值,则执行此程序

;;
esac
代码语言:javascript
复制
echo '输入 1 到 4 之间的数字:'
echo '你输入的数字为:'
read aNum
case $aNum in
    1)  echo '你选择了 1'
    ;;
    2)  echo '你选择了 2'
    ;;
    3|4)  echo '你选择了 3 或 4'
    ;;
    *)  echo '你没有输入 1 到 4 之间的数字'
    ;;
esac

case in 的 pattern 部分支持简单的正则表达式,具体来说,可以使用以下几种格式:

在这里插入图片描述
在这里插入图片描述

最后一个分支*)并不是什么语法规定,它只是一个正则表达式,表示任意字符串,所以不管 expression 的值是什么,)总能匹配成功。

下面的例子演示了如何在 case in 中使用正则表达式:

代码语言:javascript
复制
#!/bin/bash
printf "Input a character: "
read -n 1 char
case $char in
    [a-zA-Z])
        printf "\nletter\n"
        ;;
    [0-9])
        printf "\nDigit\n"
        ;;
    [0-9])
        printf "\nDigit\n"
        ;;
    [,.?!])
        printf "\nPunctuation\n"
        ;;
    *)
        printf "\nerror\n"
esac
关于 continue 和 break 我就不举例了吧。

shell 函数

代码语言:javascript
复制
#其标准写法为:
function 函数名() { #<==推荐的书写函数的方法(带括号)
	指令……
	return n
}
#简化写法1:
function 函数名{ #<==不推荐使用此方法(无括号)
	指令……
	return n
}
#简化写法2:
函数名() { #<==不用function的方法
	指令……
	return n
}

函数嘛,天天打交道的,直接上例子:

1、在shell文件内部定义函数并引用:

代码语言:javascript
复制
[~/shell/function]# cat factorial.sh
#!/bin/bash
function factorial
{
	factorial=1
	for (( i=1;i <= $1;i++ ))
	        do
	        factorial=$[ $factorial * $i ]
	        done
	echo $1的阶乘是:$factorial
}
echo '程序名':$0,用于求阶乘
factorial $1
[~/shell/function]# ./factorial.sh 10

程序名:./factorial.sh,用于求阶乘 10的阶乘是:3628800

2.返回值

函数返回码是指函数最后一条命令的状态码,可以用于函数返回值 使用return命令手动指定返回值:

代码语言:javascript
复制
[~/shell/function]# cat return.sh
#!/bin/bash
function fun1 {
  read -p "enter a: " a
  echo -n "print 2a: "
  return $[ $a * 2 ]
}
fun1
echo "return value $?"
[~/shell/function]# ./return.sh
enter a: 100
print 2a: return value 200

由于shell状态码最大是255,所以当返回值大于255时会出错。

3.函数输出

为了返回大于255的数、浮点数和字符串值,最好用函数输出到变量:

代码语言:javascript
复制
[~/shell/function]# cat ./fun_out.sh
#!/bin/bash
function fun2 {
  read -p "enter a: " a
  echo -n "print 2a: "
  echo $[ $a * 2 ]
}
result=`fun2`
echo "return value $result"
[~/shell/function]# ./fun_out.sh    
enter a: 400
return value print 2a: 800

4.向函数传递参数(使用位置参数):

代码语言:javascript
复制
[~/shell/function]# cat ./parameter.sh
#!/bin/bash
if [ $# -ne 3 ]
then
    echo "usage: $0 a b c"
    exit
fi
fun3() {
    echo $[ $1 * $2 * $3 ]
}
result=`fun3 $1 $2 $3`
echo the result is $result
[~/shell/function]# ./parameter.sh  1 2 3
the result is 6
[~/shell/function]# ./parameter.sh  1 2
usage: ./parameter.sh a b c

5.全局变量与局部变量

默认条件下,在函数和shell主体中建立的变量都是全局变量,可以相互引用,当shell主体部分与函数部分拥有名字相同的变量时,可能会相互影响,例如:

代码语言:javascript
复制
[~/shell/function]# cat ./variable.sh   
#!/bin/bash
if [ $# -ne 3 ]
then
    echo "usage: $0 a b c"
    exit
fi
temp=5
value=6
echo temp is: $temp
echo value is: $value
fun3() {
    temp=`echo "scale=3;$1*$2*$3" | bc -ql`  
    result=$temp
}
fun3 $1 $2 $3
echo "the result is $result"
if [ `echo "$temp > $value" | bc -ql` -ne 0 ]
then
    echo "temp is larger"
else
    echo "temp is still smaller"
fi
[~/shell/function]# ./variable.sh  12 3 2
temp is: 5
value is: 6
the result is 72
temp is larger

在这种情况下,在函数内部最好使用局部变量,消除影响。

代码语言:javascript
复制
[~/shell/function]# cat ./variable.sh
#!/bin/bash
if [ $# -ne 3 ]
then
    echo "usage: $0 a b c"
    exit
fi
temp=5
value=6
echo temp is: $temp
echo value is: $value
fun3() {
    local temp=`echo "scale=3;$1*$2*$3" | bc -ql`  
    result=$temp
}
fun3 $1 $2 $3
echo "the result is $result"
if [ `echo "$temp > $value" | bc -ql` -ne 0 ]
then
    echo "temp is larger"
else
    echo "temp is still smaller"
fi
[~/shell/function]# ./variable.sh  12 3 2
temp is: 5
value is: 6
the result is 72
temp is still smaller

6.向函数传递数组变量:

代码语言:javascript
复制
[~/shell/function]# cat array.sh
#!/bin/bash
a=(11 12 13 14 15)
echo ${a[*]}
function array(){
  echo parameters : "$@"
  local factorial=1
  for value in "$@"
  do
    factorial=$[ $factorial * $value ]
  done
  echo $factorial
}
array ${a[*]}
[~/shell/function]# ./array.sh
11 12 13 14 15
parameters : 11 12 13 14 15
360360

7.函数返回数组变量

[

代码语言:javascript
复制
~/shell/function]# cat array1.sh
#!/bin/bash
a=(11 12 13 14 15)
function array(){
  echo parameters : "$@"
  local newarray=(`echo "$@"`)
  local element="$#"
  local i
  for (( i = 0; i < $element; i++ ))
  {
    newarray[$i]=$[ ${newarray[$i]} * 2 ]   
  }
  echo  new value:${newarray[*]}
}
result=`array ${a[*]}`
echo ${result[*]}
[~/shell/function]# ./array1.sh
parameters : 11 12 13 14 15 new value:22 24 26 28 30
还有三个大块头,和一些零零碎碎的东西比如 shift 是位置变量左移一位。 三个大块头 sed、swk、cut,基本是对字符串进行处理的,其效率是会比 shell 直接上来做正则要快得多的,其中以 cut 较为简单,awk 功能强大,后面会为它们专门写。

这篇已经迁延日久了,我 go 语言都学好了,接下来得上手 ansible 了,咳,卷吧。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-02-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • shell 变量
  • shell 的运行方式
  • shell 运算
  • Shell 注释
  • shell 字符串处理
  • shell 条件测试
  • shell 分支循环
  • shell 函数
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档