科普文:一天学会shell编程

1.shell概叙

        本文将从shell执行、语法、实战三个方面来讲解shell编程,其实shell编程就是个批处理,将你平时在服务器上单独执行的命令,按照一定要求组织起来,写在一起,然后统一执行,就完事了。

        对于运维人员来说,收集一下服务器信息、写个巡检报告、静默安装oracle、批量安装jdk、nginx、redis、mysql啥的还是很有用的。

        开发人员掌握一些shell编程(java进程启动脚本、maven打包编译脚本、docker服务编排脚本等等),也便于在linux服务器上处理问题。

        只要你能静下来,把本文看完,并将里面的示例敲一遍,执行一遍,可以百分百保证你已经学会了shell编程。话不多说,直接开干。

1.1 什么是shell?

        shell是一个用C 语言编写的程序,它是用户使用Liunx的桥梁。shell既是一种命令语言,又是一种程序设计语言。shell是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问Liunx内核服务。

        在计算机科学中,Shell俗称壳(用来区别于核),是指“为使用者提供操作界面”的软件(command interpreter,命令解析器)。它类似于DOS下的COMMAND.COM和后来的cmd.exe。它接收用户命令,然后调用相应的应用程序。

图形界面shell 和命令行式shell

        shell从操作界面来分,可以分成:图形界面shell 、命令行式shell。

        一:图形界面shell (Graphical User Interface shell 即 GUI shell) 例如:应用最为广泛的Windows Explorer(微软的Windows系列操作系统),还有也包括广为人知的Linux shell,其中Linux shell包括X Window Manager(BlackBox和FluxBox),以及功能更强大的CDE、GNOME、KDE、 Xfce。

        二:命令行式shell (Command Line Interface shell ,即CLI shell)。命令式shell就代表了shell,也是我们平常口中的shell,所以本文的重点就是讲命令行式shell。

交互式shell和非交互式shell

        交互式模式就是shell等待用户的输入,并且执行用户提交的命令。这种模式被称作交互式是因为shell与用户进行交互。这种模式也是大多数用户非常熟悉的:登录、执行一些命令、签退。当用户签退后,shell也终止了。

        shell也可以运行在另外一种模式:非交互式模式。在这种模式下,shell不与用户进行交互,而是读取存放在文件中的命令,并且执行它们。当它读到文件的结尾,shell也就终止了。

sh、ksh、bash 

        shell从解释器来分:sh、ksh、bash、csh等等。将在1.3节中再重点讲。

1.2什么是shell脚本?

        shell脚本(shell script) ,是一种为shell编写的脚本程序,一般后缀名为 .sh
业界所说的shell 通常都是指shell脚本,但shell 和 shell script 是两个不同的概念。

        shell脚本文件命名格式:*.sh  后缀一般是".sh",当然也可以不写,比如:MySQL的mysqld_safe就没有带后缀。

        shell脚本文件执行方式:

  sh  -n  /path/to/some-script  : 只检测脚本中的语法错误,但无 法检查出命令错误,但不真正执行脚本
  sh  -x  /path/to/some_script  :调试并执行
  bash  -n  /path/to/some-script  : 只检测脚本中的语法错误,但无 法检查出命令错误,但不真正执行脚本
  bash  -x  /path/to/some_script  :调试并执行
  直接加执行权限运行:chmod +x   
  直接执行网上的脚本:   curl    http:\\sdss     2>/dev/null  | bash 

        shell脚本文件内容格式:

         包括  文件头、注释、 变量、命令、 流程控制、函数等6部分。

  1. 文件头:‌Shell脚本通常以一行特殊的注释作为文件头,‌称为Shebang行。‌Shebang行用于告诉操作系统该脚本应该用哪种解释器执行,‌通常写为#!/bin/sh#!/bin/bash,‌表示使用sh或bash解释器。‌

  2. 注释:‌Shell脚本支持两种注释方式,‌分别是单行注释和多行注释。‌单行注释以#开头,‌直到行末结束。‌多行注释使用:<<开始,‌接着是注释内容,‌最后以EOF结束。‌

  3. 变量:‌变量是Shell脚本中的基本数据类型,‌可以用于存储数字、‌字符串等信息。‌Shell脚本中的变量名通常是大写字母,‌变量值可以使用等号=进行赋值。‌在Shell脚本中,‌可以使用$符号引用变量的值。‌

  4. 命令:‌在Shell脚本中,‌命令可以是Shell命令或其他可执行程序。‌命令可以通过直接调用或使用变量调用。‌变量调用的语法为$(...)。‌

  5. 流程控制语句:‌Shell脚本支持多种流程控制语句,‌例如if语句、‌for循环语句和while循环语句等。‌

  6. 函数:即自定义函数,和C、Java中的函数定义一样,就是对重复、可复用功能的一种封装。

shell脚本示例如下:

#!/bin/bash     #文件头
source /etc/profile  #命令   引入环境变量

# This is a comment  #注释
:<<EOF               #多行注释
This is a multi-line comment.
EOF

# Define a variable
CURRENT_DATE=$(date) #变量  定义本地变量

# Print the current date
echo "The current date is: $CURRENT_DATE"  #命令 打印当前时间

