首页 > 系统相关 >Shell程序设计语言

Shell程序设计语言

时间:2025-01-14 19:32:46浏览次数:1  
标签:Shell 语言 soft sh echo shell master 程序设计 root

Shell程序设计语言

一、认识Shell

1.1 编程语言的种类

# 机器语言:站在计算机(奴隶)的角度,说计算机能听懂的语言,那就是直接用二进制编程,直接操作硬件;
         优点:执行效率最高
         缺点:1、二进制指令难以记忆,开发时极容易出错
              2、开发程序的复杂度高:即便是完成一个简单的功能,需要用到的二进制指令的条数都会非常多
# 汇编语言:站在计算机(奴隶)的角度,简写的英文标识符取代二进制指令去编写程序,本质仍然是直接操作硬件;
         优点:解决了机器语言的二进制指令难以记忆的问题,执行效率还是高
         缺点:开发程序的复杂度依然很高:因为汇编语言就是用英文标签对应原来的二进制指令,好记归好记,开发
              的复杂度却没有降低
	ps:因为上述两类语言都是在直接与计算机硬件打交道,离计算机硬件比较近,所以又统称为低级语言
# 高级语言:站在人(奴隶主)的角度,说人话,即用人类的字符去编写程序,屏蔽了硬件操作
         优点:开发复杂度地,即开发效率高
         缺点:速度肯定是不如低级语言,一直到今天,对速度要求极高的场景还会用到低级语言,比如操作系统的
              调度程序

1.2 什么是Shell

# 1、一层指的是shell这门语言,是一种特定的语法风格,规范等
 
# 2、另外一层指的是专门用于解释执行shell这门语言语法的应用程序,即shell解释器,我们常用的是bash

我们centOS7自带了Shell解释器,不需要额外安装

image-20240125172613655

一般情况下,我们创建的用户默认是/bin/bash

image-20240125172630605

shell 本身就是一门解释型、弱类型、动态语言,与 python 相对应, Python 属于解释型、强类型、动态语言,我们平时登录成功一个用户后进入的就是 bash 解释器的交互式环境,我们敲的命令其实都属于 shell 这门语言的语法

shell的优点

1. 自动备份
2. 自动部署
3. 监控脚本
4. 自动运行任务

1.3 第一个shell脚本

其实我们写shell脚本有两个地方可以写:1. 交互式环境(简单理解就是我们的linux窗口)2. 写到文件中(通常以.sh结尾)

交互式环境写脚本

image-20240125172636678

脚本文件

其实将我们在交互式环境里敲的命令直接放到一个文本文件里,一个简单的 shell 程序就完成了 
注意:shell 解释器执行程序是解释执行的,即打开文件内容,因此文件的后缀名没有硬性限制,但通常定义为.sh 结尾

image-20240125172642684

解释:

# 第一行表示我们选择使用的shell解释器是bash,也可以用:#!/usr/bin/env bash
    shell的第一行比较特殊,一般都会以#!开始来指定使用的shell解释的类型。
    在linux中,除了bash shell以外,还有很多版本的shell, 例如zsh、dash等等...
    不过bash shell还是我们使用最多的。
# 第二行以#号开始,表示本行是注释,注释是对代码的解释说明,注释的内容不会执行,对关键代码加注释是一种好的编程习惯
# 第三行中的echo是linux中的输出命令,该行的意思很明显的就是输出hello world!

1.4 shell脚本的运行方式

方式一:输入脚本的绝对路径或相对路径

/root/shujia.sh

./shujia.sh

# 注意:执行的必须是一个可执行文件  chmod a+x xxx.sh

方式二:bash或sh +脚本

sh helloworld.sh

# 注意:当脚本没有X权限时,root和文件所有者通过该方式可以正常执行

方式三:在脚本的路径前再加"或source

source helloworld.sh

区别:

第一种和第二种会新开一个bash,不同bash中的变量无法共享。

第三种是在同一个shell里面执行的

# 一个shell环境就是一个单独的全局作用域,不同的shell环境,无法访问彼此shell环境中的变量
[root@master ~]$ x=99
[root@master ~]$ cat /a/b/hello.sh 
#!/bin/bash
 
echo "hello world!"  
echo $x  # 我们在这里访问一下全局变量x
 
[root@master ~]$ source /a/b/hello.sh  # 在当前shell环境执行,可以访问到x
hello world!
111         # 取到了x的值
[root@master ~]$ bash /a/b/hello.sh  # 在子shell环境执行,不能访问到x 
hello world!
          # 此处打印空

解决方案:export :可以将当前进程的变量传递给子进程去使用

注意:将来配置profile的时候所有的变量前必须加export

二、Shell编程语法

2.1 注释

  1. 可以加在被注释代码的正上方单独一行,或者被注释代码的正后方,例如

    echo "hello" # 注释。。。

  2. 不用全部加注释,只需要在自己觉得重要或不好理解的部分加注释即可

  3. 注释可以用中文或英文,但不要用拼音

分类:

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

通过每一行加一个 # 号设置多行注释
多行注释 :<<
# 特殊的多行注释 
# end of file
:<<EOF  
注释内容... 
注释内容... 
注释内容... 
EOF

:<<! 
注释内容... 
注释内容... 
注释内容... 
!

2.2 变量

