Shell脚本入门

简介: Shell脚本入门

一、Shell脚本的基本用法

1 shell脚本自动生成注释

bash

[root@rocky01 ~]# cat .vimrc

set ts=4

set et


autocmd BufNewFile *.sh,*.script exec":call SetTitle()"

function SetTitle()

       if expand("%:e") == "sh"

       call setline(1,"#!/bin/bash")

       call setline(2,"  ")

       call setline(3,"#******************************************************")

       call setline(4,"# Author:          会不会有那么一天                 ")

       call setline(5,"# QQ:              791270697                        ")

       call setline(6,"# Date:            ".strftime("%Y-%m-%d                  ")."     ")

       call setline(7,"# FileName:        ".expand("%")."                         ")

       call setline(8,"# Version:         1.0.0                            ")

       call setline(9,"# Description:     The test script                  ")

       call setline(10,"# BLOG:            https://www.cnblogs.com/Willoneday   ")

       call setline(11,"#******************************************************")

       call setline(12,"")

       call setline(13,"")

       endif

endfunc

autocmd BufNewFile * normal G

 


2 执行远程主机的shell脚本

bash

[root@rocky01 ~]# mv hello.sh /data/httpd-2.4.54/htdocs/

[root@rocky01 ~]# hostname -I

10.0.0.128

[root@rocky01 ~]# curl 10.0.0.128/hello.sh

#!/bin/bash

echo'Hello, world!'


#方法一

[root@rocky01 ~]# curl 10.0.0.128/hello.sh 2> /dev/null |bash

Hello, world!


#方法二

[root@rocky01 ~]# curl -s 10.0.0.128/hello.sh  |bash

Hello, world!

 


3 在远程主机运行本地shell脚本

bash

[root@ubuntu01 ~]# hostname -I

10.0.0.129

[root@ubuntu01 ~]# cat work2.sh

echo"我在`hostname -I`上运行"

[root@ubuntu01 ~]#

[root@ubuntu01 ~]# ssh 10.0.0.128 /bin/bash  < work2.sh

root@10.0.0.128's password:

我在10.0.0.128 上运行

 


4 shell脚本调试

bash -n ScriptName.sh 只检测脚本中的语法错误,但无法检查出命令错误,但不真正执行脚本

bash -x ScriptName.sh 调试并执行

总结:脚本错误常见的有三种

  • 语法错误:会导致后续的命令不继续执行,可以用bash -n 检查错误,提示的出错行数不一定是准确的
  • 命令错误:默认后续的命令还会继续执行,用bash -n 无法检查出来 ,可以使用 bash -x 进行观察
  • 逻辑错误:只能使用 bash -x 进行观察

 


5 变量

5.1 变量类型

  • 内置变量,如:PS1,PATH,UID,HOSTNAME,$$,BASHPID,PPID,$?,HISTSIZE
  • 用户自定义变量

 

5.2 变量命名法则

  • 区分大小写
  • 不能使程序中的保留字和内置变量:如:if, for
  • 只能使用数字、字母及下划线,且不能以数字开头,注意:不支持短横线 “ - ”,和主机名相反

 

5.3 变量定义和引用

变量的生效范围等标准划分变量类型

  • 普通变量:生效范围为当前shell进程;对当前shell之外的其它shell进程,包括当前shell的子shell进程均无效
  • 环境变量:生效范围为当前shell进程及其子进程
  • 本地变量:生效范围为当前shell进程中某代码片断,通常指函数

5.3.1 变量赋值

  • 直接字串:name='root'
  • 变量引用:name="$USER"
  • 命令引用:name=`COMMAND` 或者 name=$(COMMAND)

5.3.2 变量引用

  • $name
  • ${name}

弱引用和强引用

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

bash

[root@rocky01 ~]# NUM=`seq 3`

[root@rocky01 ~]# echo $NUM

1 2 3

[root@rocky01 ~]# echo "$NUM"

1

2

3

[root@rocky01 ~]# echo '$NUM'

$NUM

范例:变量追加

bash

[root@rocky01 ~]# TITLE=Willoneday

[root@rocky01 ~]# TITLE+=:Yes

[root@rocky01 ~]# echo $TITLE

Willoneday:Yes

范例:删除变量

unset <name>

范例:显示系统信息

bash

[root@rocky01 ~]# cat system_info.sh

#!/bin/bash


#******************************************************

# Author:          会不会有那么一天

# QQ:              791270697

# Date:            2022-08-07

# FileName:        system_info.sh

# Version:         1.0.0

# Description:     The test script

# BLOG:            https://www.cnblogs.com/Willoneday

#******************************************************


RED="\E[1;31m"

GREEN="echo -e \E[1;32m"

END="\E[0m"

. /etc/os-release


$GREEN----------------------Host systeminfo--------------------$END

echo -e  "HOSTNAME:     $RED`hostname`$END"

echo -e  "IPADDR:       $RED` hostname -I`$END"

echo -e  "OSVERSION:    $RED$PRETTY_NAME$END"

echo -e  "KERNEL:       $RED`uname -r`$END"

echo -e  "CPU:         $RED`lscpu|grep '^Model name'|tr -s ' '|cut -d : -f2` $END"

echo -e  "MEMORY:       $RED`free -h|grep Mem|tr -s ' ' : |cut -d : -f2`$END"

echo -e  "DISK:         $RED`lsblk |grep '^sd' |tr -s ' ' |cut -d "" -f4`$END"

$GREEN---------------------------------------------------------$END

 

5.4 环境变量

  • 可以使子进程(包括孙子进程)继承父进程的变量,但是无法让父进程使用子进程的变量
  • 一旦子进程修改从父进程继承的变量,将会新的值传递给孙子进程
  • 一般只在系统配置文件中使用,在脚本中较少使用

显示所有环境变量:

  • env
  • printenv
  • export
  • declare -x

bash内的环境变量

  • PATH
  • SHELL
  • USER
  • UID
  • HOME
  • PWD
  • SHLVL   #shell的嵌套层数,即深度
  • LANG
  • MAIL
  • HOSTNAME
  • HISTSIZE
  • _            #下划线,表示前一命令的最后一个参数

 

5.5 位置变量

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

  • $1, $2, ... 对应第1个、第2个等参数,shift [n]换位置
  • $0 shell脚本本身的名字
  • $* 传递给脚本的所有参数,全部参数合为一个字符串
  • $@ 传递给脚本的所有参数,每个参数为独立字符串
  • $# 传递给脚本的参数的个数

注意:$@ $* 只在被双引号包起来的时候才会有差异

 

5.6 退出状态码变量

bash

$?的值为0        #代表成功

$?的值是1到255   #代表失败

用户可以在脚本中使用以下命令自定义退出状态码 exit n

注意:

  • 脚本中一旦遇到exit命令,脚本会立即终止;终止退出状态取决于exit命令后面的数字
  • 如果exit后面无数字,终止退出状态取决于exit命令前面命令执行结果
  • 如果没有exit命令, 即未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一条命令的状态码

 

5.7 随机数生成器变量

$RANDOM  取值范围:0-32767

范例:生成 0 - 49 之间随机数

bash

[root@rocky01 ~]# echo $[$RANDOM%50]