MY_VAR = "Hello World"   #流程控制
if [ $MY_VAR = "Hello World" ]; then
    echo "The variable is equal to Hello World"
else
    echo "The variable is not equal to Hello World"
fi

#函数  定义函数
function my_function() {
    echo "This is my function"
}

my_function  #执行函数

shell脚本格式规范:

一、脚本基本格式

 1、首行为一些命令或声明

  #!/bin/bash
  #!/usr/bin/python
  #!/usr/bin/perl
  #!/usr/bin/ruby
  #!/usr/bin/lua

2、脚本注释规范

  第一行一般为调用
  程序名
  版本号
  更改后的时间
  作者相关信息
  该程序的作用
  简要说明

一、注释
头部注释
#!/bin/bash
# vim:sw=4:ts=4:et
<<INFO
SCRIPYT:test.sh
AUTHOR:anqixiang<邮箱号>
DATE:2021-09-12
DESCRIBE:描述脚本主要功能
SYSTEM:适配哪些操作系统
WARNING:警告信息
VERSION:1.1.0<可选>
MODIFY:记录修改信息,方便查看和维护
INFO

单行注释与多行注释
单行注释以#号开头,如

#修改IP地址

多行注释表示方法(INFO可以用别的标识代替,但需与结尾保持一致)

<<INFO
SCRIPYT:test.sh
AUTHOR:anqixiang<邮箱号>
DATE:2021-09-12
INFO

二、排版规范
1.程序块采用缩进,缩进为4个空格
修改vim中Tab键的距离
举例

while true
do
    sleep 5
done

2.函数
函数功能注释
所有的函数注释应该包含:

函数的描述
全局变量的使用和修改
使用的参数说明
返回值,而不是上一条命令运行后默认的退出状态
示例:

#######################################
# Cleanup files from the backup dir
# Globals:
#   BACKUP_DIR
#   BACKUP_SID
# Arguments:
#   None
# Returns:
#   None
#######################################
cleanup() {
    ...
}

函数编写
函数首字母大写,并用“_”隔开,如Modify_Ip
函数名后面必须加小括号()
第一个大括号与小括号之间保留一个空格
第二个大括号顶格单独占一行
同级别的代码块要左对齐
举例

Modify_Ip() {
	command1
	command2
	if 表达式;then
	    command 3
	else
	    command 4
	fi
}

3.命令较长需分行书写,在低优先级操作符处划分新行,用'\'标识
command 1 | command 2 \
    && command 3 \
    || command 4

4.一行只写一条语句
command 1;command 2			#不推荐
command 1					#推荐
command 2

5.逻辑运算符&&、||和重定向、管道符前后要留空格
command 1 && command 2
command 1 | command 2

# 长命令管道换行连接,管道放置于下一个命令开头,缩进4个空格
command1 \
    | command2 \
    | command3 \
    | command4

6.一个函数只完成一个功能,且不能超过100行
7.case语句格式
case $value in
val1)
    command 1
    ;;
a|b)
    command 2
    ;;
*)
    command 3
esac

8.注释与上面的代码用空行隔开
command 1
[空一行]
#注释内容
command 2

9.循环和判断
for循环
for i in 1..3
do
    if [[ $i -eq 1 ]];then
    	echo "等于1"
    elif [[ $i -eq 2 ]];then
    	echo "等于2"
    else
    	echo "等于3"
    fi
done

while
while read line
do
	...
done < file.txt
或者
command | while read line
do
    ...
done 

三、变量规范
1.变量名由字母、数字、下划线组成, 只能以字母、下划线开头
2.尽量减少全局变量,可在变量前面加local使其成为局部变量
local name=""
for name in a b
do
	echo ${name}
done

全局变量仅在当前Shell有效,使用export定义的全局变量在所有子进程中依然有效

3.环境变量和全局变量大写,局部变量小写(使用下划线连接,如host_ip )
readonly PATH_TO_FILES='/some/path'		#不能修改的变量添加readonly 属性
declare -xr BACKUP_SID='PROD'

declare解释
功能介绍:声明变量的属性,如果使用declare,后面没有任何参数,那么bash就会主动将所有变量名与内容都调出来,just as set.

语 法:declare [-aixr] variable

参数说明:
-a :将后面的variable定义为数组
-i :将后面的variavle定义为整数数字
-x :用法与export一样,就是将后面的variable变成环境变量
-r :将一个variable的亦是设置成只读,读变量不可更改内容,也不能unset

4.引用变量用\${value}
val=${value}
[[ ${value} == "test" ]] && command 1

5.不能被清除和修改的变量通过readonly variable声明
6.常用变量集中写在脚本开头,便于修改
7.对变量赋空值时,建议使用unset
unset sum

8.用ln创建软链接文件,必须先判断文件是否存在,存在时必须先删除,然后再创建软链接
[ -h /data ] && rm -rf /data
ln -sf /home/data /data

9.命令替换推荐使用$(),并用双引号括起来,在groovy中建议使用``(反撇号)
local_ip="$(ip addr | grep ......)"

四、安全清理
1.环境变量和history不能有敏感信息