借助java程序对变量的理解:变量就是程序运行过程中其值可以发生改变的量

语法定义格式

# 1、语法:变量名=值
# 2、注意:等号左右两边不能有空格!!!
# 3、例如:
[root@master soft]# name="xiaohu"

变量的引用

[root@master soft]# address="安徽合肥"
[root@master soft]# echo $address
安徽合肥
 
注意:如果是打印百分比,建议使用${变量名}%
[root@master soft]# percent=90
[root@master soft]# echo ${percent}%
90%

删除变量

[root@master soft]# x=999
[root@master soft]# unset x
[root@master soft]# echo $x
 
[root@master soft]# 

变量的命名规范

# 变量名的命令应该见名知意,同时遵循如下规则
以字母或下划线开头,剩下的部分可以是:字母、数字、下划线,最好遵循下述规范:
    1.以字母开头
    2.使用中划线或者下划线做单词的连接  shujia-xiaohu  shujia_xiaohu  
    3.同类型的用数字区分  shujia1  shujia2
    4.对于文件名的命名最好在末尾加上拓展名  score_xiaohu.sh  score_shizhaoyang.sh
 
例如: shujia_test.tar.gz 
    5、不要带有空格、?、*等特殊字符
    6、不能使用bash中的关键字,例如if,for,while,do等
    7、不要和系统环境变量冲突

变量的使用场景

场景一:直接赋值
# 1. 显式赋值:变量名=变量值
[root@master soft]# name2="xiaohu"
[root@master soft]# echo $name2
xiaohu
[root@master soft]# name2="xiaohua"
[root@master soft]# echo $name2
xiaohua
场景二:脚本运行传值
从调用脚本时传入的位置参数获取变量值:./a1.sh a1 a2 a3
需要用到$n获取第n个位置参数值,超过10需要用${n},如下
$0 $1 $2 $3 $4 $5 $6 $7 $8 $9 ${10}
 
# 示例
[root@master soft]# cat a1.sh 
#!/bin/bash
echo ${0}
echo $1
echo $2
echo $3
echo $4
echo $5
echo $6
echo $7
echo $8
echo $9
echo ${10}
echo ${11}
echo ${12}
场景三:用户交互传值
何为交互,即输入、输出
 
# 一:read接收用户的输入,即从键盘读入变量值
read 变量名
read -p "提示信息: "  变量名
read -t 5 -p "提示信息: "  变量名  # -t指定秒数
read -n 2 变量名  # -n读取的字符个数
 
应用示例:vim name.sh
read -p "请输入你的姓名 " name
echo $name
 
# 二:输出
# echo命令
[root@master soft]# name="xiaohu"
[root@master soft]# age=18
[root@master soft]# echo -e "my name is\t$name\nmy age is\t$age"
my name is  xiaohu
my age is   19
 
还可以输出带颜色(了解即可)
# echo -e "\033[字背景颜色;文字颜色m字符串\033[0m"
# 例如
# echo -e "\033[47;30m I love shujia! \033[0m",其中47的位置代表背景色, 30的位置是代表字体颜色
echo -e "\033[31m 红色字 \033[0m"
echo -e "\033[34m 黄色字 \033[0m"
echo -e "\033[41;33m 红底黄字 \033[0m"
echo -e "\033[41;37m 红底白字 \033[0m"
# 需要使用参数-e,man  echo 可以知道-e:enable interpretation of backslash escapes。 


## 显示普通字符串 
echo "Hello World" 

## 显示转义字符 
echo "\"Hello World\"" 

## 显示变量 
name="zhangsan" 
echo "$name Hello World" 

## 显示换行 
echo -e "OK! \n"  
echo "Hello World" 

## 显示不换行 
echo -e "OK! \c" 
echo "Hello World" 

## 显示结果定向至文件 
echo "Hello World" > myfile 
## > 代表覆盖
# >> 追加写入

## 原样输出字符串 
echo '$name\"' 

## 显示命令执行结果 
echo `date`

预定变量

参数处理 参数说明
$# 传递到脚本的参数个数
$*/$@ 以一个单字符串显示所有向脚本传递的参数。
$$ 脚本运行的当前进程ID号
$! 后台运行的最后一个进程的ID号
$? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。
$0 执行的文件名
$*  所有的参数
$@  所有的参数
对于后面说的for循环而言,上面两个没有区别

$#  参数的个数
$$  当前进程的PID  # 此外,可以使用只读变量来获取父进程的PID:$PPID、获取执行脚本的用户ID:$UID
$?  上一个命令的返回值 0表示成功 
 
示例1:
[root@master soft]# chmod +x b.sh 
[root@master soft]# ./b.sh a1 a2 a3 a4 a5
a1 a2 a3 a4 a5
a1 a2 a3 a4 a5
5
18988
1
[root@master soft]# cat b.sh 
#!/bin/bash
echo $*
echo $@
echo $#
echo $$

2.3 常量

使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。
 
[root@master soft]# x=999
[root@master soft]# readonly x
[root@master soft]# x=111
-bash: x: 只读变量
如何删除readonly常量?

下载gdb

[root@master soft]# yum install gdb -y

删除

[root@master soft]# readonly x=123
[root@master soft]# cat << EOF | gdb
> attach $$
> call unbind_variable("x")
> detach
> EOF

2.4 数据类型