范例:随机字体颜色

bash

[root@rocky01 ~]# echo -e "\033[1;$[RANDOM%7+31]mWilloneday\033[0m"

 


6 算数运算

注意:bash 只支持整数,不支持小数

实现算术运算:

  • (1) let var=算术表达式
  • (2) ((var=算术表达式)) 和(1)等价
  • (3) var=$[算术表达式]
  • (4) var=$((算术表达式))
  • (5) var=$(expr arg1 arg2 arg3 ...)
  • (6) declare -i var = 数值
  • (7) echo '算术表达式' | bc

范例:自增自减

bash

let i+=1

let i++

let i-=1

let i--


[root@rocky01 ~]# i=1; let j=i++; echo "i=$i,j=$j"

i=2,j=1

[root@rocky01 ~]# i=1; let j=++i; echo "i=$i,j=$j"

i=2,j=2

 


7 逻辑运算

7.1 与或非

  • 与:& 一假则假,全真才真
  • 或:|   一真则真,全假才假
  • 非:
  • 异或:^ 异或的两个值,相同为假,不同为真。两个数字X,Y异或得到结果Z,Z再和任意两者之一X异或,将得出另一个值Y

范例:变量互换

bash

[root@rocky01 ~]# x=10;y=20;x=$[x^y];y=$[x^y];x=$[x^y];echo x=$x,y=$y

x=20,y=10

 

7.2 短路运算

  • 短路与 &&

CMD1 && CMD2

第一个CMD1结果为真(1),第二个CMD2必须要参与运算,才能得到最终的结果

第一个CMD1结果为假(0),总的结果必定为0,因此不需要执行CMD2

  • 短路或 ||

CMD1 || CMD2

第一个CMD1结果为真(1),总的结果必定为1,因此不需要执行CMD2

第一个CMD1结果为假(0),第二个CMD2 必须要参与运算,才能得到最终的结果

  • 短路与和或组合

CMD1 && CMD2 || CMD3

当CMD1执行成功时,会执行CMD2

当CMD1执行失败时,会执行CMD3

 

 


8 条件测试命令

条件测试:判断某需求是否满足,需要由测试机制来实现,专用的测试表达式需要由测试命令辅助完成测试过程,实现评估布尔声明,以便用在条件性环境下进行执行

  • 若真,则状态码变量 $? 返回0
  • 若假,则状态码变量 $? 返回

条件测试命令

  • test EXPRESSION
  • [ EXPRESSION ] #和test 等价,建议使用 [ ]
  • [[ EXPRESSION ]] 相关于增强版的 [ ], 支持[]的用法,且支持扩展正则表达式和通配符

         注意:EXPRESSION前后必须有空白字符

8.1 变量测试

bash

#判断 NAME 变量是否定义

[ -v NAME ]

 

8.2 数值测试

  • -eq 等于
  • -ne 不等于
  • -gt 大于
  • -ge 大于等于
  • -lt 小于
  • -le 小于等于

 

8.3 字符串测试

[ ]

  • [ -z STRING ] 字符串没定义或空为真,不空为假,
  • [ -n STRING ] 字符串不空为真,空为假
  • [ STRING ]     字符串不空为真,空为假
  • [ STRING1 = STRING2 ] 是否等于,注意 = 前后有空格
  • [ STRING1 != STRING2 ] 是否不等于

注意:在比较字符串时,建议变量放在“ ”中

[[  ]]

[[ == ]] 左侧字符串是否和右侧的PATTERN相同

注意:此表达式用于 == 右侧为通配符

 

[[ =~ ]] 左侧字符串是否能够被右侧的正则表达式的PATTERN所匹配

注意: 此表达式用于 =~ 右侧为扩展的正则表达式

 

注意:[[ == ]] == 右侧的 * 做为通配符,不要加“”,只想做为*符号使用时, 需要加 “” 或转义

建议:当使用正则表达式或通配符使用[[  ]],其它情况一般使用 [  ]

 

范例:使用 [ ]

bash

[root@rocky01 ~]# echo $NAME


[root@rocky01 ~]# [ -n "$NAME" ]

[root@rocky01 ~]# echo $?

1


[root@rocky01 ~]# NAME=Willoneday

[root@rocky01 ~]# [ -n "$NAME" ]

[root@rocky01 ~]# echo $?

0

[root@rocky01 ~]# [ "$NAME" ]

[root@rocky01 ~]# echo $?

0

范例:使用 [[  ]] 和 通配符

bash

#通配符*

[root@rocky01 ~]# FILE="Will*"

[root@rocky01 ~]# [[ $FILE == W* ]]

[root@rocky01 ~]# echo $?

0


#通配符?

[root@rocky01 ~]# FILE="Will"

[root@rocky01 ~]# [[ $FILE == ???? ]]

[root@rocky01 ~]# echo $?

0

[root@rocky01 ~]# [[ $FILE == ??? ]]

[root@rocky01 ~]# echo $?

1


#引号

#结论:[[ == ]] == 右侧的 * 做为通配符,不要加 " " ,只想做为*符号使用时, 需要加 " " 或转义

[root@rocky01 ~]# NAME="linux1"

[root@rocky01 ~]# [[ "$NAME" == linux* ]]

[root@rocky01 ~]# echo $?

0

[root@rocky01 ~]# [[ "$NAME" == "linux*" ]]

[root@rocky01 ~]# echo $?

1

[root@rocky01 ~]# NAME="linux*"

[root@rocky01 ~]# [[ "$NAME" == "linux*" ]]

[root@rocky01 ~]# echo $?

0

范例:判断合法的考试成绩(满分为100)

bash

[root@rocky01 ~]# [[ $SCORE =~ ^(100|[0-9]{1,2})$ ]]

范例:判断合法的正负数

bash

[root@rocky01 ~]# [[ "$N" =~ ^[0-9]+$ ]]

范例:判断合法IP

bash

[root@rocky01 ~]# [[ "$IP" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$  ]]

 

8.4 文件测试

8.4.1 文件存在性测试

  • -a FILE:同 -e,建议使用-e
  • -e FILE: 文件存在性测试,存在为真,否则为假
  • -b FILE:是否存在且为块设备文件
  • -c FILE:是否存在且为字符设备文件
  • -d FILE:是否存在且为目录文件
  • -f FILE:是否存在且为普通文件
  • -h FILE:或 -L FILE:存在且为符号链接文件
  • -p FILE:是否存在且为命名管道文件
  • -S FILE:是否存在且为套接字文件

8.4.2 文件权限测试

  • -r FILE:是否存在且可读
  • -w FILE: 是否存在且可写
  • -x FILE: 是否存在且可执行
  • -u FILE:是否存在且拥有suid权限
  • -g FILE:是否存在且拥有sgid权限
  • -k FILE:是否存在且拥有sticky权限

注意:最终结果由用户对文件的实际权限决定,而非文件属性决定

 


9 ( ) 和 { }

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

( list ) 会开启子shell,并且list中变量赋值及内部命令执行后,将不再影响后续的环境

{ list } 不会启子shell,在当前shell中运行,会影响当前shell环境

范例

bash

[root@rocky01 ~]# name=Willoneday;( echo $name;name=Dream;echo $name );echo $name