五、建议
判断推荐使用[[ ]]
判断命令是否存在用command -v代替which(command是内部命令,系统自带;如果命令不存在不会输出任何信息)
路径尽量保持绝对路径,不易出错,如果非要用相对路径,最好用./修饰
简单的if尽量使用&& ||,写成单行。比如[[ x > 2]] && echo x
使用rm删除目录的时候建议先cd到父目录,再删除子目录

1.3什么是shell解释器?

        前面虽然两次提到了#! ,但是本着重要的事情说三遍的精神,

        这里再强调一遍:在 shell 脚本,#! 告诉系统其后路径所指定的程序即是解释此脚本文件的 Shell 解释器。#! 被称作shebang(也称为 Hashbang )。

        #! 决定了脚本可以像一个独立的可执行文件一样执行,而不用在终端之前输入sh, bash, python, php等。  

# 以下两种方式都可以指定 shell 解释器为 bash,第二种方式更好
#!/bin/bash
#!/usr/bin/env bash  ## 推荐

      各主要操作系统下缺省的shell:

  • AIX下是Korn Shell。
  • Solaris缺省的是Bourne shell。
  • FreeBSD缺省的是C shell。
  • HP-UX缺省的是POSIX shell。
  • Linux是Bourne Again shell。

        十五年或者二十年前,一些服务器监控系统,为了获取服务器的资源使用情况,统一用sh解释器来执行和编写服务器信息收集脚本,因为sh可以在bash、ksh、csh下面都能运行,而bash、ksh、csh之间有兼容性问题,写出来的脚本不一定能通用。

2.shell变量

 1、变量类型

  内置变量:PS1,PATH,  UID    HOSTNAME  $$  BASHPID  PPID  $?  HISTSIZE

2、变量数据类型:

  字符  

  数值:整型、浮点型,bash不支持浮点数

3、变量全名法则

  区分大小写、不能使用程序中的保留字和内置变量,如:if ,for;   

  只能使用数字、字母及下划线,且不能以数字开头,

4、变量定义

  普通变量:生效范围为当前SHELL进程,对当前SHELL之外的其它SHELL进程,均无效

  环境变量:生效范围为当前SHELL进程及其子进程

  本地变量:生效范围为当前SHELL进程中某代码片断,通用指函数

  变量赋值:name='value',,,赋值为临时生产,退出后变量自动删除;脚本中的变量会随着脚本结束而删除

      直接字串:name='root'

      变量引用:name="$USER"

      命令引用:name=`COMMAND`   或者  name=$(COMMAND)

5、引用 

  弱引用:"$name"  其中的变量引用会被替换为变量值

  强引用:'$name'  其中的变更引用不会被替换为变量值,而保持原字符串

  追加:NAME  +=:DJY

6、变量显示和删除

  set  显示所有变量

  unset <name>  删除变量

7、环境变量

  可以使子进程,继承父进程的变量,但是无法让父进程使用子进程的变量

  一旦子进程修改从父进程继承的变量,将会新的值传递给孙子进程

  一般只在系统配置文件中使用,在脚本中较少使用

  显示所有环境变量:env    printenv    export     declare   -x

8、bash内建的 环境变量

  PATH   SHELL  USER   UID   HOME   PWD   SHLVL   LANG   MAIL   HOSTNAME    HISTSIZE  

9、只读变量

   readonly name         declare   -r name 

10、位置变量

  在bash  shell中内置的变量,在脚本代码中调用通过命令行传递给脚本的参数  

    清空所有位置变量:set  --

11、退出状态码变量: 

  $?的值为0       代表成功   ;  $ ?  取值范围 0-255

  ?的值为1-255    代表失败

  自定义退出状态码:  exit  [n]

12、展开命令行

  

 

  13、脚本安全和SET

  set命令:可以用来定制shell环境

  

    

3.格式化输出  printf  

   .

  

 1、常用格式替换符

  

   

 2、常用转义字符  

  

3、格式化输出示例

# 单引号
printf '%d %s\n' 1 "abc"
#  Output:1 abc

# 双引号
printf "%d %s\n" 1 "abc"
#  Output:1 abc

# 无引号
printf %s abcdef
#  Output: abcdef(并不会换行)

# 格式只指定了一个参数,但多出的参数仍然会按照该格式输出
printf "%s\n" abc def
#  Output:
#  abc
#  def

printf "%s %s %s\n" a b c d e f g h i j
#  Output:
#  a b c
#  d e f
#  g h i
#  j

# 如果没有参数,那么 %s 用 NULL 代替,%d 用 0 代替
printf "%s and %d \n"
#  Output:
#   and 0

# 格式化输出
printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg
printf "%-10s %-8s %-4.2f\n" 郭靖 男 66.1234
printf "%-10s %-8s %-4.2f\n" 杨过 男 48.6543
printf "%-10s %-8s %-4.2f\n" 郭芙 女 47.9876
#  Output:
#  姓名     性别   体重kg
#  郭靖     男      66.12
#  杨过     男      48.65
#  郭芙     女      47.99

4.shell运算符

一、算术运算

    SHELL允许在某些情况下对算术表达式进行求值;   bash  只支持整数,不支持小数

