shell 脚本 - 学习

一、Shell alias:给命令创建别名

alisa 用来给命令创建一个别名。若直接输入该命令且不带任何参数,则列出当前 Shell 进程中使用了哪些别名。现在你应该能理解类似ll这样的命令为什么与ls -l的效果是一样的吧。

下面让我们来看一下有哪些命令被默认创建了别名:

[root@hecs-x-medium-2-linux-20211111093159 ~]# alias
alias cp='cp -i'
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
alias grep='grep --color=auto'
alias l.='ls -d .* --color=auto'
alias ll='ls -l --color=auto'
alias ls='ls --color=auto'
alias mv='mv -i'
alias rm='rm -i'
alias which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'
  • 使用 alias 命令自定义别名

使用 alias 命令自定义别名的语法格式为:

alias new_name='command'

比如,一般的关机命令是shutdown-h now,写起来比较长,这时可以重新定义一个关机命令,以后就方便多了。

alias myShutdown='shutdown -h now'

# 调用
[root@hecs-x-medium-2-linux-20211111093159 ~]# myShutdown

再如,通过 date 命令可以获得当前的 UNIX 时间戳,具体写法为date +%s,如果你嫌弃它太长或者不容易记住,那可以给它定义一个别名。

alias timestamp='date +%s'

例子:

[root@hecs-x-medium-2-linux-20211111093159 ~]# alias timestamp='date +%s'
[root@hecs-x-medium-2-linux-20211111093159 ~]# timestamp
1637581091

别名只是临时的

  1. 在代码中使用 alias 命令定义的别名只能在当前 Shell 进程中使用,在子进程和其它进程中都不能使用。当前 Shell 进程结束后,别名也随之消失。
  2. 要想让别名对所有的 Shell 进程都有效,就得把别名写入 Shell 配置文件。Shell 进程每次启动时都会执行配置文件中的代码做一些初始化工作,将别名放在配置文件中,那么每次启动进程都会定义这个别名。

二、Shell unalias命令:删除别名

使用 unalias 内建命令可以删除当前 Shell 进程中的别名。unalias 有两种使用方法:

  • 第一种用法是在命令后跟上某个命令的别名,用于删除指定的别名。
  • 第二种用法是在命令后接-a参数,删除当前 Shell 进程中所有的别名。

同样,这两种方法都是在当前 Shell 进程中生效的。要想永久删除配置文件中定义的别名,只能进入该文件手动删除。

[root@hecs-x-medium-2-linux-20211111093159 ~]# unalias timestamp
[root@hecs-x-medium-2-linux-20211111093159 ~]# timestamp
-bash: timestamp: command not found

三、Shell echo命令:输出字符串

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

#!/bin/bash
name="zhangsan"
password="123456"
echo "${name}的密码是:${password}"

运行结果:

[root@hecs-x-medium-2-linux-20211111093159 ~]# bash test.sh 
zhangsan的密码是:123456
  • 不换行

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

#!/bin/bash
name="Tom"
age=20
height=175
weight=62
echo -n "${name} is ${age} years old, "
echo -n "${height}cm in height "
echo "and ${weight}kg in weight."
echo "Thank you!"

运行结果:

[root@hecs-x-medium-2-linux-20211111093159 ~]# bash test.sh 
Tom is 20 years old, 175cm in height and 62kg in weight.
Thank you!
  • 输出转义字符

默认情况下,echo 不会解析以反斜杠\开头的转义字符。比如,\n表示换行,echo 默认会将它作为普通字符对待。请看下面的例子:

[root@hecs-x-medium-2-linux-20211111093159 ~]# echo "hello \nworld"
hello \nworld

我们可以添加-e参数来让 echo 命令解析转义字符。例如:

[root@hecs-x-medium-2-linux-20211111093159 ~]# echo -e "hello \nworld"
hello 
world
  • \c 转义字符

有了-e参数,我们也可以使用转义字符\c来强制 echo 命令不换行了。请看下面的例子:

#!/bin/bash
name="Tom"
age=20
height=175
weight=62
echo -e "${name} is ${age} years old, \c"
echo -e "${height}cm in height \c"
echo "and ${weight}kg in weight."
echo "Thank you!"