Willoneday

Dream

Willoneday


[root@rocky01 ~]# name=Willoneday;{ echo $name;name=Dream;echo $name; };echo $name

Willoneday

Dream

Dream

 


10 组合测试条件

10.1 第一种方式

[ EXPRESSION1 -a EXPRESSION2 ]   #并且,EXPRESSION1和EXPRESSION2都是真,结果才为真

[ EXPRESSION1 -o EXPRESSION2 ]  #或者, EXPRESSION1和EXPRESSION2只要有一个真,结果就为真

[ ! EXPRESSION ]                                #取反

说明: -a 和 -o 需要使用测试命令进行,[[ ]] 不支持

 

10.2 第二种方式

COMMAND1 && COMMAND2 #并且,短路与,代表条件性的AND THEN

如果COMMAND1 成功,将执行COMMAND2,否则,将不执行COMMAND2

 

COMMAND1 || COMMAND2   #或者,短路或,代表条件性的OR ELSE

如果COMMAND1 成功,将不执行COMMAND2,否则,将执行COMMAND2

 

! COMMAND   #非,取反

范例:用户不存在则创建

bash

[root@rocky01 ~]# getent passwd Willoneday

[root@rocky01 ~]# id Willoneday &> /dev/null || useradd Willoneday

[root@rocky01 ~]# getent passwd Willoneday

Willoneday:x:1001:1001::/home/Willoneday:/bin/bash

范例:网络状态判断

bash

[root@rocky01 ~]# cat ip.sh

#!/bin/bash


#******************************************************

# Author:          会不会有那么一天

# QQ:              791270697

# Date:            2022-08-07

# FileName:        ip.sh

# Version:         1.0.0

# Description:     The test script

# BLOG:            https://www.cnblogs.com/Willoneday

#******************************************************


ip=`hostname -I`

ping $ip -c 1 &> /dev/null && echo  "$ip在线" || echo"$ip不在线"

ip2=1.1.1.1

ping $ip2 -c 1 &> /dev/null && echo  "$ip2在线" || echo"$ip2不在线"



[root@rocky01 ~]# bash ip.sh

10.0.0.128 在线

1.1.1.1不在线

范例:操作系统判断

bash

[root@rocky01 ~]# . /etc/os-release; [[ $ID == "rocky" ]] && [[ $VERSION_ID == 8* ]] && echo Rocky8 || echo NO Rocky8

Rocky8

范例:磁盘空间判断

bash

[root@rocky01 ~]# cat disk_check.sh

#!/bin/bash


#******************************************************

# Author:          会不会有那么一天

# QQ:              791270697

# Date:            2022-08-07

# FileName:        disk_check.sh

# Version:         1.0.0

# Description:     The test script

# BLOG:            https://www.cnblogs.com/Willoneday

#******************************************************


WARNING=80

SPACE_USED=`df|grep '^/dev/sd'|tr -s ' ' %|cut -d% -f5|sort -nr|head -1`

[ "$SPACE_USED" -ge $WARNING ] && echo"disk used is $SPACE_USED,will be full" | mail -s diskwaring root

 


11 read命令接受输入

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

常见选项

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

范例:基本用法

bash

[root@rocky01 ~]# read

Willoneday

[root@rocky01 ~]# echo $REPLY

Willoneday


[root@rocky01 ~]# read NAME AGE

Willoneday 20

[root@rocky01 ~]# echo $NAME

Willoneday

[root@rocky01 ~]# echo $AGE

20

范例:read和 重定向

bash

[root@rocky01 ~]# cat test.txt

1 2

[root@rocky01 ~]# read i j < test.txt ; echo i=$i j=$j

i=1 j=2


[root@rocky01 ~]# echo 1 2 | read x y ; echo x=$x y=$y

x= y=

[root@rocky01 ~]# echo 1 2 | ( read x y ; echo x=$x y=$y )

x=1 y=2

[root@rocky01 ~]# echo 1 2 | { read x y ; echo x=$x y=$y; }

x=1 y=2

范例:判断用户输入的是否为 YES

bash

[root@rocky01 ~]# read -p "Are you rich?yes or no: " ANSWER

Are you rich?yes or no: yes

[root@rocky01 ~]# [[ $ANSWER =~ ^([Yy]|[Yy][Ee][Ss])$ ]] && echo "You are rich" || echo "No"

You are rich

 


二 、shell 的配置文件

1 按生效范围分类

1.1 全局配置