shell是一门解释型,弱类型,动态语言

概括地说,编程语言的划分方式有以下三种
 
# 1、编译型or解释型
 
# 2、强类型or弱类型
2.1 强类型语言: 数据类型不可以被忽略的语言
即变量的数据类型一旦被定义,那就不会再改变,除非进行强转。
在python中,例如:name = 'xiaohu',这个变量name在被赋值的那一刻,数据类型就被确定死了,是字符型,值为'xiaohu'。
 
2.2 弱类型语言:数据类型可以被忽略的语言
比如linux中的shell中定义一个变量,是随着调用方式的不同,数据类型可随意切换的那种,即shell对数据类型的概念没有那么强的要求
 
# 3、动态型or静态型
3.1 动态语言 :运行时才进行数据类型检查
即在变量赋值时,就确定了变量的数据类型,不用事先给变量指定数据类型
 
3.2 静态语言:需要事先给变量进行数据类型定义
 
所以综上所述,shell是一门解释型的弱类型动态语言

基本数据类型

数字类型
# int整型
定义:age=18
用于标识:年龄,等级,身份证号,qq号,个数
 
# float浮点型
定义:salary=30000.0
用于标识:工资,身高,体重
字符串类型
#在shell中,加了引号的字符就是字符串类型
 
定义:name='xiaohu' 
用于标识:描述性的内容,如姓名,性别,国籍,种族
 
# 注意1:字符串包含空格必须加引号
[root@master soft]# msg="hello xiaohu"
[root@master soft]# msg=hello xiaohu
bash: xiaohu: 未找到命令...
 
# 注意2:连续的字符不加引号包含也是可以的,但我们还是强烈建议加上引号,规范一些
[root@master soft]# msg=hello
[root@master soft]# echo $msg
hello
[root@master soft]# 
 
# 注意3:单引号与双引号是不同的
" "   弱引用,引号的特殊字符有意义
' '   强引用,引号内所有特殊字符都取消意义
[root@master soft]# name=“xiaohu”
[root@master soft]# echo "${name} is good"
xiaohu is good
[root@master soft]# echo '${name} is good'
${name} is good 
弱类型语言
[root@master soft]# x=10
[root@master soft]# y="3"
[root@master soft]# expr $x + $y
13
数组类型
# 1、什么是数组?
数组就是一系列元素的集合,一个数组内可以存放多个元素
 
# 2、为何要用数组?
我们可以用数组将多个元素汇总到一起,避免单独定义的麻烦

数组分为两种
• 普通数组:只能使用整数作为数组索引
• 关联数组【字典】:可以使用字符串作为数组索引,需要用declare -A声明

普通数组
=================声明普通数组=================
# 方式一:array=(元素1 元素2 元素3)
array=(xiaohu 18 male)
 
# 方式二:array=([key1]=value1 [key2]=value2 [key3]=value3)
array=([1]=111 [0]="two" [2]=333)
 
# 方式三:依次赋值
array_new[0]=111
array_new[1]=222
array_new[2]="third"
 
# 方式四:利用执行命令的结果设置数组元素:array=($(命令))  或者  array=(`命令`)
该方式会将命令的结果以空格为分隔符切成多个元素然后赋值给数组
[root@master soft]# ls /test
a.txt   b.txt
[root@master soft]# array3=(`ls /test`)
[root@master soft]# declare -a |grep array3
declare -a array3='([0]="a.txt" [1]="b.txt")'
 
# ps:查看声明过的数组
declare -a
 
=================访问普通数组=================
[root@master soft]# ip_array=(1.1.1.1 2.2.2.2 3.3.3.3)
 
# 正向索引
[root@master soft]# echo ${ip_array[0]}
1.1.1.1
[root@master soft]# echo ${ip_array[1]}
2.2.2.2
[root@master soft]# echo ${ip_array[2]}
3.3.3.3
[root@master soft]# 
 
# 负向索引
[root@master soft]# echo ${ip_array[-1]}
3.3.3.3
[root@master soft]# echo ${ip_array[-2]}
2.2.2.2
[root@master soft]# echo ${ip_array[-3]}
1.1.1.1
关联数组
=================声明关联数组=================
[root@master soft]# declare -A info
[root@master soft]# info["name"]="xiaohu"
[root@master soft]# info["age"]=18
[root@master soft]# info["gender"]="male"
[root@master soft]# 
[root@master soft]# declare -A |grep info
declare -A info='([gender]="male" [name]="xiaohu" [age]="18" )'
[root@master soft]# 
[root@master soft]# echo ${info[*]}
male xiaohu 18
[root@master soft]# 
[root@master soft]# echo ${info["name"]}
 
=================访问关联数组=================
declare -A info # 必须要先声明一下关联数组,才能进行赋值
[root@master soft]# info=(["name"]="xiaohu" ["age"]=18 ["gender"]="male")
[root@master soft]# echo ${info[0]}
xiaohu
[root@master soft]# echo ${info["age"]}
18
[root@master soft]# echo ${info["gender"]}
male
[root@master soft]# 

2.5 变量值相关常见操作

获取变量值的长度

[root@master soft]# echo ${#url}
15

# 已知变量msg='hello world!',请统计出变量中包含的字符数量
[root@master soft]# echo ${#msg}
12

切片

# ${paramter:offset:length}