1、实现算术运算  

 2、内建的随机数生成器变量

  $RANDOM   取值范围:0-32767

3、增强型赋值:let varOPERvalue  

 二、逻辑运算  

     与:&    或:|    非:!  异或:^      相同为假,不同为真

1、 短路运算

  短路与  &&          CMD1  &&  CMD2     当1为真时,再计算2值; 当1为假时,结果为0,不再执行2

  短路或 ||

  短路与  和短路或  组合    :  CMD1  && CMD2   ||  CMD3            先执行与再执行或,反过来不使用。

三、条件测试命令

1、条件测试命令格式

  test   EXPRESSION

  [EXPRESSION]    和test等价,建议使用【】

  [[EXPRESSION]]   相当于增强版的【】,支持【】的用法 ,且支持扩展正则表达式和通配符

2、变量测试 

  【 -v  NAME】  判断NAME变量是否定义 

3、数值测试

  -eq  是否等于    ==

  -ne  是否不等于    !=

  -gt  是否大于    >

  -ge  是否大于等于  >=

  -lt  是否小于    <

  -le  是否小于等于  <=

 4、字符串测试

 5、文件测试  

   

   

 四、关于()和{}

  (CMD1; CMD2;...)和{CMD1;CMD2; ....;}都可以将多个命令组合在一起,批量执行  

 五、组合测试条件

 1、第一种方式  

2、第二种方式  

六、使用read命令来接受输入

  使用read来把输入值分配给一个或多个shell变量,read从标准输入中读取值,给每个单词分配一个变量,所有剩余单词都被分配给最后一个变量,如果变量名没有指定,默认标准输入的值赋值给系统内置变量REPLY

  格式:   read [options] [name...]

 常见选项:  

-p           指定要显示的提示
-s            静默输入, 一般用于密码
-n  N        指定输入的字符长度N
-d  '字符'   输入结束符
-t  N        TIMEOUT为N秒

5.Shell流程控制

        Shell流程控制是Shell脚本编程中的重要组成部分,它允许我们根据条件、循环或其他逻辑结构来控制脚本的执行流程。

        通过掌握条件语句、循环语句、选择语句和其他控制语句的用法,我们可以编写出更加智能、灵活、高效、可靠的自动化脚本,以满足各种复杂的自动化需求。

        在实际应用中,我们可以根据具体的任务需求选择合适的流程控制结构,并结合Shell的其他功能(如变量、函数、管道等)来实现更加复杂的自动化任务。

1、条件语句

        条件语句根据指定的条件来决定脚本的执行路径。在Shell脚本中,常用的条件语句有if、elif和else。这些语句允许我们根据条件表达式的值来执行不同的命令或命令组。

    1. if语句:当条件为真时执行相应的命令或命令组。

    2. elif语句:在if语句中,当第一个条件不满足时,可以使用elif来指定另一个条件进行判断。

    3. else语句:在if或elif条件都不满足时,执行else块中的命令或命令组。

2、循环语句

        循环语句允许我们重复执行某个命令或命令组,直到满足某个条件为止。Shell脚本中常用的循环语句有for、while和until。

    1. for循环:遍历一个列表或序列中的元素,并对每个元素执行相应的命令或命令组。

    2. while循环:当条件为真时,重复执行命令或命令组。

    3. until循环:与while循环相反,当条件为假时重复执行命令或命令组。

3、选择语句

    1. case语句:用于基于多个可能的值进行条件选择。

4、其他控制语句

        除了条件语句、选择语句和循环语句外,Shell还提供了其他一些控制语句,如break(跳出循环)、continue(跳过当前循环迭代)、exit(退出脚本)等。这些语句用于更精细地控制脚本的执行流程。

if语句

1、基本语法:

if [ 条件表达式 ]  
then  
    # 当条件为真时执行的命令  
elif [ 另一个条件表达式 ]  
then  
    # 当另一个条件为真时执行的命令  
else  
    # 当所有条件都不为真时执行的命令(可选)  
fi

2、注意事项:

    1. [ 条件表达式 ] 中的空格是必需的。方括号与条件表达式之间以及方括号内部的操作符和变量之间都需要有空格。

    2. 条件表达式通常使用测试命令(如 [ -f file ] 检查文件是否存在)或比较操作符(如 [ $a -eq $b ] 比较两个数字是否相等)。

    3. then、elif、else 和 fi 是 if 语句的关键字,用于定义条件块。

3、示例:

    声明一个名为AGE的变量,根据这个变量的值来输出不同的消息。

#!/bin/bash

AGE=25

if [ $AGE -lt 18 ]
then
    echo "You are a teenager."
elif [ $AGE -ge 18 ] && [ $AGE -lt 65 ]
then
    echo "You are an adult."
else
    echo "You are a senior citizen."
fi
# 输出:You are an adult.

        在这个示例中,使用了两个条件表达式:$AGE -lt 18(检查年龄是否小于18)和 $AGE -ge 18 && $AGE -lt 65(检查年龄是否在18到65岁之间)。根据AGE变量的值,脚本会输出相应的消息。

        在比较操作符(如-lt、-ge)和变量之间必须添加空格。还使用了逻辑操作符&& 来组合两个条件表达式。

