Shell脚本编程的实用技巧和最佳实践

news2025/1/7 13:12:47

1. Bash的变量

shell中变量的设置规则

  • 变量名称可以由字母、数字和下划线组成,但是不能以数字开头

  • 在Bash中,变量的默认类型都是字符串型,如果要进行数值运算,则必须指定变量类型为数值型

  • 变量使用等号连接,等号左右两侧不能有空格

  • 如果变量的值有空格(Linux中空格代表分割),需要使用单引号或双引号包括

  • 在变量的值中,可以使用\转义符

  • 如果需要增加变量的值,那么可以进行变量值的叠加。不过变量需要用双引号包含$变量名或者使用${变量名}

  • 如果是把命令的结果作为变量值赋予变量,则需要使用反引号或者$()包含变量

  • 建议环境变量使用大写

变量分类

  • 用户自定义变量
  • 环境变量:这种变量中主要保存的是和系统操作环境相关的数据
  • 位置参数变量:这种变量主要是用来向脚本当中传递参数或数据的,变量名不能自定义,变量作用是固定的
  • 预定义变量:是Bash中已经定义好的变量,变量名不能自定义,变量作用也是固定的

1.1 用户自定义变量(本地变量)

  • 变量定义:aa=123 (注意等号左右不能有空格)

  • 变量叠加:两种方式aa="$aa"456或者aa=${aa}789

  • 变量查看:set

  • 变量删除:unset name

可以看到这四种变量从上到下变量是越来越严格的。

1.2 环境变量

1.2.1 环境变量和用户自定义变量的区别

用户自定义变量只在当前的Shell中生效,

而环境变量会在当前Shell和这个Shell的所有子Shell当中生效,如果把环境变量写入相应的配置文件,那么这个环境变量就会在所有的Shell中生效

环境变量和用户自定义变量的区别在于 作用的范围不同

1.2.2 如何设置环境变量

export 变量名=变量值:声明环境变量

env:查询变量

unset 变量名:删除变量

$变量名:调用变量

什么是父shell和子shell?

直接进入Linux是bash环境,如果再次输入bash即进入shell的子shell中,可以使用pstree命令来查看shell的结构,最后使用exit命令即可退出子shell

可以使用set或者env命令来查看系统中的变量

1.2.3 系统常见的环境变量
PATH 是系统查找命令的路径

在ubuntu中输入$PATH命令,输出为

-bash: /home/ubuntu/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games: No such file or directory

从这里也可以看出来上次得到的 在bin目录中存放的是常用的可执行文件

在Linux系统中一切都是文件,我们输入的命令都是文件,那么为什么执行shell脚本的时候需要添加路径,而执行正常命令的时候却不要呢?

因为正常命令的路径已经存放在PATH当中去了,而Linux在执行命令时会首先在PATH中搜索,所以执行正常命令时不用添加路径

当然也可以脚本存放在PATH中,这样就可以不用输入路径就可以执行,不过通常不会直接把这样的脚本直接添加到PATH中,因为这样可能会对原系统改动

如果想要直接运行脚本,而不用每次添加路径,更为保险的做法是利用变量叠加添加脚本所在路径,

PATH="$PATH":/root 通过这条命令,就把root目录存放在PATH中了,可以再次使用$PATH命令查看是否已经添加了/root目录,不过这只是临时生效,想要永远生效需要写入配置文件

PS1 定义系统提示符的命令

1.2.4. 环境变量配置文件

见环境变量配置文件

1.3 位置参数变量

其实就相当于编程中的函数传递参数

位置参数变量作用
$n | n为数字,$0代表命令本身,$1-$9表示接受第一个到第九个的参数,十以上的参数需要用大括号包含,如${10}
$* | 这个变量代表命令行所有的参数,$*把所有的参数看成一个整体
$@ | 这个变量也代表命令行中的所有参数,不过$@把每个参数区分对待
$#这个变量代表命令行中所有参数的个数

下面是一个例子

#!/bin/bash
# 位置参数的功能

echo "A total of $# parameters"
#使用$#代表所有参数的个数
echo "The parameters is : $*"
#使用$*代表所有的参数
echo "The parameters is : $@"
#使用$@也代表所有的参数

./Loca.sh 12 34 56

输出为

A total of 3 parameters
The parameters is : 12 34 56
The parameters is : 12 34 56

1.4 预定义变量