[root@master soft]# msg="abcdef"
[root@master soft]# echo ${msg:3}  # 从3号索引开始,一直到最后
def
[root@master soft]# echo ${msg:3:2}  # 从3号索引开始,往后数2个字符
de
[root@master soft]# echo ${msg::3}  # 从0开始,往后数3个字符
abc

截断【截掉】

# =================》一、砍掉左边的字符《=================
# 1.1 简单使用 
[root@master soft]# url="www.shujia.com.cn"
[root@master soft]# echo ${url#www.}
shujia.com.cn
 
# 1.2 结合 #* 非贪婪,默认情况下*是非贪婪,尽可能地少“吃”字符
[root@master soft]# echo ${url#*w}
ww.shujia.com.cn
 
# 1.3 结合 ##* 贪婪,尽可能地多“吃”字符
[root@master soft]# echo ${url##*w}  # *会尽可能多地吃掉字符,一直匹配到最远的那个w才停下来
.shujia.com.cn
 
# =================》二、砍掉右边的字符《=================
# 1.1 简单使用
[root@master soft]# url="www.shujia.com.cn"
[root@master soft]# echo ${url%.cn}
www.shujia.com
 
# 1.2 结合%.*非贪婪
[root@master soft]# echo ${url%.*}
www.shujia.com
# 1.3 结合%%.*贪婪
[root@master soft]# echo ${url%%.*}
www

let操作

# (1) 变量的值
[root@master soft]# j=1
[root@master soft]# let ++j
[root@master soft]# echo $j
2
[root@master soft]# 

++ 表示的是自加1,
i++ 表示先赋值运算,再自加1
++i 表示先自加1,再做赋值运算

-- 表示的自减1
i-- 表示先赋值运算,再自减1
--i 表示先自减1,再做赋值运算
 
# (2) 表达式的值
[root@master soft]# unset i
[root@master soft]# unset j
[root@master soft]# 
[root@master soft]# i=1
[root@master soft]# j=1
[root@master soft]# 
[root@master soft]# let x=i++  # 先把i赋值给x,然后再++
[root@master soft]# let y=++j  # 先++j,然后再把j的结果赋值给y
[root@master soft]# echo $i
2
[root@master soft]# echo $j
2
[root@master soft]# echo $x
1
[root@master soft]# echo $y
2

三、运算符

3.1 算术运算符

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

算术运算符需要配合以下操作符使用

# 浮点运算
bc  
 
# 整数运算
expr
$(())
$[]
let
bc 计算工具( yum install bc -y )
[root@master soft]# res=`echo "1+1" | bc`
[root@master soft]# echo $res
2
 
[root@master soft]# res=`echo "10%3" | bc`
[root@master soft]# echo $res
1
 
[root@master soft]# res=`echo "1.2+1.3" | bc`
[root@master soft]# echo $res
2.5
 
[root@master soft]# res=`echo "5.0+3.0" | bc`
[root@master soft]# echo $res
8.0
expr
[root@master soft]# res=`expr 5 / 3`  # 不支持浮点计算
[root@master soft]# echo $res
1
 
[root@master soft]# res=`expr 1+1`  # 注意:要有空格
[root@master soft]# echo $res
1+1
[root@master soft]# res=`expr 1 + 1`
[root@master soft]# echo $res
2
 