图片

for循环

    在Shell脚本中,for循环是一种常用的控制结构,用于迭代一系列的值或元素。Shell脚本中的for循环有几种不同的形式,但最常见的是基于列表或数组的循环,以及基于范围的循环。

1、基于列表或数组的循环

    • 使用空格分隔的值列表

    • for element in value1 value2 value3
      do
          echo "$element"
      done
      # 输出
      value1
      value2
      value3

    • 使用数组(注意:不是所有的Shell都支持数组)

  • array=("value1" "value2" "value3")
    for element in "${array[@]}"
    do
        echo "$element"
    done
    # 输出
    value1
    value2
    value3

    • 从文件中读取每一行

for line in $(cat filename.txt)
do
    echo "$line"
done
    • 使用通配符迭代文件或目录

for file in /path/to/dir/*.txt
do
    echo "$file"
done

2、基于范围的循环

    在Bash中,可以使用seq命令或花括号扩展。

    • 使用seq命令

for i in $(seq 1 5)
do
    echo "$i"
done
# 输出:
1
2
3
4
5
    • 使用花括号扩展(Bash 4+)

for i in {1..5}
do
    echo "$i"
done
# 输出:
1
2
3
4
5

3、遍历命令的输出

    • 遍历ls命令的输出(注意:不要在生产环境中对ls的输出进行解析)

for file in $(ls /path/to/dir/)
do
    echo "$file"
done
    • 更安全的方法是使用find命令与-print0和read -d $'\0'组合

find /path/to/dir/ -type f -print0 | while IFS= read -r -d $'\0' file; do
    echo "$file"
done

        注意:当处理文件名或路径时,特别是当它们可能包含空格、换行符或其他特殊字符时,使用$(command)或命令替换可能会产生问题。在这种情况下,使用find命令与-print0和read -d $'\0'组合是更安全的选择。

while循环

    在Shell脚本中,while循环用于在条件为真时重复执行一系列命令。当条件变为假时,循环停止执行。

1、基本语法

while [ 条件表达式 ]  
do  
    # 当条件为真时执行的命令  
done

2、示例

    • 使用简单的条件检查

#!/bin/bash  

counter=1  

while [ $counter -le 5 ]  
do  
    echo "这是循环的第 $counter 次迭代"  
    ((counter++))  
done

# 输出:
这是循环的第 1 次迭代
这是循环的第 2 次迭代
这是循环的第 3 次迭代
这是循环的第 4 次迭代
这是循环的第 5 次迭代

        在这个例子中,初始化了一个变量counter,并使用while循环检查该变量的值是否小于或等于5。在每次循环迭代中,我们都打印出一条消息并增加counter的值。当counter的值大于5时,条件不再为真,循环停止。

    • 读取文件内容

#!/bin/bash  

while IFS= read -r line  
do  
    echo "$line"  
done < filename.txt

        在这个例子中,我们使用read命令从文件filename.txt中逐行读取内容,并将每一行存储在变量line中。IFS=用于防止文件名中的空格或特殊字符被错误地解释为字段分隔符。然后,我们打印出每一行的内容。

    • 无限循环(需要手动退出)

        有时,你可能想要创建一个无限循环,直到用户手动退出。这可以通过在循环条件中使用true来实现,因为true始终返回真。

#!/bin/bash  

while true  
do  
    echo "这是一个无限循环,按Ctrl+C退出"  
    sleep 1  # 暂停1秒,以避免过快地输出  
done

        在这个例子中,我们使用true作为while循环的条件,因此循环将无限期地继续执行。我们使用sleep 1命令在每次迭代之间暂停1秒,以避免过快地输出消息。要退出这个无限循环,你可以按Ctrl+C。

3、注意事项

    • 确保在while循环中正确地更新条件表达式中使用的变量,以避免无限循环。

    • 当从文件中读取内容时,使用read命令的-r选项可以确保按原样读取行,而不会解释反斜杠字符为转义字符。

    • 使用IFS=可以确保文件名中的空格、换行符和其他特殊字符不会被错误地解释为字段分隔符。

    • 如果在while循环中使用read命令从管道或重定向中读取输入,请确保在done关键字之后没有额外的输入重定向操作符(如<或|),因为这可能会导致语法错误。

until循环

    在Shell脚本中,until循环与while循环类似,但它会在条件表达式为假时执行循环体,直到条件表达式变为真时停止。也就是说,until循环会一直执行,直到满足某个条件为止。

1、基本语法

until [ 条件表达式 ]
do
    # 当条件为假时执行的命令
done

2、示例

    • 使用简单的条件检查

#!/bin/bash

counter=0

until [ $counter -ge 5 ]
do
    echo "这是循环的第 $counter 次迭代"
    ((counter++))
done

        在这个例子中,我们初始化了一个变量counter,并使用until循环检查该变量的值是否大于或等于5。在每次循环迭代中,我们都打印出一条消息并增加counter的值。当counter的值达到或超过5时,条件变为真,循环停止。

    • 等待某个文件出现

假设你正在等待一个名为result.txt的文件在某个目录下出现,你可以使用until循环配合文件测试操作符[ -f file ]来实现。

#!/bin/bash

until [ -f /path/to/result.txt ]
do
    echo "result.txt 文件尚未出现,等待..."
    sleep 1  # 暂停1秒,以避免过快地检查
done

echo "result.txt 文件已出现,继续执行后续操作..."

在这个例子中,until循环会一直检查/path/to/result.txt文件是否存在。如果不存在,它会打印一条消息并暂停1秒,然后再次检查。一旦文件出现,条件变为真,循环停止,并打印出相应的消息。

3、注意事项

    • 与while循环类似,确保在until循环中正确地更新条件表达式中使用的变量,以避免无限循环。

    • 使用sleep命令可以在循环迭代之间添加延迟,以避免过快地检查条件或执行其他操作。

    • 如果你的Shell脚本中同时使用了while和until循环,请确保你清楚它们之间的区别,并正确地使用它们来满足你的需求。

case语句

    在Shell脚本中,case 语句用于基于多个可能的值进行条件选择。它与C、C++、Java等编程语言中的 switch 语句类似,但Shell中使用的是 case 而不是 switch。case 语句允许脚本根据变量的值来执行不同的代码块。

1、基本语法:

case 变量 in
    模式1)
        # 当变量匹配模式1时执行的命令
        ;;
    模式2)
        # 当变量匹配模式2时执行的命令
        ;;
    ...
    *)
        # 当变量不匹配任何模式时执行的命令(可选)
        ;;
esac

2、注意事项:

    1. 每个 case 分支都以 ;; 结尾,这表示该分支的结束。

    2. *) 是一个特殊的模式,用于捕获所有不匹配前面模式的值。

    3. 可以在模式中使用通配符,如 *(匹配任何字符串)、 ?(匹配任何单个字符)等。

3、示例:

    声明一个名为 DAY 的变量,它包含了星期几的缩写(如Mon、Tue等),根据这个变量的值来输出不同的消息。

#!/bin/bash

DAY="Wed"


case $DAY in
    Mon)
        echo "Today is Monday."
        ;;
    Tue)
        echo "Today is Tuesday."
        ;;
    Wed)
        echo "Today is Wednesday."
        ;;
    Thu)
        echo "Today is Thursday."
        ;;
    Fri)
        echo "Today is Friday."
        ;;
    Sat|Sun)
        echo "Today is the weekend!"
        ;;
    *)
        echo "Invalid day: $DAY"
        ;;
esac
# 输出:Today is Wednesday.

         在这个示例中,我们根据DAY变量的值来匹配不同的case分支,并输出相应的消息。注意,在Sat|Sun)这个分支中,我们使用了 | 来表示“或”的关系,这意味着当DAY的值为Sat或Sun时,都会执行这个分支中的命令。如果DAY的值不匹配任何已知的模式,那么就会执行*)这个默认分支中的命令。

其他控制语句

1、break

    break 语句用于立即终止当前的循环(如 for、while 或 until 循环)。当遇到 break 语句时,循环将不再继续执行,而是跳转到循环之后的下一条语句。

#!/bin/bash
for i in {1..5}
do
    if [ $i -eq 3 ]; then
        break
    fi
    echo $i
done
# 输出:1 2

2、continue

    continue 语句用于跳过当前循环的剩余部分,直接开始下一次循环迭代。当遇到 continue 语句时,循环的当前迭代将立即结束,但不会终止整个循环。

#!/bin/bash
for i in {1..5}
do
    if [ $i -eq 3 ]; then
        continue
    fi
    echo $i
done
# 输出:1 2 4 5

3、exit

    exit 语句用于立即终止脚本的执行。可以为其提供一个状态码,用于指示脚本是否成功执行。状态码 0 通常表示成功,而非零值表示某种错误或异常情况。

#!/bin/bash
if [ ! -f "somefile.txt" ]; then
    echo "somefile.txt does not exist!"
    exit 1
fi
echo "Processing somefile.txt..."
# 如果 somefile.txt 不存在,则脚本会输出错误消息并退出,状态码为 1

注意:exit 语句不仅会终止当前脚本的执行,还会终止任何调用该脚本的父进程(如果它是在子shell中运行的)。因此,在脚本中使用 exit 时要小心,确保你了解其潜在影响。

6.shell示例

学习脚本

#! /bin/bash
#--------------------------------------------
# shell shell脚本学习
# author:zhouxx
#--------------------------------------------

# if语句
if [ $1="input" ]
then
	echo "Hello Shell"
elif [ $1!="output" ]
then
	echo exit
else
	echo "Good bye"
fi


# while循环
temp=$2
while [ $temp -lt 10 ]
do
	echo "whileing"
	temp=$(($temp+1))
done


# case的使用
case $temp in
	"10")
		echo "while正常执行"
		;;
	*)
		echo "while执行异常,temp=$temp"
		;;
esac

# 使用read进行终端读取
read -p "请随便输入:" input
echo "input=$input"


# 当前文件名为
echo "当前文件名为:$(basename $0)"
# 更改文件后缀
echo "文件名更换为txt:$(basename $0 .sh).txt"
# 当前文件的路径为
echo "当前文件的路径为:$(dirname $0)"


# 编写一个函数
func(){
	echo "函数func正在被调用"
	echo "传入func的参数为:$1,即文件路径"
	return 1;
}
# 调用这个函数
func "$0"
echo "函数的返回值为$?"

服务器巡检脚本

#!/bin/bash
#--------------------------------------------
# shell 服务器巡检示例
# author:zhouxx
#--------------------------------------------

#系统信息
os_system(){
os_type=`uname`
echo "当前系统是:$os_type"
os_banben=`cat /etc/redhat-release`
echo "当前系统版本是:$os_banben"
os_neihe=`uname -r`
echo "当前系统内核为:$os_neihe"
os_time=`date +%F_%T`
echo "当前系统实际为:$os_time"
os_last=`uptime |awk '{print $4 $5}'| awk -F , '{print $1}'`
echo "当前系统最后重启时间为:$os_last"
os_hostname=`hostname`
echo "当前系统主机名为:$os_hostname"
}
#网络信息
os_network(){
##主机IP地址
ip_addr=`ifconfig ens33| grep broadcast | awk '{print $2}'`
echo "当前系统ip为:$ip_addr"
##判断主机是否可以连通
ping -c1 www.baidu.com
if [ $? -eq 0 ];then
        echo "当前系统IP可以连通"
else
        echo "当前系统IP不通请联系管理员"
fi
##统计网卡流量
#流入流量
RX=`ifconfig ens33 | grep RX | head -n1 | awk '{print $3/1024/1024}'`
echo "网卡流入流量为:$RX"
#流出流量
TX=`ifconfig ens33 | grep TX | head -n1 | awk '{print $3/1024/1024}'`
echo "网卡流出流量为:$TX"
}

#硬件信息
cpu_info(){
#cpu的物理个数
cpu_phy=`cat /proc/cpuinfo | grep "physical id" | wc -l`
echo "当前系统物理cpu为:$cpu_phy"
#cpu的核心数
cpu_core=`cat /proc/cpuinfo | grep "core" | wc -l`
echo "当前系统cpu核心数为:$cpu_core"
#cpu的型号
cpu_model=`cat /proc/cpuinfo | grep "model" | sed -n '2p' | awk -F : '{print $2}'`
echo "当前系统cpu型号是:$cpu_model"
}
##内存信息
mem_info(){
mem_total=`free -m | grep Mem | awk '{print $2}'`
echo "内存总大小为:$mem_total"
mem_used=`free -m | grep Mem | awk '{print $3}'`
echo "已使用内存为:$mem_used"
mem_free=`free -m | grep Mem | awk '{print $4}'`
echo "剩余内存为:$mem_free"

#百分比
shiyong_free=`echo "scale=2;$mem_used/$mem_total*100"|bc`
echo "已使用内存百分比为:$shiyong_free"
shengyu_free=`echo "scale=2;$mem_free/$mem_total*100"|bc`
echo "剩余内存百分比为:$shengyu_free"
}
os_system
os_network
cpu_info
mem_info

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于Android平台开发,仿头条新闻app

相关视频教程在某站上面(&#x1f50d;浩宇软件开发) 1. 项目模块功能思维导图 2. 项目涉及到的技术点 数据来源&#xff1a;聚合数据API使用okhttp网络请求框架获取api数据使用gson库解析json数据使用RecyclerViewadapter实现新闻列表使用SQLite数据库实现用户登录&#xff0…

【thingsbord源码编译】 显示node内存不足

编译thingsbord显示报错 FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory问题原因分析 重新安装java版本 编译通过

小程序多次扫描获取sence失败------ivx

扫码图片被告知侵权了&#xff0c;删除了&#xff0c;如果有需要的同学可以自己尝试。或者直接联系我。 在微信小程序里面有一个函数 wx.getEnterOptionsSync() 功能描述 获取本次小程序启动时的参数。如果当前是冷启动&#xff0c;则返回值与 App.onLaunch 的回调参数一致&am…

前端最全面试题【最新版本2024-7月】

文章目录 最常见问题javascript篇Javascript的运行机制javascript的数据类型怎样判断变量的类型数据类型转换闭包的优缺点v-if和v-for哪个优先级更高&#xff1f; 如果两个同时出现&#xff0c;应该怎么优化得到更好的性能&#xff1f;HTML5的新特性和CSS3的新特性div 上下居中…

SpringBoot注解--11--@JSONField @JsonProperty

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一个问题&#xff1a;后端实体类isXXX开头的属性&#xff0c;传到前端后自动去掉is解决方法&#xff1a; JsonProperty和JSONField1.简介2.注解的区别2.1 底层框架不…

政安晨【零基础玩转各类开源AI项目】基于Ubuntu系统部署ComfyUI:功能最强大、模块化程度最高的Stable Diffusion图形用户界面和后台

目录 ComfyUI的特性介绍 开始安装 做点准备工作 在Conda虚拟环境中进行 依赖项的安装 运行 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: 零基础玩转各类开源AI项目 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&…

Rust vs Go: 特点与应用场景分析

目录 介绍Rust的特点Go的特点Rust的应用场景Go的应用场景总结 介绍 Rust和Go&#xff08;Golang&#xff09;是现代编程语言中两个非常流行的选择。凭借各自的独特优势和广泛的应用场景&#xff0c;吸引了大量开发者的关注。本文将详细介绍Rust和Go的特点&#xff0c;并探讨它…

【理解串】

目录 一、串的基本概念二、串的基本操作及实现三、串的存储实现3.1、静态数组实现3.2、动态数组实现 四、串的朴素模式匹配4.1、算法思想4.2、代码实现 五、KMP算法5.1、算法思想5.2、求模式串的next数组5.2、代码实现 一、串的基本概念 串&#xff1a;即字符串&#xff08;st…

一行命令快速导出、导入Python的依赖环境(Python)

文章目录 一、pip1、导出2、导入 二、Conda&#xff08;简&#xff09;1、导出1、导入 一、pip 1、导出 在Pycharm的Terminal窗口输入如下命令&#xff0c;即可将环境导出至文件requirements.txt。 pip freeze > C:\Users\sdl\Deskto\requirements.txt也可以在DOS界面执行…

Python 核心编程

Python 核心编程 1. 数据类型1.1 整型 int1.2 浮点数 float1.3 布尔类型 bool1.4 字符串 str1.5 列表 list1.6 元组 tuple1.7 集合 set1.8 字典 dict 2. 逻辑结构、文件操作2.1 分支结构和三元表达2.2 循环和遍历2.3 目录和路径2.4 文件操作 3. 函数、类、异常处理3.1 函数3.2 …

[Flask笔记]一个完整的Flask程序

前面讲过Flask是一个轻量级Web开发框架&#xff0c;为什么说是轻量级的呢&#xff0c;因为它用短短几行代码就能运行起来&#xff0c;我们一起来看看最简单的flask框架。 安装Flask 在看Flask框架之前我们需要先安装flask模块&#xff0c;学过python的肯定都知道&#xff0c;…

2.5 计算机网络

声明&#xff1a;文章参考的《系统架构设计师教程&#xff08;第二版&#xff09;》&#xff0c;如有侵权&#xff0c;本人将立即修改和删除。 利用通信线路将地理上分散的、具有独立功能的计算机系统和通信设备按不同的形式连接起来&#xff0c;并依靠网络软件以及通信协议实现…

《昇思25天学习打卡营第18天|onereal》

RNN实现情感分类 概述 情感分类是自然语言处理中的经典任务&#xff0c;是典型的分类问题。本节使用MindSpore实现一个基于RNN网络的情感分类模型&#xff0c;实现如下的效果&#xff1a; 输入: This film is terrible 正确标签: Negative 预测标签: Negative输入: This film…

Android数据库基础

目录 1、安卓数据存储方式 2、数据库事务 数据库事务的特性(ACID) 事务的隔离级别 事务总结 3、ContetProvider 作用 ​编辑 统一资源标识符URI ​编辑 MIME类型 ContentProvider主要方法 4、ContentResolver 作用 主要方法 使用案例 辅助工具类 ContentUris Uri…

matlab 有倾斜的椭圆函数图像绘制

matlab 有倾斜的椭圆函数图像绘制 有倾斜的椭圆函数图像绘制xy交叉项引入斜线负向斜线成分正向斜线成分 x^2 y^2 xy 1 &#xff08;负向&#xff09;绘制结果 x^2 y^2 - xy 1 &#xff08;正向&#xff09;绘制结果 有倾斜的椭圆函数图像绘制 为了确定椭圆的长轴和短轴的…

torchplus

https://gitee.com/hj_research/torchplus 一、安装 pip install tplus

Linux磁盘-创建分区

作者介绍&#xff1a;简历上没有一个精通的运维工程师。希望大家多多关注作者&#xff0c;下面的思维导图也是预计更新的内容和当前进度(不定时更新)。 Linux磁盘涉及到的命令不是很多&#xff0c;但是在实际运维中的作用却很大&#xff0c;因为Linux系统及业务都会承载到硬盘…

评估指标:精确率(Precision)、召回率(Recall)、F1分数(F1 Score)

评估指标&#xff1a;精确率&#xff08;Precision&#xff09;、召回率&#xff08;Recall&#xff09;、F1分数&#xff08;F1 Score&#xff09; 前言相关介绍1. 准确率&#xff08;Accuracy&#xff09;2. 精确率&#xff08;Precision&#xff09;3. 召回率&#xff08;Re…

君子签电子合同推动企业人事管理变革,降本提效

在日益复杂的人力资源管理领域&#xff0c;合同签署与管理成为HR面临的一大挑战。面对庞大的合同量、繁琐的审批流程、频繁的岗位变动以及离职时的合同管理难题&#xff0c;传统方式已难以满足高效、安全、合规的需求。 君子签针对HR面临的挑战和需求&#xff0c;打造智能合同…

Alertmanager告警配置

1、告警概述及说明 告警能力在Prometheus的架构中被划分成两个独立的部分。 通过在Prometheus中定义AlertRule(告警规则)&#xff0c;Prometheus会周期性的对告警规则进行计算&#xff0c;如果满足告警触发条件就会向Alertmanager发送告警信息。 当Alertmanager接收到 Promethe…