预定义变量作用
$?最后一次执行的命令(上一条命令)的返回状态。如果这个命令的值为0,证明上一个命令正确执行,如果这个变量的值为非0(具体是哪个数,由命令自己决定),则证明上一个命令执行不正确
$$当前进程的进程号(PID)
$!后台运行的最后一个进程的进程号(PID)

注: $?可以判断上一条命令是否正确

1.5 使用read接受键盘输入

read [选项] [变量名]

选项:

-p 提示信息 在等待read输入时,输出提示信息

-t 秒数 read可以一直等待用户输入,使用此选项可以指定等待时间

-n 字符数 read命令只接受指定的字符数,就会执行

-s: 隐藏输入的数据,

通过下面这个例子来体会

#!/bin/bash

read -t 30 -p "Please input your name: " name  #在shell脚本中,一定注意空格的使用,此处要添加空格
# -p  提示“请输入姓名”并等待30秒,把用户的输入保存入变量name中
echo "Name is $name"
echo -e "\n"

read -s -t 30 -p "Please input your age(using hidden mode): " age
# 年龄是隐私,所以我们用-s选项隐藏输入
echo "\t"
echo "Age is $age"
echo -e "\n"

read -n 1 -t 30 -p "Please select your gender[M/F]: " gender
#使用"-n 1"选项只接收一个输入字符就会执行(不用输入回车会直接执行)
echo -e "\n"
echo "Sex is $gender"

2. Bash运算符

2.1 数值运算与运算符

方法一 declare声明变量类型

语法:declare [+-][选项] 变量名

选项:

 - 给变量设定类型属性

 + 取消变量的类型属性

 -i 将变量声明为整数型(integer)

 -x 将变量声明为环境变量

 -p 显示指定变量的被声明类型

#!/bin/bash
# 数值运算

aa=11
bb=22
declare -i cc=$aa+$bb
echo $cc
方法二 expr或let数值运算工具
#!/bin/bash
# 使用expr工具进行数值运算

aa=11
bb=22
dd=$(expr $aa + $bb)  #注意"+"左右两边有空格
echo $dd
方法三 $((运算式))或者$[运算式]

ff=$(($aa + $bb)) 注意在$aa$bb两边加上空格

2.2 变量测试和内容替换

对于两个变量x和y,测试y的值然后对x赋值(其实可以通过if实现同样的功能)

由于那个表格过于复杂,所以就不详细记录了,形如下面两个

x=${y-新值}

x=${y:-新值} 之类的语法,就是实现变量测试的功能

3. 条件判断(Conditionals)

3.1 按照文件类型进行判断

测试选项作用
-d 文件判断该文件是否存在,并且是否为目录文件(是目录为真)
-e 文件判断该文件是否存在(存在为真)
-f 文件判断该文件是否存在,并且是否为普通文件(是普通文件为真)

两种判断格式

使用test命令

一个例子 test -e /root/install.log 判断/root下是否有install.log

使用中括号 []

[ -e /root/install.log ] 请注意命令两边距离中括号有空格

如何查看判断结果? ->使用echo $?命令

但是这样是不是稍微显得有一些麻烦,如何解决这个问题?

[ -d /root ] && echo "yes" || echo "no" 第一条命令如果正确执行,则打印 yes,否则打印 no

3.2 按照文件权限进行判断

测试选项作用
-r判断文件是否存在,并且是否该文件拥有读权限
-w判断该文件是否存在,并且该文件是否拥有写权限
-x判断该文件是否存在,并且是否该文件拥有执行权限

3.3 两个文件之间进行比较

测试选项作用
文件1 -nt 文件2判断文件1的修改时间是否比文件2的新
文件1 -ot 文件2判断文件1的修改时间是否比文件2的旧
文件1 -ef 文件2判断文件1和文件2的Inode号是否一致,可以理解为两个文件是否为同一个文件

3.4 两个整数之间的比较

测试选项作用
整数1 -eq 整数2判断整数1与整数2是否相等
整数1 -ne 整数2判断两个整数是否不相等
整数1 -gt 整数2整数1是否大于整数2
整数1 -lt 整数2小于
整数1 -ge 整数2大于等于
整数1 -le 整数2小于等于

一个例子 [ 22 -gt 23 ] && echo yes || echo no

表示如果22>23则返回yes否则返回no

结果是no

3.5 字符串的判断

测试选项作用
-z 字符串判断字符串是否为空 (为空返回为真)
-n 字符串判断字符串是否为非空(非空返回真)
字符串1==字符串2判断字符串1是否和字符串2相等(相等返回为真)
字符串1!=字符串2判断字符串2是否和字符串2不相等(不相等返回为真)