针对所有用户皆有效

  • /etc/profile
  • /etc/profile.d/*.sh
  • /etc/bashrc

1.2个人配置

只针对特定用户有效

~/.bashrc

~/.bash_profile

 

 


2 按登录方式分类

2.1 交互式登录

  • 直接通过终端输入账号密码登录
  • su - UserName

2.2 非交互式登录

  • su UserName
  • 图形界面下打开的终端
  • 执行脚本
  • 任何其它的bash实例

 


3 按功能划分分类

3.1 profile类

profile类为交互式登录的shell提供配置

bash

全局:/etc/profile, /etc/profile.d/*.sh

个人:~/.bash_profile

功用:

(1) 用于定义环境变量

(2) 运行命令或脚本

3.2 bashrc类

为非交互式和交互式登录的shell提供配置

bash

全局:/etc/bashrc

个人:~/.bashrc

功用:

(1) 定义命令别名和函数

(2) 定义本地变量

 


三、流程控制

1 if 选择执行语句

1.1 格式

if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ else COMMANDS; ] fi

1.2 单分支

bash

if 判断条件;then

   条件为真的分支代码

fi

1.3 双分支

bash

if 判断条件; then

   条件为真的分支代码

else

   条件为假的分支代码

fi

1.4 多分支

bash

if 判断条件1; then

   条件1为真的分支代码

elif 判断条件2; then

   条件2为真的分支代码

elif 判断条件3; then

   条件3为真的分支代码

...

else

   以上条件都为假的分支代码

fi

范例:身体质量指数 (BMI)

bash

[root@rocky01 ~]# cat bmi.sh

#!/bin/bash


#******************************************************

# Author:          会不会有那么一天

# QQ:              791270697

# Date:            2022-08-08

# FileName:        bmi.sh

# Version:         1.0.0

# Description:     The test script

# BLOG:            https://www.cnblogs.com/Willoneday

#******************************************************


read -p "请输入身高(m为单位): " HIGH

if [[ ! "$HIGH" =~ ^[0-2](\.[0-9]{,2})?$ ]];then

   echo"输入错误的身高!"

   exit 1

fi


read -p "请输入体重(kg为单位): " WEIGHT

if [[ ! "$WEIGHT" =~ ^[0-9]{1,3}$ ]];then

   echo"输入错误的体重!";

   exit 2;

fi


BMI=`echo$WEIGHT/$HIGH^2|bc`


if [ $BMI -le 18 ];then

   echo"太瘦了,多吃点!"

elif [ $BMI -lt 24 ];then

   echo"身材很棒!"

else

   echo"太胖了,注意节食,加强运动!"

fi

 


2 case 条件判断语句

2.1 格式

case: case WORD in [PATTERN [| PATTERN]...) COMMANDS ;;]... esac

bash

case 变量引用 in

PAT1)

   分支1

   ;;

PAT2)

   分支2

   ;;

...

*)

   默认分支

   ;;

esac

case支持glob风格的通配符:

  • *   任意长度任意字符
  • ?   任意单个字符
  • [ ] 指定范围内的任意单个字符
  • |   或者,如: a|b

范例:Yes or No

bash

[root@rocky01 ~]# cat yes-no.sh

#!/bin/bash


#******************************************************

# Author:          会不会有那么一天

# QQ:              791270697

# Date:            2022-08-08

# FileName:        yes-no.sh

# Version:         1.0.0

# Description:     The test script

# BLOG:            https://www.cnblogs.com/Willoneday

#******************************************************


read -p "Do you agree(yes/no)? " INPUT


case$INPUTin

[yY]|[Yy][Ee][Ss])

   echo"You input is YES"

   ;;

[Nn]|[Nn][Oo])

   echo"You input is NO"

   ;;

*)

   echo"Input fales,please input yes or no!"

   ;;

esac

 


3 for 循环

3.1 格式1

for NAME [in WORDS ... ] ; do COMMANDS; done

bash

for 变量名 in 列表

do

   循环体

done

执行机制

  • 依次将列表中的元素赋值给“变量名”; 每次赋值后即执行一次循环体; 直到列表中的元素耗尽,循环结束
  • 如果省略 [in WORDS ... ] ,此时使用位置参数变量 in "$@

3.2 格式2

for ((: for (( exp1; exp2; exp3 )); do COMMANDS; done

bash

for((控制变量初始化;条件判断表达式;控制变量的修正表达式))

do

   循环体

done

 

范例:100以内的奇数和

bash

[root@rocky01 ~]# sum=0;for i in {1..100..2};do let sum+=i;done;echo sum=$sum

sum=2500

范例:使用位置参数变量

bash

[root@rocky01 ~]# cat for-sum.sh

#!/bin/bash


#******************************************************

# Author:          会不会有那么一天

# QQ:              791270697

# Date:            2022-08-08

# FileName:        for-sum.sh

# Version:         1.0.0

# Description:     The test script

# BLOG:            https://www.cnblogs.com/Willoneday

#******************************************************


sum=0

for i in$@;

do

   let sum+=i

done

echo sum=$sum

[root@rocky01 ~]#

[root@rocky01 ~]# bash for-sum.sh 1 2 3 10

sum=16

范例:将目录YYYY-MM-DD/中所有文件,移动到YYYY-MM/DD/下

bash

#1.先创建好环境

[root@rocky01 opt]# cat create_dir.sh

#!/bin/bash


PDIR=/data/test


for i in {1..365};

do

   #创建过去365天的目录

   DIR=`date -d "-$i day" +%F`

   mkdir -p $PDIR/$DIR


   #随机创建10个文件

   for j in {1..10};

   do

       touch $PDIR/$DIR/$RANDOM.log

   done

done

bash

#2.开始移动

[root@rocky01 opt]# cat mv_file.sh

#!/bin/bash


PDIR=/data/test

cd$PDIR || {  echo 无法进入 $PDIR;exit 1; }


for DIR in *;

do

   YYYY_MM=`echo$DIR |cut -d "-" -f 1,2`

   DD=`echo$DIR |cut -d "-" -f 3`

   [ -d $YYYY_MM/$DD ] || mkdir -p $YYYY_MM/$DD &> /dev/null

   mv $DIR/* $YYYY_MM/$DD

done

rm -rf $PDIR/*-*-*

范例:生成进度

bash

[root@rocky01 ~]# for((i = 0; i <= 100; ++i)); do printf "\e[4D%3d%%" $i;sleep 0.01s; done

100%

 


4 while 循环

4.1 格式

while COMMANDS; do COMMANDS; done

bash

while CONDITION;

do

   循环体

done

说明

  • 进入条件:CONDITION为 true
  • 退出条件:CONDITION为 false

bash

whiletrue;

do

   循环体

done

bash

while [ true ];

do

   循环体

done

 


4.2 while read (while的特殊用法)

while 循环的特殊用法,遍历文件或文本的每一行

4.2.1 格式

bash

whileread line; do

   循环体

done < /PATH/FILE


#依次读取/PATH/FILE文件中的每一行,且将行赋值给变量line

 

范例:查看可登录用户的用户名和UID

bash

[root@rocky01 ~]# cat while_read_passwd.sh

#!/bin/bash


#******************************************************

# Author:          会不会有那么一天

# QQ:              791270697

# Date:            2022-08-08

# FileName:        while_read_passwd.sh

# Version:         1.0.0

# Description:     The test script

# BLOG:            https://www.cnblogs.com/Willoneday

#******************************************************


whileread line;

do

   if [[ ! "$line" =~ /sbin/nologin$ ]];then

   echo$line | cut -d: -f1,3

   fi

done < /etc/passwd


[root@rocky01 ~]# bash while_read_passwd.sh

root:0

sync:5

shutdown:6

halt:7

rye:1000

Willoneday:1001

 


5 until 循环

5.1 格式

until COMMANDS; do COMMANDS; done

bash

until CONDITION;

do

   循环体

done

说明:

  • 进入条件: CONDITION 为false
  • 退出条件: CONDITION 为tru

执行条件和while相反,所以一般用while

 


6 continue 循环控制语句

continue [N]:提前结束第N层的本轮循环,而直接进入下一轮判断;最内层为第1层

6.1 格式

bash

while CONDITION1;

do

   CMD1

   ...

   if CONDITION2;then

       continue

   fi

   CMDn

   ...

done

范例:有无continue效果展示

bash

#有continue的效果

[root@rocky01 ~]# cat continue_yes.sh

#!/bin/bash


for((j=1;j<=3;j++))

do

   for((i=1;i<=3;i++));

   do

       [ $i -eq 2 ] && continue 2

       echo j=$j,i=$i

   done

done


[root@rocky01 ~]# bash continue_yes.sh

j=1,i=1

j=2,i=1

j=3,i=1

bash

#无continue的效果

[root@rocky01 ~]# cat continue_no.sh

#!/bin/bash


for((j=1;j<=3;j++))

do

   for((i=1;i<=3;i++));

   do

       echo j=$j,i=$i

   done

done


[root@rocky01 ~]# bash continue_no.sh

j=1,i=1

j=1,i=2

j=1,i=3

j=2,i=1

j=2,i=2

j=2,i=3

j=3,i=1

j=3,i=2

j=3,i=3

 


7 break 循环控制语句

break [N]:提前结束第N层整个循环,最内层为第1层

7.1 格式

bash

while CONDITION1;

do

   CMD1

   ...

   if CONDITION2;then

   break

   fi

   CMDn

   ...

done

范例:N层break效果展示

bash

#break第一层

[root@rocky01 ~]# cat break_yes1.sh

#!/bin/bash


for((j=1;j<=3;j++))

do

   for((i=1;i<=3;i++));

   do

       [ $i -eq 2 ] && break 1

       echo j=$j,i=$i

   done

done


[root@rocky01 ~]# bash break_yes1.sh

j=1,i=1

j=2,i=1

j=3,i=1

bash

#break第二层

[root@rocky01 ~]# cat break_yes2.sh

#!/bin/bash


for((j=1;j<=3;j++))

do

   for((i=1;i<=3;i++));

   do

       [ $i -eq 2 ] && break 2

       echo j=$j,i=$i

   done

done


[root@rocky01 ~]# bash break_yes2.sh

j=1,i=1

 


8 shift 循环控制命令

说明

shift [n] 用于将参量列表 list 左移指定次数,缺省为左移一次。

参量列表 list 一旦被移动,最左端的那个参数就从列表中删除。while 循环遍历位置参量列表时,常用到 shift

范例:检测用户是否存在

bash

[root@rocky01 ~]# cat shift_user.sh

#!/bin/bash


#******************************************************

# Author:          会不会有那么一天

# QQ:              791270697

# Date:            2022-08-08

# FileName:        shift_user.sh

# Version:         1.0.0

# Description:     The test script

# BLOG:            https://www.cnblogs.com/Willoneday

#******************************************************


#检测是否有参数

if [ $# -eq 0 ];then

   echo"Usage: $0 user1 user2 ..."

   exit

fi


#检测用户是否存在

while [ "$1" ];do

   if id $1 &> /dev/null;then

       echo"$1 is exist"

   else

       echo"$1 is no exist"

   fi

   shift

done


[root@rocky01 ~]# bash shift_user.sh

Usage: shift_user.sh user1 user2 ...

[root@rocky01 ~]# bash shift_user.sh root Willoneday qqq

root is exist

Willoneday is exist

qqq is no exist

 


9 select 循环与菜单

9.1 格式

select NAME [in WORDS ... ;] do COMMANDS; done

bash

select NAME in list;

do

   循环体命令

done

说明:

  • select 循环主要用于创建菜单,按数字顺序排列的菜单项显示在标准错误上,并显示 PS3 提示符,等待用户输入
  • 用户输入菜单列表中的某个数字,执行相应的命令
  • 用户输入菜单列表中的某个数字,会将对应的WORD值赋值给NAME变量
  • 用户输入被保存在内置变量 REPLY 中
  • select 是个无限循环,因此要用 break 命令退出循环,或用 exit 命令终止脚本。也可以按 ctrl+c退出循环
  • select 经常和 case 联合使用
  • 与 for 循环类似,可以省略 in list,此时使用位置参量

范例:菜单

bash

[root@rocky01 ~]# cat select.sh

#!/bin/bash


#******************************************************

# Author:          会不会有那么一天

# QQ:              791270697

# Date:            2022-08-08

# FileName:        select.sh

# Version:         1.0.0

# Description:     The test script

# BLOG:            https://www.cnblogs.com/Willoneday

#******************************************************


sum=0

PS3="请点菜(1-3): "

select MENU in 北京烤鸭 小龙虾 点菜结束;

do

   case$REPLYin

   1)

       echo$MENU 价格是 100

       let sum+=100

       ;;

   2)

       echo$MENU 价格是 88

       let sum+=88

       ;;

   3)

       echo"点菜结束,退出"

       break

       ;;

   *)

       echo"点菜错误,重新选择"

       ;;

   esac

done

echo"总价格是: $sum"


[root@rocky01 ~]# bash select.sh

1) 北京烤鸭

2) 小龙虾

3) 点菜结束

请点菜(1-3): 1

北京烤鸭 价格是 100

请点菜(1-3): 2

小龙虾 价格是 88

请点菜(1-3): 1

北京烤鸭 价格是 100

请点菜(1-3): 3

点菜结束,退出

总价格是: 288

 


四、function 函数

1 函数介绍

函数function是由若干条shell命令组成的语句块,实现代码重用和模块化编程

它与shell程序形式上是相似的,不同的是它不是一个单独的进程,不能独立运行,而是shell程序的一部分

 

函数和shell程序区别

  • Shell程序在子Shell中运行
  • 函数在当前Shell中运行。因此在当前Shell中,函数可对shell中变量进行修改

 


2 管理函数

函数由两部分组成:函数名和函数体

2.1 定义函数

bash

#语法一:

func_name () {

...函数体...

}


#语法二:

function func_name {

...函数体...

}


#语法三:

functionfunc_name () {

...函数体...

}

2.2 查看函数

bash

#查看当前已定义的函数名

declare -F

#查看当前已定义的函数定义

declare -f

#查看指定当前已定义的函数名

declare -f func_name

#查看当前已定义的函数名定义

declare -F func_name

2.3 删除函数

bash

unset func_name

 


3 函数调用

函数的调用方式

  • 可在交互式环境下定义函数
  • 可将函数放在脚本文件中作为它的一部分
  • 可放在只包含函数的单独文件中

调用:函数只有被调用才会执行,通过给定函数名调用函数,函数名出现的地方,会被自动替换为函数代码

函数的生命周期:被调用时创建,返回时终止

3.1 交互式环境调用函数

bash

[root@rocky01 ~]# NAME () {

> echo"Willoneday"

> }

[root@rocky01 ~]# NAME

Willoneday

3.2 脚本中定义及使用函数

函数在使用前必须定义,因此应将函数定义放在脚本开始部分,直至shell首次发现它后才能使用,调用函数仅使用其函数名即可

bash

[root@rocky01 ~]# cat NAME.sh

#!/bin/bash


NAME () {

echo"Willoneday"

}


NAME


[root@rocky01 ~]# bash NAME.sh

Willoneday

3.3 使用函数文件

可以将经常使用的函数存入一个单独的函数文件,然后将函数文件载入shell,再进行调用函数

函数文件名可任意选取,但最好与相关任务有某种联系,例如:functions

一旦函数文件载入shell,就可以在命令行或脚本中调用函数。可以使用delcare -f 或set 命令查看所有定义的函数,其输出列表包括已经载入shell的所有函数

若要改动函数,首先用unset命令从shell中删除函数。改动完毕后,再重新载入此文件

实现函数文件的过程:

  1. 创建函数文件,只存放函数的定义
  2. 在shell脚本或交互式shell中调用函数文件,格式如下:

.  filename
source  filename

bash

[root@rocky01 ~]# NAME

-bash: NAME: command not found

[root@rocky01 ~]# . NAME.sh

Willoneday

[root@rocky01 ~]# NAME

Willoneday

 


4 函数参数

函数可以接受参数:

  • 传递参数给函数:在函数名后面以空白分隔给定参数列表即可,如:testfunc arg1 arg2 ...
  • 在函数体中当中,可使用$1, $2, ...调用这些参数;还可以使用$@, $*, $#等特殊变量

bash

[root@rocky01 ~]# cat NAME.sh

#!/bin/bash


NAME () {

echo"your name:$@"

}


[ $# -eq 0 ] && echo"Usage: $0 name1 name2..." && exit


NAME $@


[root@rocky01 ~]# bash NAME.sh

Usage: NAME.sh name1 name2...

[root@rocky01 ~]# bash NAME.sh Willoneday Rye

your name:Willoneday Rye

 


5 函数变量

变量作用域:

  • 普通变量:只在当前shell进程有效,为执行脚本会启动专用子shell进程;因此,本地变量的作用范围是当前shell脚本程序文件,包括脚本中的函数
  • 环境变量:当前shell和子shell有效
  • 本地变量:函数的生命周期;函数结束时变量被自动销毁

注意:

  • 如果函数中定义了普通变量,且名称和局部变量相同,则使用本地变量
  • 由于普通变量和局部变量会冲突,建议在函数中只使用本地变量

函数中定义本地变量:

local  NAME=VALUE

 


6 函数递归

函数递归:函数直接或间接调用自身,注意递归层数,可能会陷入死循环

递归特点:

  • 函数内部自已调用自已
  • 必须有结束函数的出口语句,防止死循环

范例:fork炸弹

bash

[root@rocky01 ~]# :(){ :|:& };:


#该函数":"会在后台创建大量的子进程,将资源耗尽

 


五、脚本相关工具

1 trap 信号捕捉

trap 命令可以捕捉信号,修改信号原来的功能,实现自定义功能

bash

#进程收到系统发出的指定信号后,将执行自定义指令,而不会执行原操作

trap'触发指令' 信号


#忽略信号的操作

trap'' 信号

 

#恢复原信号的操作

trap'-' 信号


#列出自定义信号操作

trap -p


#当脚本退出时,执行finish函数

trap finish EXIT

bash

[root@rocky01 ~]# trap -l

1) SIGHUP  2) SIGINT  3) SIGQUIT  4) SIGILL  5) SIGTRAP

6) SIGABRT  7) SIGBUS  8) SIGFPE  9) SIGKILL 10) SIGUSR1

11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM

16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP

21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ

26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR

31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3

38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8

43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13

48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12

53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7

58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2

63) SIGRTMAX-1 64) SIGRTMAX

范例:自定义trap信号

bash

[root@rocky01 ~]# cat trap_test.sh

#!/bin/bash


#信号可以是名字或编号(trap -l)

#trap "echo 脚本执行中无法结束" 2

trap"echo 脚本执行中无法结束" INT


for i in {1..5}

do

   echo"${i}"

   sleep 2

done


[root@rocky01 ~]# bash trap_test.sh

1

^C脚本执行中无法结束

2

3

4

5

范例:finish函数

bash

[root@rocky01 ~]# cat trap_exit.sh

#!/bin/bash


finish () {

   echo finish-`date +%F`| tee -a /root/finish.log

}


trap finish exit

whiletrue;

do

   echo running

   sleep 2

done


[root@rocky01 ~]# bash trap_exit.sh

running

running

running

^Cfinish-2022-08-09


[root@rocky01 ~]# cat finish.log

finish-2022-08-09

 


2 mktemp 创建临时文件

mktemp 命令用于创建并显示临时文件,可避免冲突

2.1 格式

bash

mktemp [OPTION]... [TEMPLATE]

说明:TEMPLATE: filenameXXX,X至少要出现三个

常见选项:

  • -d #创建临时目录
  • -p DIR或--tmpdir=DIR   #指明临时文件所存放目录位置

范例:文件垃圾箱

bash

#方法1:脚本实现

[root@rocky01 ~]# cat /data/scripts/rm.sh

#!/bin/bash

DIR=`mktemp -d /tmp/trash-$(date +%F_%H-%M-%S)XXXXXX`

mv $* $DIR

echo $* is move to $DIR


[root@rocky01 ~]# alias rm=/data/scripts/rm.sh


#方法2:函数实现

[root@rocky01 ~]# function rm () { local trash=`mktemp -d /tmp/trashXXXX`;mv $* $trash; }

 


3 install 安装复制文件

install 功能相当于cp,chmod,chown,chgrp ,mkdir 等相关工具的集合

install 命令格式

bash

install [OPTION]... [-T] SOURCE DEST 单文件

install [OPTION]... SOURCE... DIRECTORY

install [OPTION]... -t DIRECTORY SOURCE...

install [OPTION]... -d DIRECTORY... #创建空目录

选项

bash

-m MODE,默认755

-o OWNER

-g GROUP

-d DIRNAME 目录

 


4 expect 交互式转化批处理工具

expect 是由Don Libes基于 Tcl( Tool Command Language )语言开发的,主要应用于自动化交互式操作的场景

4.1 语法

expect [选项] [ -c cmds ] [ [ -[f|b] ] cmdfile ] [ args ]

常见选项:

  • -c:从命令行执行expect脚本,默认expect是交互地执行的
  • -d:可以调试信息

4.2 expect中相关命令

  • spawn 启动新的进程
  • expect 从进程接收字符串
  • send 用于向进程发送字符串
  • interact 允许用户交互
  • exp_continue 匹配多个字符串在执行动作后加此命令

4.3 单一分支模式语法

bash

[root@rocky01 ~]# expect

expect1.1> expect "hi" {send "You said hi\n"}

hi

You said hi

4.4 多分支模式语法

bash

#写法一

[root@rocky01 ~]# expect

expect1.1> expect "hi" { send "You said hi\n" } "hehe" { send "Hehe yourself\n" } "bye" { send "Good bye\n" }

hi

You said hi

expect1.2> expect "hi" { send "You said hi\n" } "hehe" { send "Hehe yourself\n" } "bye" { send "Good bye\n" }

hehe

Hehe yourself

expect1.3> expect "hi" { send "You said hi\n" } "hehe" { send "Hehe yourself\n" } "bye" { send "Good bye\n" }

bye

Good bye


#写法二

expect {

   "hi" { send "You said hi\n"}

   "hehe" { send "Hehe yourself\n"}

   "bye" { send  " Good bye\n"} }

范例:expect 变量

bash

[root@rocky01 ~]# cat expect1

#!/usr/bin/expect

set ip 10.0.0.129

set user root

set password 000000

#10秒超时时间

set timeout 10


spawn ssh $user@$ip

expect {

   "yes/no" { send "yes\n";exp_continue }

   "password" { send "$password\n" }

}

interact

范例:expect 位置参数

bash

[root@rocky01 ~]# cat expect2

#!/usr/bin/expect

set ip [lindex $argv 0]

set user [lindex $argv 1]

set password [lindex $argv 2]


spawn ssh $user@$ip

expect {

   "yes/no" { send "yes\n";exp_continue }

   "password" { send "$password\n" }

}

interact


[root@rocky01 ~]# expect expect2 10.0.0.129 root 000000

spawn ssh root@10.0.0.129

root@10.0.0.129's password:

Last login: Tue Aug  9 21:22:01 2022 from 10.0.0.128

范例:shell脚本调用 expect

bash

[root@rocky01 ~]# cat expect3.sh

#!/bin/bash


#******************************************************

# Author:          会不会有那么一天

# QQ:              791270697

# Date:            2022-08-09

# FileName:        expect3.sh

# Version:         1.0.0

# Description:     The test script

# BLOG:            https://www.cnblogs.com/Willoneday

#******************************************************


NET=10.0.0

user=root

password=000000

IPLIST="

4

13

17

"


for ID in$IPLIST;

do

   ip=$NET.$ID

   expect <<EOF

   set timeout 20


   spawn ssh $user@$ip

   expect {

       "yes/no" { send "yes\n";exp_continue }

       "password" { send "$password\n" }

   }


   expect "#" { send "sed -i 's/SELINUX=.*/SELINUX=disabled/g' /etc/selinux/config\n" }

   expect "#" { send "setenforce 0\n" }

   expect "#" { send "exit\n" }

   expect eof