如果是乘法,如需要转义\*
[root@master soft]# echo `expr 3 \* 10`
30
[root@master soft]# 
$[]
[root@master soft]# echo $[$num1+$num2]  # 等同于 echo $[num1+num2]
333
[root@master soft]# echo $[1.3+3.1] 
-bash: 1.3+3.1: 语法错误: 无效的算术运算符 (错误符号是 ".3+3.1")
let赋值 只支持赋值操作
[root@master soft]# let res=1+1
[root@master soft]# echo $res
2
[root@master soft]# 
[root@master soft]# let res=50/5
[root@master soft]# echo $res
10
[root@master soft]# let c=1.3*3
-bash: let: c=1.3*3: 语法错误: 无效的算术运算符 (错误符号是 ".3*3"

3.2 文件测试运算符

#!/bin/bash 

file="/var/node/test.sh" 
if [ -r $file ] 
then
	echo "文件可读" 
else
	echo "文件不可读" 
fi

if [ -w $file ] 
then
	echo "文件可写" 
else
	echo "文件不可写" 
fi

if [ -x $file ] 
then
	echo "文件可执行" 
else
	echo "文件不可执行" 
fi

if [ -f $file ] 
then
	echo "文件为普通文件" 
else
	echo "文件为特殊文件" 
fi

if [ -d $file ] 
then
	echo "文件是个目录" 
else
	echo "文件不是个目录" 
fi

if [ -s $file ] 
then
	echo "文件不为空" 
else
echo "文件为空" 
fi

3.3 字符串测试运算符

# == 判断两个字符串是否相等
[root@master soft]# [ "aaa" == "aaa" ];echo $?
0

# != 判断两个字符串是否不相等
[root@master soft]# [ "aaa" != "aaa" ];echo $?
1

# -z 判断字符串长度是否为0
[root@master soft]# ip=""
[root@master soft]# [ -z "$ip" ];echo $?  # 注意引号
0
[root@master soft]# ip='1.1.1.1'
[root@master soft]# [ -z "$ip" ];echo $?
1

# -n 判断字符串长度是否不为0
[root@master soft]# [ -n "$ip" ];echo $?  # 注意加引号
1

3.4 数值测试符(比较运算符)

运算符 说明 举例
-eq 检测两个数是否相等,相等返回true [$a -eq $b ]返回 false。
-ne 检测两个数是否不相等,不相等返回true [$a -ne $b ]返回 true。
-gt 检测左边的数是否大于右边的,如果是,返回true [$a -gt $b ]返回 false.
-lt 检测左边的数是否小于右边的,如果是,返回true [$a -It $b ]返回 true。
-ge 检测左边的数是否大于等于右边的,如果是,返回true [$a -ge $b ]返回 false。
-le 检测左边的数是否小于等于右边的,如果是,返回true [$a -le $b ]返回 true.
[root@master soft]# [ 10 -eq 10 ];echo $?
0
[root@master soft]#  [ 10 -eq 10 -a 10 \> 3 ];echo $?
0

3.5 关系运算符

需要结合(())进行使用

[root@master soft]# x=100
 
[root@master soft]# (($x>10))
[root@master soft]# echo $?
0
 
[root@master soft]# (($x < 10));echo $?
1
 
[root@master soft]# (($x == 100));echo $?
0
 
[root@master soft]# (($x != 100));echo $?
1
 
[root@master soft]# (($x != 100)) && (("xiaohu" == "xiaohu"))
[root@master soft]# echo $?
1
 
[root@master soft]# (($x != 100)) || (("xiaohu" == "xiaohu"))
[root@master soft]# echo $?
0
 
[root@master soft]# (($x != 100 && "xiaohu" == "xiaohu"));echo $?
1
 
[root@master soft]# (($x != 100 || "xiaohu" == "xiaohu"));echo $?
0

3.6 赋值运算符

[root@master soft]# x=10
[root@master soft]# ((x%3))
[root@master soft]# echo $x
10
[root@master soft]# ((x%=3))
[root@master soft]# echo $x
1

# 相关括号:
格式1: test 条件表达式
格式2: [ 条件表达式 ] -eq -lt -gt -le -ge -ne
格式3: (()) 数值比较,运算   += -= %= == != < > <= >=  && ||
 
格式4: [[ 条件表达式 ]],支持正则  =~

# 双中括号的正则
[root@master soft]# [[ "$USER" ==  "root" ]];echo $?  # 注意内层[]中包含的内容必须左右两侧加空格
0
[root@master soft]# [[ "$USER" ==  "root" ]];echo $?  # 一个等号也行两个等号也可以额
0
 
# 此外[[]]内部是可以使用正则的,注意:正则表达式不要加引号
[root@master soft]# [[ "$USER" =~ ^wyh$ ]];echo $?  # 正则表达式不要加引号
1
[root@master soft]# [[ "$USER" =~ ^root$ ]];echo $?  # 正则表达式不要加引号
0
[root@master soft]# [[ ! "$USER" =~ t$ ]] && echo 此时不是管理员登录 || echo 是管理员登录
是管理员登录
 
[root@master soft]# num1=123
[root@master soft]# [[ "$num1" =~ ^[0-9]+$ ]];echo $?  # 判断是否是数字
0
[root@master soft]# [[ "$num1" =~ ^[0-9]+$ ]] && echo "是数字"
是数字
 
[root@master soft]# num2=abc123de
[root@master soft]# [[ "$num2" =~ ^[0-9]+$ ]];echo $?
1
[root@master soft]# [[ "$num2" =~ ^[0-9]+$ ]] || echo "num2不是数字"
num2不是数字

3.7 布尔运算符

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

[ $a -It 20 -a $b -gt 100 ];echo $?

3.8 逻辑运算符

运算符 说明 举例
&& 逻辑的AND [[$a -It 100 && $b-gt 100 ]]返回 false
|| 逻辑的OR [[$a -It 100 || $b -gt 100 ]]返回 true

四、流程控制

4.1 if语句

if 条件
then
    要执行的命令1
    要执行的命令2
    要执行的命令3
    ...
fi

if 条件;then
    要执行的命令1
    要执行的命令2
    要执行的命令3
    ...
fi
 
# 上述语法可以用一行代码代替
[ 条件信息 ] && xxx

双分支

if 条件;then
    要执行的命令1
    要执行的命令2
    要执行的命令3
    ...
else
    要执行的命令1
    要执行的命令2
    要执行的命令3
    ...
fi
 
# 上述语法可以用一行代码代替
[ 条件信息 ] && xxx || xxxx

举例:

#!/bin/bash
username='xiaohu'
password='123'
read -p 'user: ' name 
read -p 'passwd: ' passwd
 
if [ $name == $username -a $passwd == $password ]
then
    echo 'login successful'
else
    echo 'username or password err'
fi

多分支

if 条件;then
    要执行的命令1
    要执行的命令2
    要执行的命令3
    ...
elif 条件;then
    要执行的命令1
    要执行的命令2
    要执行的命令3
    ...
elif 条件;then
    要执行的命令1
    要执行的命令2
    要执行的命令3
    ...
...
else
    要执行的命令1
    要执行的命令2
    要执行的命令3
    ...
fi

举例:

======================版本1======================
#!/bin/bash
age=87
read -p 'num: ' n
 
if [ $n -eq $age ];then
    echo 'you get it'
elif [ $n -gt $age ];then
    echo 'too big'
elif [ $n -lt $age ];then
    echo 'too small'
fi
 
======================版本2======================
#!/bin/bash
 
read -p ">>> " num
 
[[ ! $num =~ ^[0-9]+$ ]] && echo "请输入数字" && exit
 
if [ $num -gt 18 ];then
    echo "too big"
elif [ $num -lt 18 ];then
    echo "too small"
else
    echo "you got it"
fi

4.2 case选择

case 变量 in
模式1)
    命令序列1
    ;;