一个例子[ -z "$name" ] && echo yes || echo no

如果name没有赋值,返回为yes,如果为name赋值,则返回no

3.6 多重条件判断

测试选项作用
判断1 -a 判断2逻辑与,判断1与判断2都成立,最终结果为真
判断1 -o 判断2逻辑或,判断1与判断2有一个成立,返回结果为真
! 判断逻辑非

输入命令的时候 注意空格

aa=11
[ -n "$aa" -a "$aa" -gt 23 ] && echo yes || echo no
# -n "$aa" 判断变量aa的是否有值,同时判断变量aa是否大于23
# 因为变量aa的值不大于23,所以第一个判断为真,但是逻辑与要求均为真,所以最后返回结果为假

4. 流程控制(Loops)

4.1 if语句

1. 单分支if条件语句
if [ 条件判断式 ] ; then

	程序

fi

或者

if [ 条件判断 ]
	then
		程序
fi

注意:

  • 以if开头,以fi结尾,和其他语言不同

  • [ 条件判断式 ] 就是使用test命令判断,所以中括号和条件判断式之间必须有空格

  • then后面跟符合条件之后执行的程序,可以放在[]之后,用;分割,也可以换行写入,就不需要;

例子:判断分区使用率

#!/bin/bash

rate=$(df -h | grep "/dev/vda2" | awk '{print $5}' | cut -d "%" -f1)
# 把根分区使用率作为变量值赋予变量rate

if [ $rate -ge 80 ]
	then 
		echo "Warning! /dev/vda2 is full!!! "
fi
2. 双分支if条件语句
if [ 条件判断式 ]
	then
		条件成立时,执行的程序
	else
		条件不成立时,执行的另一个程序
fi
3. 多分支if 语句
if [ 条件判断式1 ]
	then
		当判断1成立时,执行程序1
	elif [ 条件判断式2 ]
		then
			当条件判断式2成立时,执行程序2
	elif ...
	else
		当所有条件都不成立,最后执行此程序
fi

一个例子,体会多分支语句

#!/bin/bash
#判断用户输入的是什么文件
# 通过这个脚本体会多分支语句

read -p "Please input a filename: " file
#接受键盘的输入,并赋予变量file

if [ -z "$file" ]
#判断file是否为空
	then 
		echo "Error, please input a filename"
		exit 1
elif [ ! -e "$file" ]
#判断file的值是否存在
	then 
		echo "Your input is not a file!"
		exit 2
elif [ -f "$file" ]
#判断file的值是否为普通文件
	then 
		echo "$file is a regulare file"
elif [ -d "$file" ]
#判断file的值是否为目录文件
	then 
		echo "$file is a directory"
	else
		echo "$file is an other file"
fi

4.2 case语句 多分支判断语句

case语句和if...elif...else语句一样都是多分支条件语句,不过和if多分支条件语句不同的是,case语句只能判断一种条件关系,而if语句可以判断多种条件关系

case $变量名 in
	"值1")
	如果变量的值等于值1,则执行程序1
	;;
	"值2")
	如果变量的值等于值2,则执行程序2
	;;
	*)
	如果变量的值都不是以上的值,则执行此程序
	;;
esac

一个例子

#!/bin/bash
#判断用户的输入

read -p "Please choose yes/no: " -t 30 cho
case $cho in 
	"yes")
		echo "Your choose is yes!"
		;;
	"no")
		echo "Your choose is no!"
		;;
	*)
		echo "Your choose is error!"
		;;
esac

不得不说,这语法太奇怪了...

4.3 for循环

语法1
for 变量 in 值1 值2 值3...
	do
		程序
	done

一个例子

#!/bin/bash
#打印时间
for time in morning noon afternoon night
	do 
		echo "$time"
	done

还有一个例子

#!/bin/bash

cd /root/sh/
ls *.sh > ls.log

y=1
for i in $(cat ls.log)
	do
		echo $y
		y=$(( $y+1))
	done
语法2
for (( 初始值;循环控制条件;变量变化 ))
	do
		程序
	done

一个例子

#!/bin/bash
#从1加到100

s=0
for ((i=1;i<=100;i=i+1))
do
	s=$(( $s+$i ))
done
echo "The sum of 1+2+3+...100 is: $s"

这里面有一个例子 ->批量添加指定数量的用户,不过有些复杂,和我的目的无关了,就不写了

4.4 while循环与until循环

while循环