运行结果:

[root@hecs-x-medium-2-linux-20211111093159 ~]# bash test.sh 
Tom is 20 years old, 175cm in height and 62kg in weight.
Thank you!

四、Shell read命令:读取从键盘输入的数据

read 是 Shell 内置命令,用来从标准输入中读取数据并赋值给变量。如果没有进行重定向,默认就是从键盘读取用户输入的数据;如果进行了重定向,那么可以从文件中读取数据。
read 命令的用法为:

read [-options] [variables]
# options表示选项,如下表所示;variables表示用来存储数据的变量,可以有一个,也可以有多个。
# options和variables都是可选的,如果没有提供变量名,那么读取的数据将存放到环境变量 REPLY 中。
选项说明
-a array把读取的数据赋值给数组 array,从下标 0 开始。
-d delimiter用字符串 delimiter 指定读取结束的位置,而不是一个换行符(读取到的数据不包括 delimiter)。
-e在获取用户输入的时候,对功能键进行编码转换,不会直接显式功能键对应的字符。
-n num读取 num 个字符,而不是整行字符。
-p prompt显示提示信息,提示内容为 prompt。
-r原样读取(Raw mode),不把反斜杠字符解释为转义字符。
-s静默模式(Silent mode),不会在屏幕上显示输入的字符。当输入密码和其它确认信息的时候,这是很有必要的。
-t seconds设置超时时间,单位为秒。如果用户没有在指定时间内输入完成,那么 read 将会返回一个非 0 的退出状态,表示读取失败。
-u fd使用文件描述符 fd 作为输入源,而不是标准输入,类似于重定向。

例子:

[root@hecs-x-medium-2-linux-20211111093159 ~]# read filename
hello.txt
[root@hecs-x-medium-2-linux-20211111093159 ~]# echo $filename
hello.txt

五、Shell exit命令:退出当前进程

exit是一个 Shell 内置命令,用来退出当前 Shell 进程,并返回一个退出状态;使用$?可以接收这个退出状态。

exit命令可以接受一个整数值作为参数,代表退出状态。如果不指定,默认状态值是 0。一般情况下,退出状态为 0 表示成功,退出状态为非 0 表示执行失败(出错)了。
exit 退出状态只能是一个介于 0~255 之间的整数,其中只有 0 表示成功,其它值都表示失败。

Shell 进程执行出错时,可以根据退出状态来判断具体出现了什么错误,比如打开一个文件时,我们可以指定 1 表示文件不存在,2 表示文件没有读取权限,3 表示文件类型不对。

#!/bin/bash
echo "befor exit"
exit 8
echo "after exit"

运行结果:

[root@hecs-x-medium-2-linux-20211111093159 ~]# bash test.sh 
befor exit

注意,exit表示退出当前 Shell 进程,我们必须在新进程中运行 test.sh,否则当前 Shell 会话(终端窗口)会被关闭,我们就无法看到输出结果了。【前一篇博客说到 bash 命令是新开一个进程执行的】

七、Shell declare和typeset命令:设置变量属性

declaretypeset都是 Shell 内建命令,它们的用法相同,都用来设置变量的属性。不过 typeset 已经被弃用了,建议使用 declare 代替。

declare 命令的用法如下所示:

declare [+/-] [aAfFgilprtux] [变量名=变量值]

其中,-表示设置属性,+表示取消属性,aAfFgilprtux都是具体的选项,它们的含义如下表所示:

选项含义
-f [name]列出之前由用户在脚本中定义的函数名称和函数体。
-F [name]仅列出自定义函数名称。
-g name在 Shell 函数内部创建全局变量。
-p [name]显示指定变量的属性和值。
-a name声明变量为普通数组。
-A name声明变量为关联数组(支持索引下标为字符串)。
-i name将变量定义为整数型。
-r name[=value]将变量定义为只读(不可修改和删除),等价于 readonly name。
-x name[=value]将变量设置为环境变量,等价于 export name[=value]。