模式2)
    命令序列2
    ;;
模式3)
    命令序列3
    ;;
*)
    无匹配后命令序列
esac

注意:

case语句只支持shell通配符,例如:*表示任意字符串,?表示任意字符,中括号表示字符集如[a-z]表示一个小写字母
如果要处理正则表达式则需要使用用if [[ 字符串 =~ "正则" ]]这种形式

案例:

#!/bin/bash
read -p "username: " -t 5 username
echo
if [ -z $username ];then
    username="default"
fi
 
case $username in
root)
    echo "管理员用户"
    ;;
xiaohu)
    echo "普通用户"
    ;;
default)
    echo "默认用户"
    ;;
*)
    echo "其他用户"
esac

4.3 while循环

# 一、while语句结构:条件为真时,执行循环体代码
while 条件
do
    循环体
done
 
# 二、until语法结构:条件为假时,一直执行循环体代码,直到条件变为真
until 条件
do
    循环体
done

案例:

[root@master soft]# cat a.sh 
#!/bin/bash
 
x=0
while (($x<3))
do
    echo $x  # 0,1,2
    let x++
done
 
echo "================"
 
y=0
until (($y==3))
do
    echo $y # 0,1,2
    let y++
done
 
[root@master soft]# ./a.sh 
0
1
2
================
0
1
2
[root@master soft]# 

流程控制语句:

continue:默认退出本次循环

break:默认退出本层循环

4.4 for循环

# Shell风格语法
for 变量名 [ in 取值列表 ]
do
    循环体
done
 
# C语言风格语法
for ((初值;条件;步长))
do  
    循环体
done

案例1 shell风格

for i in {1..10}
do
    echo $i
done

案例2:C语言风格

for ((i=1;${i}<=10;i++))
do
    echo $i
done

4.5 扩展使用select

select var in ...
do
    ...
    break
done

案例:

[root@master soft]#  cat select2.sh 
#!/bin/bash
PS3='choose one: ' # select默认使用PS3变量的值做提示符
echo

select var in $1 $2 $3 $4
do
    echo
    echo "your choose is $var"
    echo "OK"
    echo
    break # 跳出select,否则是死循环
done

 
[root@master soft]# 
[root@master soft]# ./select2.sh 苹果 梨 蔬菜 香蕉 
 
1) 苹果
2) 梨
3) 蔬菜
4) 香蕉
5) 茄子
choose one: 1

your choose is 苹果
OK

五、函数

函数的定义

#语法:
[ function ] funname [()]
{
    命令1;
    命令2;
    命令3;
    ...
    [return int;]
}
 
# 示例1:完整写法
function 函数名() {
    函数要实现的功能代码
}
 
# 示例2:省略关键字(),注意此时不能省略关键字function
function 函数名 {
    函数要实现的功能代码
}
 
# 示例3:省略关键字function
函数名() {
    函数要实现的功能代码
}

函数的调用

# 语法:
函数名  # 无参调用
函数名 参数1 参数2  # 有参调用
 
# 示例
function test1(){
    echo "执行第一个函数"
}
 
function test2 {
    echo "执行第二个函数"
}
 
test3(){
    echo "执行第三个函数"
}
 
# 调用函数:直接引用函数名字即调用函数,会触发函数内代码的运行
test1
test2
test3

函数调用时传递参数

# 调用函数test1,在其后以空格为分隔符依次罗列参数 
test1 111 222 333 444 555

函数内部使用$n来取值

[root@master soft]# cat b.sh 
function test1(){
  echo "...start..."
  echo $1
  echo $2
  echo $3
  echo "...end..."
}
 
test1 111 222 333 444 555  # 为函数体传参
 
[root@master soft]# ./b.sh 
...start...
111
222
333
...end...
注意1:不要和脚本外部传参搞混淆
[root@master soft]# cat b.sh 
function test1(){
  echo "...start..."
  echo "这是函数内:$1"
  echo "这是函数内:$2"
  echo "这是函数内:$3"
  echo "...end..."
}
 
# test1 111 222 333 444 555
test1
 
echo "这是脚本级的参数$1"
echo "这是脚本级的参数$2"
echo "这是脚本级的参数$3"
 
[root@master soft]# 
[root@master soft]# 
[root@master soft]# ./b.sh xxx yyy zzz mmm nnn
...start...
这是函数内:
这是函数内:
这是函数内:
...end...
这是脚本级的参数xxx
这是脚本级的参数yyy
这是脚本级的参数zzz
[root@master soft]# 
注意2:$*和$@
# 1、当$*和$@没有被引号引用起来的时候,它们确实没有什么区别,都会把位置参数当成一个个体。
 