EOF

done

 


六、array 数组

1 数组介绍

数组名和索引

  • 索引的编号从0开始,属于数值索引
  • 索引可支持使用自定义的格式,而不仅是数值格式,即为关联索引,bash 4.0版本之后开始支持
  • bash的数组支持稀疏格式(索引不连续)

 


2 声明数组

  • #普通数组可以不事先声明,直接使用
  • declare -a ARRAY_NAME
  •  
  • #关联数组必须先声明,再使用
  • declare -A ARRAY_NAME

  注意:两者不可相互转换

 


3 数组赋值

3.1  一次只赋值一个元素

ARRAY_NAME[INDEX]=VALUE

bash

#范例

weekdays[0]="Sunday"

weekdays[4]="Thursday"

3.2 一次赋值全部元素

ARRAY_NAME=("VAL1" "VAL2" "VAL3" ...)

bash

#范例

title=("ceo""coo""cto")

num=({0..10})

3.3 只赋值特定元素

ARRAY_NAME=([0]="VAL1" [3]="VAL2" ...)

 


4 显示数组

declare -a

 


5 引用数组

5.1 引用特定的数组元素

${ARRAY_NAME[INDEX]}

#如果省略[INDEX]表示引用下标为0的元素

bash

#范例

[root@rocky01 ~]# declare -a title=([0]="ceo" [1]="coo" [2]="cto")