while循环是不定循环,也称作条件循环,只要条件判断式成立,循环就会一直继续,直到条件判断式不成立,循环才会停止。这就和for的固定循环不太一样

语法

while [ 条件判断式 ]
	do
		程序
	done

一个简单的例子

#!/bin/bash

i=1
s=0
while [ $i -le 100 ]
#如果变量i的值小于等于100,则执行循环
do
		s=$(( $s+$i ))
		i=$(( $i+1 ))
done
echo "The sum is: $s"
until循环

until循环,和while循环相反,until循环时只要条件判断式不成立则进行循环,并执行循环程序,一旦循环条件成立,则终止循环

until [ 条件判断式 ]
	do
		程序
	done

5. 函数(Functions)

可以参考菜鸟教程

function funname(){
    action;
    return int;
}

从上面可以看出函数形式和C语言中是相似的
需要注意的是其中的关键字function,大部分时间都可以省略,这样还可以与其他的shell 兼容,但是有时候bash中命令可能与你的函数名重名,这是就不能省略了,见What is the 'function' keyword used in some bash scripts?

一个例子

#!/bin/bash

function demoFun1(){
    echo "这是我的第一个 shell 函数!"
    return `expr 1 + 1`
}

demoFun1
echo $?
echo $?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2271682.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

让css设置的更具有合理性

目录 一、合理性设置宽高 二、避免重叠情况&#xff0c;不要只设置最大宽 三、优先使用弹性布局特性 四、单词、数字换行处理 五、其他编码建议 平常写css时&#xff0c;除了遵循一些 顺序、简化、命名上的规范&#xff0c;让css具有合理性也是重要的一环。 最近的需求场…

【C++】深入理解C语言中的特殊字符处理与问题分析优化

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;题目&#xff1a;B2090 年龄与疾病输入格式输出格式输入输出样例 &#x1f4af;初始代码分析与问题排查问题点分析 &#x1f4af;修正后的代码与优化修正与优化要点 &#…

面试题解,JVM中的“类加载”剖析

一、JVM类加载机制说一下 其中&#xff0c;从加载到初始化就是我们的类加载阶段&#xff0c;我们逐一来分析 加载 “加载 loading”是整个类加载&#xff08;class loading&#xff09;过程的一个阶段&#xff0c;加载阶段JVM需要完成以下 3 件事情&#xff1a; 1&#xff0…

vue路由模式面试题

vue路由模式 1.路由的模式有哪些?有什么区别? history和hash模式 区别: 1.表现的形态不同: 在地址栏url中:hash模式中带有**#**号,history没有 2.请求错误时表现不同: 在hash模式中,对于404地址请求时,不会进行请求 但是在history模式中,对于404请求时,仍然会进行请求…

构建一个rust生产应用读书笔记7-确认邮件3

设计架构思路 从前面的学习过程中&#xff0c;我们从单一文件测试套件发展到模块化测试套件&#xff0c;并构建了一套强大的辅助工具&#xff0c;这是一个非常重要的进展。个人认为测试代码和应用代码一样&#xff0c;是一个持续进化的过程。随着项目的不断成长&#xff0c;测…

默认ip无法访问,利用dhcp功能获取ip进行访问的方法

应用场景&#xff1a; ac的默认ip如192.168.1.1在pc与ac的eth2以后网口直连无法ping通&#xff0c;而且pc改为dhcp自动获取ip也获取不到ip地址&#xff0c;无法进行web配置和命令行操作。 原因是ac或其他设备被修改了默认ip或者对应端口所属vlanid&#xff0c;现在的端口vlan…

redis的集群模式与ELK基础

一、redis的集群模式 1.主从复制 &#xff08;1&#xff09;概述 主从模式&#xff1a;这是redis高可用的基础&#xff0c;哨兵和集群都是建立在此基础之上。 主从模式和数据库的主从模式是一样的&#xff0c;主负责写入&#xff0c;然后把写入的数据同步到从服务器&#xff…

大脑特训,自信 “满格”

编辑&#xff1a;念小艺 在追求自信的漫漫长路上&#xff0c;诸多因素如同闪耀的星光&#xff0c;为人们指引着方向。保持良好的饮食习惯&#xff0c;让身体摄取充足且均衡的营养&#xff0c;为精神的饱满提供坚实后盾&#xff1b;持续投身于锻炼之中&#xff0c;在挥洒汗水的…

渗透测试-非寻常漏洞案例