# 2、"$*" 会把所有位置参数当成一个整体(或者说当成一个单词),如果没有位置参数,则"$*"为空,如果有两个位置参数并且分隔符为空格时,"$*"相当于"$1 $2"
 
# 3、"$@"  会把所有位置参数当成一个单独的字段,如果没有位置参数,则"$@"展开为空(不是空字符串,而是空列表),如果存在一个位置参数,则"$@"相当于"$1",如果有两个参数,则"$@"相当于"$1"  "$2"等等
[root@master soft]# cat b.sh 
echo "=======函数test1==========="
function test1(){
    echo "$*"  # 111 222 333 444 555
    echo "$@"  # 111 222 333 444 555
    echo $#    # 5
    echo $$    # 87380
    echo $?    # 0
}
 
test1 111 222 333 444 555
 
echo "=======函数test2==========="
function test2(){
    for i in "$*"  # 注意:$*不加引号结果与$@一模一样
    do
        echo $i
    done
}
 
test2 111 222 333 "444 555"  # 注意"444 555"被引号引成了一个参数
# 运行结果为:111 222 333 444 555
 
echo "=======函数test3==========="
function test3(){
    for i in "$@"  # 注意:$*不加引号结果与$@一模一样
    do
        echo $i
    done
}
 
test3 111 222 333 "444 555"  # 注意"444 555"被引号引成了一个参数
# 运行结果为:
# 111
# 222
# 333
# 444 555

函数返回值的问题

1. 当函数体中没有return,那么最后一条命令(运行成功为0,其他不为0)作为返回值

[root@master soft]# cat b.sh 
function test1(){
  echo 111
  echo 222
  echo 333
  xxx  # 运行该命令出错
}
 
test1
echo $?
[root@master soft]# ./b.sh 
111
222
333
./b.sh:行5: xxx: 未找到命令
127

2. 有return的时候,注意,return后面只能跟整数并且【0-255】,在shell中0表示true, 1表示false

[root@master soft]# cat b.sh 
function test1(){
  echo 111
  echo 222
  echo 333
  return 0
}
 
test1
echo $?  # 用$?获取函数的返回值
[root@master soft]# ./b.sh 
111
222
333
0

局部变量

使用local关键字定义在函数内部的变量叫做局部变量,只能在函数内部使用

[root@master soft]# cat hello.sh 
#!/bin/bash
 
# 定义函数
function test(){
    local x=111
    echo "函数内访问x:$x"
}
 
# 调用函数
test
 
echo "在函数外即全局访问x:$x"  # 无法访问函数内的局部变量

全局变量

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

[root@master soft]# cat hello.sh 
#!/bin/bash
x=2222
 
function test(){
    echo "函数内访问x:$x"
}
test
echo "在函数外即全局访问x:$x" 

注意1:在函数内定义的变量,如果没有用local声明,那么默认也是全局变量

[root@master soft]# cat hello.sh 
#!/bin/bash
function test(){
  x=2222  # 全局变量
}
test
echo "在函数外即全局访问x:$x"  
 
[root@master soft]# ./hello.sh 
在函数外即全局访问x:2222

注意2:每执行一个解释器,都会开启一个解释的shell进程,每个shell进程都有自己的作用域彼此互不干扰

[root@master soft]# x=111  # 该变量仅仅只在当前shell进程中有效,对新的shell进程无影响
[root@master soft]# echo $x
111
[root@master soft]# bash  # 执行bash解释器,则开启一个新的进程,或者干脆直接打开一个新的终端
[root@master soft]# echo $x
 
[root@master soft]# 

注意3:

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

[root@master soft]# echo $x
 
[root@master soft]# cat hello.sh 
#!/bin/bash
function test(){
  x=2222  # 全局变量
}
test
 
[root@master soft]# source hello.sh  # 在当前shell进程中执行,产生一个全局变量x
[root@master soft]# echo $x  # 在当前shell进程中访问全局变量x,可以看到
2222
[root@master soft]# 
[root@master soft]# 
[root@master soft]# cat aaa.sh 
#!/bin/bash
echo $x
 
[root@master soft]# source aaa.sh # 在当前shell进程中访问全局变量x,同样可以看到
2222

注意4:全局变量只在当前Shell进程中有效,对其它Shell进程和子进程都可以使用

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

[root@master soft]# export y=333  # 爷爷
[root@master soft]# bash  # 爸爸
[root@master soft]# echo $y
333
[root@master soft]# bash  # 孙子
[root@master soft]# echo $y
333
 
ps:通过exit命令可以一层一层地退出 Shell。

# 命令
set:显示所有变量
env:环境变量

注意5:环境变量只能向下传递不能向上传递

注意6:两个没有父子关系的shell进程不能传递环境变量

我们一直强调的是环境变量在 Shell 子进程中有效,并没有说它在所有的 Shell 进程中都有效;如果你通过终端创建了一个新的 Shell 窗口,那它就不是当前 Shell 的子进程,环境变量对这个新的 Shell 进程仍然是无效的。

注意7:环境变量也是临时的

[root@master soft]# ps -ef
root     123436  0.0  0.1 116356  2960 pts/0    Ss   21:52   0:00 -bash
root     123492  0.0  0.1 116472  2932 pts/0    S    21:54   0:00 bash
root     123520  0.0  0.1 116440  2988 pts/0    S    21:54   0:00 bash
 