[root@rocky01 ~]# echo ${title}

ceo

[root@rocky01 ~]# echo ${title[0]}

ceo

[root@rocky01 ~]# echo ${title[1]}

coo

[root@rocky01 ~]# echo ${title[2]}

cto

5.2 引用数组所有元素

  • ${ARRAY_NAME[*]}
  • ${ARRAY_NAME[@]}

bash

#范例

[root@rocky01 ~]# echo ${title[@]}

ceo coo cto

[root@rocky01 ~]# echo ${title[*]}

ceo coo cto

5.3 数组的长度(元素个数)

  • ${#ARRAY_NAME[*]}
  • ${#ARRAY_NAME[@]}

bash

#范例

[root@rocky01 ~]# echo ${#title[*]}

3

5.4 数组的所有下标

  • ${!ARRAY_NAME[*]}
  • ${!ARRAY_NAME[@]}

bash

#范例

[root@rocky01 ~]# echo ${!title[*]}

0 1 2

 


6 删除数组

6.1 删除数组中的某元素

注:删除数组中的某元素,会导致稀疏格式

unset  ARRAY[INDEX]

6.2 删除整个数组

unset  ARRAY

 


7 数组数据处理

7.1 数组切片

  • ${ARRAY[@]:offset:number}
  • ${ARRAY[*]:offset:number}
  • offset    #要跳过的元素个数
  • number #要取出的元素个数

取偏移量之后的所有元素:

  • {ARRAY[@]:offset}
  • {ARRAY[*]:offset}

bash

#范例

[root@rocky01 ~]# num=({0..10})

[root@rocky01 ~]# echo ${num[*]:2:3}

2 3 4

[root@rocky01 ~]# echo ${num[*]:6}

6 7 8 9 10

7.2 向数组中追加元素

  • ARRAY[${#ARRAY[*]}]=value
  • ARRAY[${#ARRAY[@]}]=value

bash

[root@rocky01 ~]# num[${#num[@]}]=11

 


8 关联数组

declare -A ARRAY_NAME

ARRAY_NAME=([idx_name1]='val1' [idx_name2]='val2‘...)

注意:关联数组必须先声明再调用

bash

#范例

[root@rocky01 ~]# declare -A name

[root@rocky01 ~]# name[ceo]=xiaoming

[root@rocky01 ~]# name[cto]=xiaohong


[root@rocky01 ~]# echo ${name[ceo]}

xiaoming

[root@rocky01 ~]# echo ${name[cto]}

xiaohong

 


七、字符串处理

1 字符串切片

1.1 基于偏移量取字符串

  • #返回字符串变量var的字符的长度,一个汉字算一个字符
    ${#var}
  • #返回字符串变量var中从第offset个字符后(不包括第offset个字符)的字符开始,到最后的部分,
    offset的取值在0 到 ${#var}-1 之间(bash4.2后,允许为负值)
    ${var:offset}
  • #返回字符串变量var中从第offset个字符后(不包括第offset个字符)的字符开始,长度为number的部分
    ${var:offset:number}
  • #取字符串的最右侧几个字符,取字符串的最右侧几个字符, 注意:冒号后必须有一空白字符
    ${var: -length}
  • #从最左侧跳过offset字符,一直向右取到距离最右侧lengh个字符之前的内容,即:掐头去尾
    ${var:offset:-length}
  • #先从最右侧向左取到length个字符开始,再向右取到距离最右侧offset个字符之间的内容,注意:-length前空格,并且length必须大于offset
    ${var: -length:-offset}

bash

#范例

[root@rocky01 ~]# str=love中国

[root@rocky01 ~]# echo ${#str}

6

[root@rocky01 ~]# echo ${str:4}

中国

[root@rocky01 ~]# echo ${str:2:2}

ve

1.2 基于模式取子串

1.2.1 自左而右

  • #其中word可以是指定的任意字符,自左而右,查找var变量所存储的字符串中,第一次出现的word, 删除字符串开头至第一次出现word字符串(含)之间的所有字符,即懒惰模式,以第一个word为界删左留右
    ${var#*word}
  • #从var变量的值中删除以word开头的部分
    ${var#word}
  • #同上,贪婪模式,不同的是,删除的是字符串开头至最后一次由word指定的字符之间的所有内容,即贪婪模式,以最后一个word为界删左留右
    ${var##*word}
    ${var##word}

bash

#范例

[root@rocky01 ~]# url=https://www.cnblogs.com/Willoneday

[root@rocky01 ~]# echo ${url#*/}

/www.cnblogs.com/Willoneday

[root@rocky01 ~]# echo ${url##*/}

Willoneday

1.2.2 自右而左

  • #其中word可以是指定的任意字符,功能:自右而左,查找var变量所存储的字符串中,第一次出现的word, 删除字符串最后一个字符向左至第一次出现word字符串(含)之间的所有字符,即懒惰模式,以从右向左的第一个word为界删右留左
    ${var%word*}
    ${var%word}
  • #同上,只不过删除字符串最右侧的字符向左至最后一次出现word字符之间的所有字符,即贪婪模式,以从右向左的最后一个word为界删右留左
    ${var%%word*}
    ${var%%word}

bash

#范例

[root@rocky01 ~]# url=https://www.cnblogs.com/Willoneday

[root@rocky01 ~]# echo ${url%/*}

https://www.cnblogs.com

 


2 查找替换

  • #查找var所表示的字符串中,第一次被pattern所匹配到的字符串,以substr替换之
    ${var/pattern/substr}
  • #查找var所表示的字符串中,所有能被pattern所匹配到的字符串,以substr替换之
    ${var//pattern/substr}
  • #查找var所表示的字符串中,行首被pattern所匹配到的字符串,以substr替换之
    ${var/#pattern/substr}
  • #查找var所表示的字符串中,行尾被pattern所匹配到的字符串,以substr替换之
    ${var/%pattern/substr}

bash

#范例

[root@rocky01 ~]# name=Willoneday123123

[root@rocky01 ~]# echo ${name/123/456}

Willoneday456123

[root@rocky01 ~]# echo ${name//123/456}

Willoneday456456

bash

#范例:查找并删除

[root@rocky01 ~]# name=Willoneday123

[root@rocky01 ~]# echo ${name/123}

Willoneday123

[root@rocky01 ~]# echo ${name//123}

Willoneday

 


3 字符大小写转换

  • #把var中的所有小写字母转换为大写
    ${var^^}
  • #把var中的所有大写字母转换为小写
    ${var,,}

bash

#范例

[root@rocky01 ~]# url=https://www.cnblogs.com/Willoneday

[root@rocky01 ~]# echo ${url^^}

HTTPS://WWW.CNBLOGS.COM/WILLONEDAY

 


八、高级变量

1 高级变量赋值

bash

#范例

[root@rocky01 ~]# title=ceo

[root@rocky01 ~]# name=${title-Willoneday}

[root@rocky01 ~]# echo $name

ceo

[root@rocky01 ~]#

[root@rocky01 ~]# title=""

[root@rocky01 ~]# name=${title-Willoneday}

[root@rocky01 ~]# echo $name


[root@rocky01 ~]#

[root@rocky01 ~]# unset title

[root@rocky01 ~]# name=${title-Willoneday}

[root@rocky01 ~]# echo $name

Willoneday

 


2 有类型变量

Shell变量一般是无类型的,但是bash Shell提供了declare和typeset两个命令用于指定变量的类型,两个命令是等价的

declare [选项] 变量名

选项:

  • -r 声明或显示只读变量
  • -i 将变量定义为整型数
  • -a 将变量定义为数组
  • -A 将变量定义为关联数组
  • -f 显示已定义的所有函数名及其内容
  • -F 仅显示已定义的所有函数名
  • -x 声明或显示环境变量和函数,相当于export
  • -l 声明变量为小写字母 declare -l var=UPPER
  • -u 声明变量为大写字母 declare -u var=lower

 


3 变量间接引用

3.1 eval命令

eval命令将会首先扫描命令行进行所有的置换,然后再执行该命令。该命令适用于那些一次扫描无法实现其功能的变量,该命令对变量进行两次扫描

bash

#范例

[root@rocky01 ~]# x=y

[root@rocky01 ~]# y=z

[root@rocky01 ~]# echo $x

y

[root@rocky01 ~]# echo \$$x

$y

[root@rocky01 ~]# eval echo \$$x

z

3.2 间接变量引用

如果第一个变量的值是第二个变量的名字,从第一个变量引用第二个变量的值就称为间接变量引用variable1的值是variable2,而variable2又是变量名,variable2的值为value,间接变量引用是指通过variable1获得变量值value的行为

bash Shell提供了两种格式实现间接变量引用

  • 方法1 #变量赋值
    eval tempvar=\$$variable1
    #显示值
    eval echo \$$variable1
    eval echo '$'$variable1
  • 方法2 #变量赋值
    tempvar=${!variable1}
    #显示值
    echo ${!variable1}

范例:${!var}

bash

[root@rocky01 ~]# cat test.sh

#/bin/bash

var() {

   local var="$1"

   echo"${!var}"

}


var 1 a

var 2 a b

var 3 a b c


[root@rocky01 ~]# bash test.sh

1

a

b

 

目录
相关文章
|
1月前
|
Shell
一个用于添加/删除定时任务的shell脚本
一个用于添加/删除定时任务的shell脚本
79 1
|
22天前
|
Shell Linux 测试技术
6种方法打造出色的Shell脚本
6种方法打造出色的Shell脚本
45 2
6种方法打造出色的Shell脚本
|
8天前
|
XML JSON 监控
Shell脚本要点和难点以及具体应用和优缺点介绍
Shell脚本在系统管理和自动化任务中扮演着重要角色。尽管存在调试困难、可读性差等问题,但其简洁高效、易于学习和强大的功能使其在许多场景中不可或缺。通过掌握Shell脚本的基本语法、常用命令和函数,并了解其优缺点,开发者可以编写出高效的脚本来完成各种任务,提高工作效率。希望本文能为您在Shell脚本编写和应用中提供有价值的参考和指导。
29 1
|
13天前
|
Ubuntu Shell 开发工具
ubuntu/debian shell 脚本自动配置 gitea git 仓库
这是一个自动配置 Gitea Git 仓库的 Shell 脚本,支持 Ubuntu 20+ 和 Debian 12+ 系统。脚本会创建必要的目录、下载并安装 Gitea,创建 Gitea 用户和服务,确保 Gitea 在系统启动时自动运行。用户可以选择从官方或小绿叶技术博客下载安装包。
35 2
|
27天前
|
监控 网络协议 Shell
ip和ip网段攻击拦截系统-绿叶结界防火墙系统shell脚本
这是一个名为“小绿叶技术博客扫段攻击拦截系统”的Bash脚本,用于监控和拦截TCP攻击。通过抓取网络数据包监控可疑IP,并利用iptables和firewalld防火墙规则对这些IP进行拦截。同时,该系统能够查询数据库中的白名单,确保合法IP不受影响。此外,它还具备日志记录功能,以便于后续分析和审计。
47 6
|
23天前
|
运维 监控 Shell
深入理解Linux系统下的Shell脚本编程
【10月更文挑战第24天】本文将深入浅出地介绍Linux系统中Shell脚本的基础知识和实用技巧,帮助读者从零开始学习编写Shell脚本。通过本文的学习,你将能够掌握Shell脚本的基本语法、变量使用、流程控制以及函数定义等核心概念,并学会如何将这些知识应用于实际问题解决中。文章还将展示几个实用的Shell脚本例子,以加深对知识点的理解和应用。无论你是运维人员还是软件开发者,这篇文章都将为你提供强大的Linux自动化工具。
|
1月前
|
监控 Unix Shell
shell脚本编程学习
【10月更文挑战第1天】shell脚本编程
67 12
|
1月前
|
存储 运维 监控
自动化运维:使用Shell脚本简化日常任务
【9月更文挑战第35天】在IT运维的日常工作中,重复性的任务往往消耗大量的时间。本文将介绍如何通过编写简单的Shell脚本来自动化这些日常任务,从而提升效率。我们将一起探索Shell脚本的基础语法,并通过实际案例展示如何应用这些知识来创建有用的自动化工具。无论你是新手还是有一定经验的运维人员,这篇文章都会为你提供新的视角和技巧,让你的工作更加轻松。
55 2
|
2月前
|
Shell
shell脚本变量 $name ${name}啥区别
shell脚本变量 $name ${name}啥区别
|
2月前
|
人工智能 监控 Shell
常用的 55 个 Linux Shell 脚本(包括基础案例、文件操作、实用工具、图形化、sed、gawk)
这篇文章提供了55个常用的Linux Shell脚本实例,涵盖基础案例、文件操作、实用工具、图形化界面及sed、gawk的使用。
499 2