【实例1】将变量声明为整数并进行计算。

#!/bin/bash
declare -i m n ret  #将多个变量声明为整数
m=10
n=30
ret=$m+$n
echo $ret

运行结果:

[root@hecs-x-medium-2-linux-20211111093159 ~]# bash test.sh 
40

八、Shell 数学计算(算术运算,加减乘除运算)

如果要执行算术运算(数学计算),就离不开各种运算符号,和其他编程语言类似,Shell 也有很多算术运算符,下面就给大家介绍一下常见的 Shell 算术运算符,如下表所示。

算术运算符说明/含义
+、-加法(或正号)、减法(或负号)
*、/、%乘法、除法、取余(取模)
**幂运算
++、–自增和自减,可以放在变量的前面也可以放在变量的后面
!、&&、||逻辑非(取反)、逻辑与(and)、逻辑或(or)
<、<=、>、>=比较符号(小于、小于等于、大于、大于等于)
==、!=、=比较符号(相等、不相等;对于字符串,= 也可以表示相当于)
<<、>>向左移位、向右移位
~、|、 &、^按位取反、按位或、按位与、按位异或
=、+=、-=、*=、/=、%=赋值运算符,例如 a+=1 相当于 a=a+1,a-=1 相当于 a=a-1

Shell 中常用的六种数学计算方式

运算操作符/运算命令说明
(( ))用于整数运算,效率很高,推荐使用
let用于整数运算,和 (()) 类似。
$[]用于整数运算,不如 (()) 灵活。
expr可用于整数运算,也可以处理字符串。比较麻烦,需要注意各种细节,不推荐使用。
bcLinux下的一个计算器程序,可以处理整数和小数。Shell 本身只支持整数运算,想计算小数就得使用 bc 这个外部的计算器。
declare -i将变量定义为整数,然后再进行数学运算时就不会被当做字符串了。功能有限,仅支持最基本的数学运算(加减乘除和取余),不支持逻辑运算、自增自减等,所以在实际开发中很少使用。

1、Shell (()):对整数进行数学运算

双小括号 (( )) 是 Bash Shell 中专门用来进行整数运算的命令,它的效率很高,写法灵活,是企业运维中常用的运算命令。

注意:(( )) 只能进行整数运算,不能对小数(浮点数)或者字符串进行运算。后续讲到的 bc 命令可以用于小数运算。

双小括号 (( )) 的语法格式为:((表达式))

(( )) 的用法如下:

运算操作符/运算命令说明
((a=10+66))((b=a-15)) ((c=a+b))这种写法可以在计算完成后给变量赋值。以 ((b=a-15)) 为例,即将 a-15 的运算结果赋值给变量 c。 注意,使用变量时不用加$前缀,(( )) 会自动解析变量名。
a= ( ( 10 + 66 ) b = ((10+66) b= ((10+66)b=((a-15)) c=$((a+b))可以在 (( )) 前面加上$符号获取 (( )) 命令的执行结果,也即获取整个表达式的值。以 c= ( ( a + b ) ) 为 例 , 即 将 a + b 这 个 表 达 式 的 运 算 结 果 赋 值 给 变 量 c 。 注 意 , 类 似 c = ( ( a + b ) ) 这 样 的 写 法 是 错 误 的 , 不 加 ‘ ((a+b)) 为例,即将 a+b 这个表达式的运算结果赋值给变量 c。 注意,类似 c=((a+b)) 这样的写法是错误的,不加` ((a+b))a+bcc=((a+b))`就不能取得表达式的结果。
((a>7 && b==c))(( )) 也可以进行逻辑运算,在 if 语句中常会使用逻辑运算。
echo $((a+10))需要立即输出表达式的运算结果时,可以在 (( )) 前面加$符号。
((a=3+5, b=a+10))对多个表达式同时进行计算。

2、Shell let命令:对整数进行数学运算

给变量 i 加 8:

[root@hecs-x-medium-2-linux-20211111093159 ~]# i=2
[root@hecs-x-medium-2-linux-20211111093159 ~]# let i+=8
[root@hecs-x-medium-2-linux-20211111093159 ~]# echo $i
10

let i+=8 等同于 ((i+=8)),但后者效率更高。

let 后面可以跟多个表达式:

[root@hecs-x-medium-2-linux-20211111093159 ~]# a=10 b=32
[root@hecs-x-medium-2-linux-20211111093159 ~]# let a+=1 b-=2
[root@hecs-x-medium-2-linux-20211111093159 ~]# echo $a $b
11 30

3、Shell bc 命令【计算器命令】

一般情况下,我们使用不带任何参数的bc命令。
如果需要bc不输出提示信息,可以加上-q参数: bc -q

如果要使用强大的数学库,比如计算三角函数,需要加上-l参数:bc -l

因为 bc 本身是一个命令解释器,要退出它只要直接输入 quit 回车 或者 按 Ctrl+D 终止。

[root@hecs-x-medium-2-linux-20211111093159 ~]# echo 3 * 4 | bc
(standard_in) 1: syntax error
[root@hecs-x-medium-2-linux-20211111093159 ~]# echo "3 * 4" | bc   # 进行计算需要加上 ""
12
[root@hecs-x-medium-2-linux-20211111093159 ~]# echo "scale=7;355/113" | bc # scale 保留小数点后几位
3.1415929 
  • 进制转换
[root@hecs-x-medium-2-linux-20211111093159 ~]# echo "ibase=16; FFFF" | bc  # 16进制要大写 转 十进制
65535
[root@hecs-x-medium-2-linux-20211111093159 ~]# echo "obase=16; 1000" | bc # 十进制 转 十六进制
3E8
  • 写入文件计算
# test.bc 文件内的内容如下:
123*321
123/321
scale=4;123/321
[root@hecs-x-medium-2-linux-20211111093159 ~]# cat test.bc
123*321
123/321
scale=4;123/321
[root@hecs-x-medium-2-linux-20211111093159 ~]# cat test.bc | bc
39483
0
.3831
  • 计算三角形面积举例
#!/bin/bash 
echo -n "Enter base of a triangle : " 
read b 
echo -n "Enter height of a triangle : " 
read h 
area=$(echo "scale=2;(1/2) * $b * $h"|bc) 
echo "Area of a triangle is $area" 

测试结果:

[root@hecs-x-medium-2-linux-20211111093159 ~]# bash tangle.sh 
Enter base of a triangle : 60
Enter height of a triangle : 100
Area of a triangle is 3000.00
  • 使用数学库
[root@hecs-x-medium-2-linux-20211111093159 ~]# echo "scale=100; a(1)*4" | bc
Runtime error (func=(main), adr=11): Function a not defined.
[root@hecs-x-medium-2-linux-20211111093159 ~]# echo "scale=100; a(1)*4" | bc -l
3.141592653589793238462643383279502884197169399375105820974944592307\
8164062862089986280348253421170676

九、Shell test 指令

1、与文件检测相关的 test 选项

文件类型判断
选 项作 用
-b filename判断文件是否存在,并且是否为块设备文件。
-c filename判断文件是否存在,并且是否为字符设备文件。
-d filename判断文件是否存在,并且是否为目录文件。
-e filename判断文件是否存在。
-f filename判断文件是否存在,井且是否为普通文件。
-L filename判断文件是否存在,并且是否为符号链接文件。
-p filename判断文件是否存在,并且是否为管道文件。
-s filename判断文件是否存在,并且是否为非空。
-S filename判断该文件是否存在,并且是否为套接字文件。
文件权限判断
选 项作 用
-r filename判断文件是否存在,并且是否拥有读权限。
-w filename判断文件是否存在,并且是否拥有写权限。
-x filename判断文件是否存在,并且是否拥有执行权限。
-u filename判断文件是否存在,并且是否拥有 SUID 权限。
-g filename判断文件是否存在,并且是否拥有 SGID 权限。
-k filename判断该文件是否存在,并且是否拥有 SBIT 权限。
文件比较
选 项作 用
filename1 -nt filename2判断 filename1 的修改时间是否比 filename2 的新。
filename -ot filename2判断 filename1 的修改时间是否比 filename2 的旧。
filename1 -ef filename2判断 filename1 是否和 filename2 的 inode 号一致,可以理解为两个文件是否为同一个文件。这个判断用于判断硬链接是很好的方法

举例:

#!/bin/bash
read -p "输入内容输入文件名称:" filename
read -p "输入内容:" url
if test -w $filename && test -n $url;  # 判断 $filename 文件是否有写入权限 并且 $url 是否非空
then
    echo $url > $filename  # 将 $url 内容写入 $filename 文件内
    echo "写入成功"
else
    echo "写入失败"
fi

测试结果:

# 在 Shell 脚本文件所在的目录新建一个文本文件并命名为 log.txt,然后运行 Shell 脚本,运行结果为:
[root@hecs-x-medium-2-linux-20211111093159 ~]# bash file_test.sh 
输入内容输入文件:helloworld.sh
输入写入内容:log.txt
写入成功
[root@hecs-x-medium-2-linux-20211111093159 ~]# vim helloworld.sh 
log.txt

2、与数值比较相关的 test 选项

选 项作 用
num1 -eq num2判断 num1 是否和 num2 相等。
num1 -ne num2判断 num1 是否和 num2 不相等。
num1 -gt num2判断 num1 是否大于 num2 。
num1 -lt num2判断 num1 是否小于 num2。
num1 -ge num2判断 num1 是否大于等于 num2。
num1 -le num2判断 num1 是否小于等于 num2。

注意,test只能用来比较整数,小数相关的比较还得依赖 bc 命令【计算器命令】。

Shell test 数值比较举例:

#!/bin/bash
read a b
if test $a -eq $b;
then
    echo "两个数相等"
else
    echo "两个数不相等"
fi

测试结果:

[root@hecs-x-medium-2-linux-20211111093159 ~]# bash num_test.sh
10 20
两个数不相等
[root@hecs-x-medium-2-linux-20211111093159 ~]# bash num_test.sh
10 10
两个数相等

3、与字符串判断相关的 test 选项

选 项作 用
-z str判断字符串 str 是否为空。
-n str判断宇符串 str 是否为非空。
str1 = str2 str1 == str2===是等价的,都用来判断 str1 是否和 str2 相等。
str1 != str2判断 str1 是否和 str2 不相等。
str1 > str2判断 str1 是否大于 str2。\>>的转义字符,这样写是为了防止>被误认为成重定向运算符。
str1 < str2判断 str1 是否小于 str2。同样,\<也是转义字符。

举例:

#!/bin/bash
read str1
read str2
#检测字符串是否为空
if [ -z "$str1" ] || [ -z "$str2" ];
then
    echo "字符串不能为空"
    exit 0
fi
#比较字符串
if [ $str1 = $str2 ];
then
    echo "两个字符串相等"
else
    echo "两个字符串不相等"
fi

测试结果:

[root@hecs-x-medium-2-linux-20211111093159 ~]# bash touch.sh 
yyyd
jsjj
两个字符串不相等
[root@hecs-x-medium-2-linux-20211111093159 ~]# bash touch.sh 

jjj
字符串不能为空

4、与逻辑运算相关的 test 选项

选 项作 用
expression1 -a expression逻辑与,表达式 expression1 和 expression2 都成立,最终的结果才是成立的。
expression1 -o expression2逻辑或,表达式 expression1 和 expression2 有一个成立,最终的结果就成立。
!expression逻辑非,对 expression 进行取反。

例子:

#!/bin/bash
read str1
read str2
#检测字符串是否为空
if [ -z "$str1" -o -z "$str2" ];  #使用 -o 选项取代之前的 ||
then
    echo "字符串不能为空"
    exit 0
fi
#比较字符串
if [ $str1 = $str2 ];
then
    echo "两个字符串相等"
else
    echo "两个字符串不相等"
fi

测试结果:

[root@hecs-x-medium-2-linux-20211111093159 ~]# bash touch.sh 
yyyd
jsjj
两个字符串不相等
[root@hecs-x-medium-2-linux-20211111093159 ~]# bash touch.sh 

jjj
字符串不能为空

当你在 test 命令中使用变量时,我强烈建议将变量用双引号""包围起来。

注意点:

  1. test 命令比较奇葩,>、<、== 只能用来比较字符串,不能用来比较数字,比较数字需要使用 -eq、-gt 等选项;不管是比较字符串还是数字,test 都不支持 >= 和 <=。
  2. 对于整型数字的比较,我建议大家使用 (())

十、 Shell [[]] 关键字详解:检测某个条件是否成立

[[ ]]是 Shell 内置关键字,它和 test 命令 类似,也用来检测某个条件是否成立。

test 能做到的,[[ ]] 也能做到,而且 [[ ]] 做的更好;test 做不到的,[[ ]] 还能做到。可以认为 [[ ]] 是 test 的升级版,对细节进行了优化,并且扩展了一些功能。

  • [[ ]] 的用法为:[[ expression ]]

当 [[ ]] 判断 expression 成立时,退出状态为 0,否则为非 0 值。注意[[ ]]expression之间的空格,这两个空格是必须的,否则会导致语法错误。

[[ ]] 是 Shell 内置关键字,不是命令,在使用时没有给函数传递参数的过程,所以 test 命令的某些注意事项在 [[ ]] 中就不存在了,具体包括:

  • 不需要把变量名用双引号""包围起来,即使变量是空值,也不会出错。
  • 不需要、也不能对 >、< 进行转义,转义后会出错。
  • 注意,[[ ]] 剔除了 test 命令的-o-a选项,你只能使用 || 和 &&。

例子:

#!/bin/bash
read str1
read str2
if [[ -z $str1 ]] || [[ -z $str2 ]];  #不需要对变量名加双引号 也可以写成 [[ -z $str1 || -z $str2 ]] 
# 不可以写成 [[ -z "$str1" -o -z "$str2" ]] 
then
    echo "字符串不能为空"
elif [[ $str1 < $str2 ]];  #不需要也不能对 < 进行转义
then
    echo "str1 < str2"
else
    echo "str1 >= str2"
fi

测试结果:

[root@hecs-x-medium-2-linux-20211111093159 ~]# bash test.sh 
yysyd
hshd
str1 >= str2
[root@hecs-x-medium-2-linux-20211111093159 ~]# bash test.sh 

shahd
字符串不能为空
[root@hecs-x-medium-2-linux-20211111093159 ~]# bash test.sh 
yyy
yyyy
str1 < str2

总结:

test 或 [][[ ]]
[ -z “ s t r 1 " ] ∥ ∥ [ − z " str1" ] \|\| [ -z " str1"][z"str2” ][[ -z $str1 ]] || [[ -z $str2 ]]
[ -z “ s t r 1 " − o − z " str1" -o -z " str1"oz"str2” ][[ -z $str1 -o -z $str2 ]]×
[ -z $str1 || -z $str2 ]×[[ -z $str1 || -z $str2 ]]
  • [[ ]] 支持正则表达式

在 Shell [[ ]] 中,可以使用=~来检测字符串是否符合某个正则表达式,它的用法为:[[ str =~ regex ]]

#!/bin/bash
read tel
if [[ $tel =~ ^1[0-9]{10}$ ]];  
then
    echo "你输入的是手机号码"
else
    echo "你输入的不是手机号码"
fi

测试结果:

[root@hecs-x-medium-2-linux-20211111093159 ~]# bash test.sh 
2132172321
你输入的不是手机号码
[root@hecs-x-medium-2-linux-20211111093159 ~]# bash test.sh 
17787727711
你输入的是手机号码

^1[0-9]{10}$的说明:

  • ^匹配字符串的开头(一个位置);
  • [0-9]{10}匹配连续的十个数字;
  • $匹配字符串的末尾(一个位置);

最后:
[[ ]] 对数字的比较仍然不友好,所以我建议,以后大家使用 if 判断条件时,用 (()) 来处理整型数字,用 [[ ]] 来处理字符串或者文件。

文章内容参考:http://m.biancheng.net/view/942.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TyuIn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值