注意:
# -开头的bash代表是在登录终端登录的顶级shell进程
# 非-开头的bash代表的是子shell进程
# 一旦退出了在终端登录的顶级shell,那么该终端下开启的所有子shell都会被回收,export设置的环境变量随即消失

练习题(晚上离开教室之前交)

# 1、脚本案例: 编写小脚本, 可以实现2位数加减乘除运算
 
# 2、脚本案例: 编写判断输入参数是否是整数信息脚本

# 3、如何利用脚本计算1+2+3+4..10总和数值

# 4、根据考试成绩输出对应的礼物,90分以上爸爸给买电脑,80分以上爸爸给买手机, 60分以上爸爸请吃一顿大餐,60分以下爸爸给买学习资料。
	要求:该题使用多重if完成

# 5、输入一批整数,使用循环求出最大值与最小值,输入0时结束。

# 6、给20块钱买可乐,每瓶可乐3块钱,喝完之后退瓶子可以换回1块钱,问最多可以喝到多少瓶可乐。

# 7、求九九乘法表

标签:Shell,语言,soft,sh,echo,shell,master,程序设计,root
From: https://www.cnblogs.com/03270925yhd/p/18671425

相关文章

  • C语言练习(6)
    按要求编写程序。该题的要求是:有1000元,想存5年,可按以下5种办法存:①一次存5年期;②先存2年期,到期后将本息再存3年期;③先存3年期,到期后将本息再存2年期;④存1年期,到期后将本息再存1年期,连续存5次;⑤存活期存款,活期利息每季度结算一次。分别给出了不同存期的利率,要求计算并比......
  • 嵌入Embedding-计算理解语言的钥匙
    定义:将人类语言与数字建立联系的强大方法嵌入技术的演变:Wod2VecCBOW(ContinuousBagofWords):根据上下文词汇预测目标词汇(情感分析、文本分类、词相似性)Skip-Gram:根据目标单词预测周围单词在训练Word2Vec模型时,包含词典和词向量模型的训练词典的构建是训练过程的一部分。具......
  • window10 powsershell 安装ubuntu22.04
     参考  https://learn.microsoft.com/en-us/windows/wsl/basic-commands#install      https://learn.microsoft.com/en-us/windows/wsl/install ListavailableLinuxdistributions  wsl--list--online以下是可安装的有效分发的列表。使默认分发用......
  • Power Shell 7 批量给pdf添加页码
    #批量给pdf添加页码#导入必要的程序集Add-Type-AssemblyNameSystem.DrawingAdd-Type-AssemblyNameSystem.Windows.Forms#加载iTextSharp.dll#假设iTextSharp.dll位于C:\path\to\iTextSharp目录$iTextSharpPath="C:\Users\Administrator\source\repos\ConsoleAp......
  • 【C语言】_指针面试题
    目录1.示例12.示例23.示例34.示例45.示例56.示例67.示例71.示例1#include<stdio.h>intmain(){ inta[5]={1,2,3,4,5}; int*ptr=(int*)(&a+1); printf("%d,%d",*(a+1),*(ptr-1)); return0;}运行结果如下:分析:(1)a=&a[0],a+......
  • 【C语言】_字符函数
    目录1.字符分类函数1.1相关函数及其功能1.2使用示例2.字符转换函数2.1相关函数及其功能2.2使用示例1.字符分类函数1.1相关函数及其功能函数其参数符合下列条件则返回真iscntrl任何控制字符isspace空白字符:空格'';换页'\f';换行:'\n';回车:'\r';制表符:'\t';垂直制......
  • 嵌入式基础 C语言预科准备
    一、C语言的基本结构(1)、第一个C程序:helloworld配置部署好vsCode之后,就可以直接在上面写代码了,新建一个新的C程序文件,向屏幕输出一串字符“HelloWorld!”下面,从整体上来分析一下这个最简单的C语言程序,将这个最简程序的各个部分剖析清楚,明白我们写下的每一个字符的具体......
  • 重回C语言之老兵重装上阵(五)C语言作用域规则
    C语言中的作用域(Scope)定义了一个变量或函数名可以被访问的范围。作用域规则帮助我们了解变量和函数在代码中有效的区域。C语言中的作用域主要分为两类:变量作用域和函数作用域。1.作用域的类型C语言的作用域主要可以分为以下几种类型:块作用域(BlockScope):在代码块......
  • 重回C语言之老兵重装上阵(四)vscode配置C语言多文件编译运行
    眨眼睛第一次使用vscode写C语言多文件项目发现插件默认配置只能单文件编译,勃然大怒,于是怒了一下首先明确这一件事,只要有环境C文件就能通过命令行编译,插件只是简化输入命令行的工具,所以我们只需修改插件编译的配置即可一.首先我们需要下载对应插件点击跳转具体插件安装......
  • EpiCoder:基于特征树的代码生成框架——提升代码大语言模型的多样性与复杂性
    引言:代码生成领域的挑战与机遇近年来,大语言模型(LLMs)在代码理解和生成领域展现了巨大的潜力。通过对大规模代码数据进行预训练,这些模型能够生成功能强大的代码片段。然而,现有的代码生成方法主要依赖于代码片段作为种子数据,这些片段在功能性和结构上存在局限性,难以捕捉真实编......