声明 本文章所分享内容仅用于网络安全技术讨论&#xff0c;切勿用于违法途径&#xff0c;所有渗透都需获取授权&#xff0c;违者后果自行承担&#xff0c;与本号及作者无关&#xff0c;请谨记守法. 此文章不允许未经授权转发至除先知社区以外的其它平台&#xff01;&#xff0…

计算机的发展、计算机基本组成原理

计算机系统 软件 硬件 硬件的发展 软件的发展 低级语言&#xff1a;机器语言、汇编语言 一、早期冯诺依曼机的结构 存储程序&#xff1a;将指令以二进制代码事先输入计算机的主存储器 在计算机系统软件和硬件是等效的 软件&#xff1a;数据 程序 硬件&#xff1a; 存储器、…

公共数据授权运营系统建设手册(附下载)

在全球范围内&#xff0c;许多国家和地区已经开始探索公共数据授权运营的路径和模式。通过建立公共数据平台&#xff0c;推动数据的开放共享&#xff0c;促进数据的创新应用&#xff0c;不仅能够提高政府决策的科学性和公共服务的效率&#xff0c;还能够激发市场活力&#xff0…

[极客大挑战 2019]HardSQL 1

看了大佬的wp&#xff0c;没用字典爆破&#xff0c;手动试出来的&#xff0c;屏蔽了常用的关键字&#xff0c;例如&#xff1a;order select union and 最搞的是&#xff0c;空格也有&#xff0c;这个空格后面让我看了好久&#xff0c;该在哪里加括号。 先传入1’ 1试试&#…

iOS 逆向学习 - iOS Architecture Cocoa Touch Layer

iOS 逆向学习 - iOS Architecture Cocoa Touch Layer 一、Cocoa Touch Layer 简介二、Cocoa Touch Layer 的核心功能1. UIKit2. Event Handling&#xff08;事件处理&#xff09;3. Multitasking&#xff08;多任务处理&#xff09;4. Push Notifications&#xff08;推送通知&…

STM32烧写失败之Contents mismatch at: 0800005CH (Flash=FFH Required=29H) !

一&#xff09;问题&#xff1a;用ULINK2给STM32F103C8T6下载程序&#xff0c;下载方式设置如下&#xff1a; 出现下面两个问题&#xff1a; 1&#xff09;下载问题界面如下&#xff1a; 这个错误的信息大概可以理解为&#xff0c;在0x08000063地址上读取到flash存储为FF&am…

使用命令行管理git项目

# 初始化一个新的Git仓库 git init # 添加文件到暂存区 git add <file> # 提交暂存区的更改到仓库 git commit -m "commit message" # 查看当前仓库的状态 git status # 查看提交历史 git log # 查看文件的改动 git diff <file> # 创建一个新…

论文笔记PhotoReg: Photometrically Registering 3D Gaussian Splatting Models

1.abstract 最近推出的3D高斯飞溅(3DGS)&#xff0c;它用多达数百万个原始椭球体来描述场景&#xff0c;可以实时渲染。3DGS迅速声名鹊起。然而&#xff0c;一个关键的悬而未决的问题仍然存在&#xff1a;我们如何将多个3DG融合到一个连贯的模型中&#xff1f;解决这个问题将使…

javaEE-网络原理-1初识

目录 一.网络发展史 1.独立模式 2.网络互联 二.局域网LAN 1.基于网线直连&#xff1a; 2.基于集线器组件&#xff1a; 3.基于交换机组件&#xff1a; 4.基于交换机和路由器组件 ​编辑 三、广域网WAN 四、网络通信基础 1.ip地址 2.端口号&#xff1a; 3.协议 4.五…

电子电气架构 --- 整车整车网络管理浅析

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 所谓鸡汤,要么蛊惑你认命,要么怂恿你拼命,但都是回避问题的根源,以现象替代逻辑,以情绪代替思考,把消极接受现实的懦弱,伪装成乐观面对不幸的…

xml格式化(3):增加头部声明

前言 这篇文章&#xff0c;是用来增加头部声明。 正文 from lxml import etreedef pretty_print(element, level0, indent" "):result ""# 判断元素是否为注释节点if isinstance(element, etree._Comment):result f"{indent * level}<!--{el…

STM32 高级 物联网通讯之LoRa通讯

目录 LoRa通讯基础知识 常见的3种通讯协议 远距离高速率的传输协议 近距离高速率传输技术 近距离低功耗传输技术 低功耗广域网 采用授权频段技术 非授权频段 LoRa简介 LoRa的特点 远距离 低功耗 安全 标准化 地理定位 移动性 高性能 低成本 LoRa应用 LoRa组…