Q:shell脚本说明
shell 脚本是个纯文本文件,命令从上而下,一行一行地开始执行。shell 脚本拓展名为.sh
。shell 脚本第一行一定要为:
#!/bin/bash
Q:shell脚本语法(一个简单的示例脚本)
创建一个 test.sh 文件,里面输出 "Hello World"。
[root@FSM0 shell]# vim test.sh
[root@FSM0 shell]# cat test.sh
#!/bin/bash
echo Hello World
[root@FSM0 shell]# chmod +x test.sh
[root@FSM0 shell]# ./test.sh
Hello World
VRM01:/home/GalaX8800 # id registry
uid=1003(registry) gid=1001(fusioncompute) groups=1001(fusioncompute),10(wheel)
执行一个 shell 脚本
1、使用sh命令执行:(test.sh文件没有执行权限,无法直接执行,可以使用sh命令来执行)
[gandalf@VRM01 ~]$ ll
-rw-r----- 1 gandalf GalaX8800 12 May 10 14:02 test.sh
[gandalf@VRM01 ~]$ ./test.sh
-bash: ./test.sh: Permission denied
[gandalf@VRM01 ~]$ sh test.sh
test
2、直接执行:(需要给test.sh文件赋予可执行权限,就可以直接执行了)
[gandalf@VRM01 ~]$ chmod +x test.sh
[gandalf@VRM01 ~]$ ll test.sh
-rwxr-x--- 1 gandalf GalaX8800 12 May 10 14:02 test.sh
[gandalf@VRM01 ~]$ ./test.sh
test
变量赋值引用
1、变量赋值:一共有五种方式
1)直接赋值,格式为:变量名=变量值
[gandalf@VRM01 ~]$ id=123
[gandalf@VRM01 ~]$ echo ${id}
123
注:变量名或者变量值与=之间都不能有空格,否则会报错。
注:变量值如果有空格的话,可是用引号括起来(没有空格时,也可以用引号,效果一样,单引号双引号都行,成对即可)。
[gandalf@VRM01 ~]$ id =123
id: ‘=123’: no such user
[gandalf@VRM01 ~]$ id= 123
-bash: 123: command not found
[gandalf@VRM01 ~]$ id="i love you"
[gandalf@VRM01 ~]$ echo ${id}
i love you
[gandalf@VRM01 ~]$ id='you love me'
[gandalf@VRM01 ~]$ echo ${id}
you love me
2)Read命令读取,格式为:read 变量1 变量2
[gandalf@VRM01 ~]$ read name age
shenjl 18
[gandalf@VRM01 ~]$ echo ${name},${age}
shenjl,18
注:read命令是系统内置命令。
3)利用命令和输出结果赋值:将一个命令的输出结果当做变量的值。
[gandalf@VRM01 ~]$ cmd=$(date +%F)
[gandalf@VRM01 ~]$ echo ${cmd}
2022-12-28
2、只读变量:
语法:readonly 变量名,使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。
[gandalf@VRM01 ~]$ url="https://www.runoob.com"
[gandalf@VRM01 ~]$ readonly url
[gandalf@VRM01 ~]$ url="https://www.baidu.com"
-bash: url: readonly variable
[gandalf@VRM01 ~]$ echo ${url}
https://www.runoob.com
3、删除变量:
语法:unset 变量名,变量删除后不能再次使用,unset命令不能删除只读变量。
高级变量
1、替换子串:可以替换字符串中任意满足条件的子串
语法:
${string/substring/replacement} 仅仅替换第一次匹配
${string//substring/replacement} 替换所有的匹配
[gandalf@VRM01 ~]$ VERSION_PATH=123,456,789
[gandalf@VRM01 ~]$ echo ${VERSION_PATH//,/ } # 将所有的逗号','号替换成空格' '
123 456 789
[gandalf@VRM01 ~]$
交互式 shell
#!/bin/bash
echo "please input name and your age:"
read name age # 读取键盘输入字符串,赋值给变量 name 和 age
echo "your name:" $name ",your age: $age" # shell 脚本输出变量:$变量名
输出:
[root@FSM0 shell]# sh test.sh
please input name and your age:
shenjl 23
your name: shenjl ,your age: 23
数值计算
说明:shell 仅支持整型,数值计算使用$((表达式))
,示例:
#!/bin/bash
read -p "please input operand and number: " operand number # -p 后面跟提示信息,即在输入前打印提示信息
echo "$operand + $number = $(($operand+$number))"
echo "$operand - $number = $(($operand - $number))"
echo "$operand * $number = $(($operand * $number))"
divided=$(($operand/$number)) # 赋值等号间不能有空格
echo "$operand / $number = $divided"
输出:
[root@FSM0 shell]# sh test.sh
please input operand and number: 44 5
44 + 5 = 49
44 - 5 = 39
44 * 5 = 220
44 / 5 = 8
test命令
说明:test
命令用于查看文件是否存在、权限等信息,可以进行数值、字符和文件三方面的测试。
cmd1 && cmd2
#当 cmd1 执行完毕且正确,那么 cmd2 执行;当 cmd1 执行完毕且错误,那么 cmd2 不执行
cmd1 || cmd2
#当 cmd1 执行完毕且正确,那么 cmd2 不执行;当 cmd1 执行完毕且错误,那么 cmd2 执行
字符串测试:
#!/bin/bash
read -p "please input first string: " firstStr
read -p "please input second string: " secondStr
test $firstStr = $secondStr && echo "The two strings are the same" || echo "The two strings are not the same"
# test str1 = str2 :两个字符串相等则为真
输出:
[root@FSM0 shell]# sh test.sh
please input first string: shenjl
please input second string: shenjl
The two strings are the same
[root@FSM0 shell]# sh test.sh
please input first string: shenjl
please input second string: shenjl1111
The two strings are not the same
文件测试:
#!/bin/bash
read -p "please input file name: " filename
test -e $filename && echo "$filename exist" || echo "$filename non-existence"
# test -e :如果文件存在则为真
中括号判断符
字符串判断:
#!/bin/bash
read -p "please input first string: " firstStr
read -p "please input second string: " secondStr
[ "$firstStr" == "$secondStr" ] && echo "The two strings are the same" || echo "The two strings are not the same"
# 中括号两端内侧要加空格,内容建议加 "",否则有空格时会出现参数过多
[ "$firstStr" != "$secondStr" ] && echo "The two strings are not the same" || echo "The two strings are the same"
[ "$firstStr" = "$secondStr" ] && echo "The two strings are the same" || echo "The two strings are not the same"
echo firstStr: $firstStr
echo secondStr: $secondStr
== 可用于判断变量是否相等,= 除了可用于判断变量是否相等外,还可以表示赋值。
= 与 == 在 [ ] 中表示判断(字符串比较)时是等价的。
默认变量 $0~$n
说明:
$0
~$n
:表示 shell 脚本的执行参数,包括 shell 脚本执行命令本身,shell 脚本执行命令本身为$0
。
$0
:shell 脚本文件名称
$#
:表示所有脚本参数的个数。
$@
:表示除$0
外的所有参数。
$?
:表示上一条指令的返回值,成功是 0,不成功是 1。
$$
:表示 shell 本身的 PID(ProcessID),即当前进程的 PID。
举例:
#!/bin/bash
echo "The zero parameter :"$0
echo "The first parameter :"$1
echo "The second parameter:"$2
echo "The label of the last parameter:"$#
echo "All parameters :"$@
输出:
[root@FSM0 shell]# sh test.sh 01 02
The zero parameter :test.sh
The first parameter :01
The second parameter:02
The label of the last parameter:2
All parameters :01 02
$(( ))、$( )、``、${ }、''、""、()、(())、[]、[[]]、{}
1、$( )与``(反引号):返回括号中命令的结果。(命令替换)
说明:命令替换与变量替换差不多,都是用来重组命令行]的,先完成引号里的命令行,然后将其结果替换出来,再重组成新的命令行。
VRM01:~ # echo $(pwd)'/shenjl'
/root/shenjl
VRM01:~ # echo `pwd`'/shenjl'
/root/shenjl
VRM01:~ # echo today is $(date "+%Y-%m-%d")
today is 2022-12-28
注:两者效果相同,建议使用$(),因为反引号很容易与单引号混淆,另外就是在多层次的复合替换中,反引号必须要额外的跳脱处理(反斜线),$()更加的直观。$()的弊端是,并不是所有的类unix系统都支持这种方式,但反引号是肯定支持的。
举例:将cmd1执行结果作为cmd2参数,再将cmd2结果作为cmd3的参数
cmd3 $(cmd2 $(cmd1)) # 使用$()
cmd3 `cmd2 \`cmd1\`` # 使用``需要跳脱处理
2、${}:变量替换。
说明:一般情况下,$var与${var}是没有区别的,但是用${ }会比较精确的界定变量名称的范围,推荐使用。
[gandalf@VRM01 ~]$ echo ${PATH}
/usr/java/jre1.8.0_322/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/opt/galax/ge_backup/bin
[gandalf@VRM01 ~]$ echo $PATH
/usr/java/jre1.8.0_322/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/opt/galax/ge_backup/bin
3、双单引号'':
说明:在单引号中的所有的特殊符号,如$
和 "`"(反引号)都没有特殊含义。
4、双引号"":
说明:在双引号中特殊符号都没有特殊含义,但是$
、"`" 和 "\" 外,这三个符号是有特殊含义的,拥有“调用变量的值”、“引用命令”和“转义符"的特殊含义。
[gandalf@VRM01 ~]$ echo '$name' # 原样输出
$name
[gandalf@VRM01 ~]$ echo '${name}' # 原样输出
${name}
[gandalf@VRM01 ~]$ echo "${name}" # 变量替换输出
shenjl
5、单小括号():
1)命令组:括号中的命令将会新开一个子shell顺序执行,所以括号中的变量不能够被脚本余下的部分使用。括号中多个命令之间用分号隔开,最后一个命令可以没有分号,各命令和括号之间不必有空格。
2)命令替换:等同于[cmd,shell扫描一遍命令行,发现了(cmd)结构,便将 (cmd)中的cmd执行一次,得到其标准输出,再将此输出放到原来命令。有些shell不支持,如tcsh。
3)初始化数组:如:array=(a b c d)
条件判断-与或非
1、简单的条件判断:$$ 和 ||
# -a,-o,!
a="shen"
b="jin"
c="shen"
if [ ${a} = ${b} -a ${a} = ${c} ];then // 与,先计算${a} = ${b},${a} = ${c}之后,才比较[]是否为true
if [ ${a} = ${b} -o ${a} = ${c} ];then // 或,先计算${a} = ${b},${a} = ${c}之后,才比较[]是否为true
if [ !${a} = ${c} ];then // 非,计算${a} = ${c}之后,取反,比较[]是否为true
# &&,||,!
a="shen"
b="jin"
c="shen"
if [ ${a} = ${b} ] && [ ${a} = ${c} ];then // 与,先计算${a} = ${b},为true后再计算${a} = ${c},否则阻断
if [ ${a} = ${b} ] || [ ${a} = ${c} ];then // 与,先计算${a} = ${b},为true后再计算${a} = ${c},否则阻断
2、复杂的条件判断:
if 条件判断;then
# 判断成立后要执行的语句
fi # 结束语句
--------------------------------------------------------------------
if 条件判断; then
# 条件判断后成立要执行的语句
else
# 条件判断后不成立要执行的语句
fi
--------------------------------------------------------------------
if 条件判断; then
# 条件判断后成立要执行的语句
elif 条件判断;then # 此语句可多次添加
# 条件判断后成立要执行的语句
else
# 条件判断后不成立要执行的语句
fi
--------------------------------------------------------------------
case $变量 in # 与 C语言 switch case 相似
"第一个变量内容")
# 程序段
;; # 表示第一个程序块结束
"第二个变量内容")
# 程序段
;; # 表示第二个程序块结束
"第n个变量内容")
# 程序段
;; # 表示第 n个程序块结束
*) # 类似 C语言 switch case的 default
# 程序段
;;
esac
函数
function fname(){ # function 可写可不写,Shell 函数在定义时不能指明参数,但是在调用时却可以传递参数
# 并且给它传递什么参数它就接收什么参数。
# 函数代码段
}
--------------------------------------------------------------------
fname # 函数调用
--------------------------------------------------------------------
fname param1 param2 # 函数传参,多个参数之间以空格分隔
--------------------------------------------------------------------
$1 $2 # 函数传入的参数取值,$1表示第一个参数,以此类推
注:
1、Shell 不限制定义和调用的顺序,你可以将定义放在调用的前面,也可以反过来,将定义放在调用的后面。
2、函数的返回值有 return 和 echo 两种方式:
return:只能返回数值,且大小不能超过255,即返回值[0,255]。接收方式:$?
echo:是一个非常安全的返回方式,通过将返回值输出到标准输出返回。由于子进程会继承父进程的标准输出,因此,子进程的输出也就直接反映到父进程。接收方式:$(函数名) 或者 `函数名`
#!/bin/sh
function test()
{
echo "256"
}
result=`test`
echo "result is: $result"
result=$(test)
echo "result is: $result"
// 输出
result is: 256
result is: 256
循环
while 条件 # 条件状态为判断式,条件成立时循环,直到条件不成立
do # 循环开始
# 循环代码段
done
--------------------------------------------------------------------
until 条件 # 条件状态为判断式,条件不成立时循环,直到条件成立
do # 循环开始
# 循环代码段
done
--------------------------------------------------------------------
for var in con1 con2 con3 ......
do
# 循环代码段
done
# 变量 var 循环变化,第一次循环等于 con1,第二次循环等于 con2,以此类推
--------------------------------------------------------------------
for((初始值;限制值;执行步长))
do
# 循环代码段
done
# 用法类似于 C语言 for循环
举例:
#!/bin/bash
for name in lcx1 lcx2 lcx3
do
echo "name = $name"
done
--------------------------------------------------------------------
#!/bin/bash
for((count=0;count<=10;count++))
do
echo "$count"
done
标准输入输出
0 是标准输入,一般是从键盘获得输入
1 是标准输出,一般是输出到屏幕了
2 是标准错误,有时候屏幕上可以看到,但是重定向的文件中看不到的就是它了
>为重定向符号
/dev/null 是一个特殊的设备文件,这个文件接收到任何数据都会被丢弃,俗称“黑洞”
2>/dev/null意思就是把错误输出到“黑洞”,也就是说如果你的命令出错的话,错误报告直接就删除了。不会显示在屏幕上。
echo "hello" > t.log // 将输出转到t.log文件
echo "hello" 1> t.log // 同上
nohup java -jar app.jar >log 2>&1 &
解释:
本来1----->屏幕 (1指向屏幕)
执行>log后, 1----->log (1指向log)
执行2>&1后, 2----->1 (2指向1,而1指向log,因此2也指向了log) // 2>&1:将标准错误输出重定向到标准输出
因此:
整数比较 & 字符串比较
# 整数比较,只支持数字,不支持字符串,除非字符串的值是数字
a=5
b=6
if [ ${a} -eq ${b} ];then // -eq 等于
if [ ${a} -ne ${b} ];then // -ne 不等于
if [ ${a} -gt ${b} ];then // -gt 大于
if [ ${a} -ge ${b} ];then // -ge 大于等于
if [ ${a} -lt ${b} ];then // -gt 小于
if [ ${a} -le ${b} ];then // -gt 小于等于
if [ ${a} = ${b} ];then // 等于
if [ ${a} == ${b} ];then // 等于,不建议使用,仅在bash中有效,不符合posix标准,换个dash或者tcsh等shell环境就无效了
if ((${a} > ${b}));then // 大于
if ((${a} >= ${b}));then // 大于等于
if ((${a} < ${b}));then // 小于
if ((${a} <= ${b}));then // 小于等于
# 字符串比较
a="shen"
b="jinlong"
if [ ${a} = ${b} ];then // 检测两个字符串是否相等,相等返回 true。
if [ ${a} != ${b} ];then // 检测两个字符串是否相等,不相等返回 true。
if [ -z ${a} ];then // 检测字符串长度是否为0,为 0 返回 true。
if [ -n ${a} ];then // 检测字符串长度是否为0,不为 0 返回 true。
if [ $a ];then // 检测字符串是否为空,不为空返回 true。
注意:
1、操作符两边都需要加空格。
2、[]两边都需要加空格。
shell脚本中调用另一个shell脚本的三种方式:fork、exec、source
1、fork:(/path/script.sh)
fork 是最普通的, 就是直接在脚本里面用/path/script.sh
来调用script.sh
这个脚本。运行的时候开一个sub-shell
执行调用的脚本,sub-shell
执行的时候, parent-shell
还在。sub-shell
执行完毕后返回parent-shell
。sub-shell
从parent-shell
继承环境变量,但是sub-shell
中的环境变量不会带回parent-shell
。
2、exec:(exec /path/script.sh)
exec 与 fork 不同,不需要新开一个sub-shell
来执行被调用的脚本。被调用的脚本与父脚本在同一个shell
内执行。但是使用 exec 调用一个新脚本以后,父脚本中 exec 行之后的内容就不会再执行了。这是 exec 和 source 的区别。
3、source (source /path/script.sh)
与 fork 的区别是不新开一个sub-shell
来执行被调用的脚本,而是在同一个 shell 中执行。所以被调用的脚本中声明的变量和环境变量,都可以在主脚本中得到和使用。
举例如下:
a.sh
#!/bin/bash
A=B
echo "PID for a.sh before exec/source/fork: $$"
export A
echo "a.sh: A is ${A}"
case $1 in
exec)
echo "using exec…"
exec ./b.sh ;;
source)
echo "using source…"
. ./b.sh ;;
*)
echo "using fork by default…"
./b.sh ;;
esac
echo "PID for a.sh after exec/source/fork: $$"
echo "a.sh: A is ${A}"
b.sh
#!/bin/bash
echo "PID for b.sh: $$"
echo "b.sh get A=${A} from a.sh"
A=C
export A
echo "b.sh: A is ${A}"
执行测试:
[gandalf@VRM01 ~]$ ./a.sh
PID for a.sh before exec/source/fork: 1641420
a.sh: A is B
using fork by default…
PID for b.sh: 1641421
b.sh get A=B from a.sh
b.sh: A is C
PID for a.sh after exec/source/fork: 1641420
a.sh: A is B
------------------------------------------------------------------
[gandalf@VRM01 ~]$ ./a.sh exec
PID for a.sh before exec/source/fork: 1556740
a.sh: A is B
using exec…
PID for b.sh: 1556740
b.sh get A=B from a.sh
b.sh: A is C
------------------------------------------------------------------
[gandalf@VRM01 ~]$ ./a.sh source
PID for a.sh before exec/source/fork: 1563167
a.sh: A is B
using source…
PID for b.sh: 1563167
b.sh get A=B from a.sh
b.sh: A is C
PID for a.sh after exec/source/fork: 1563167
a.sh: A is C
[gandalf@VRM01 ~]$
局部变量和全局变量
关于局部变量和全局变量:
(1)shell 脚本中定义的变量是global的,作用域从被定义的地方开始,一直到shell结束或者被显示删除的地方为止。
(2)shell函数定义的变量也是global的,其作用域从 函数被调用执行变量的地方 开始,到shell或结束或者显示删除为止。函数定义的变量可以是local的,其作用域局限于函数内部。但是函数的参数是local的。
(3)如果局部变量和全局变量名字相同,那么在这个函数内部,会使用局部变量。
举例:定义一个局部变量
function Hello()
{
local text="Hello World!!!" #局部变量
echo $text
}
https://integrate-cida.szv.dragon.tools.huawei.com/deskui/project
常见的特殊用法
用 $? 来获取函数的 return值,用 $(函数名) 来获取函数的 echo 值。
1、输出xxx数组中元素的个数:${#xxx[@]}
[gandalf@VRM01 ~]$ VERSION_PATH=123,456,789 # 字符串赋值
[gandalf@VRM01 ~]$ versions=(${VERSION_PATH//,/ }) # 替换所有的“,”为空格,作为赋值
[gandalf@VRM01 ~]$ echo ${versions[0]}
123
[gandalf@VRM01 ~]$ echo ${versions[1]}
456
[gandalf@VRM01 ~]$ echo ${#versions[@]}
3
[gandalf@VRM01 ~]$ echo ${#versions}
3
2、判断文件或者文件夹是否存在:
# shell判断文件夹是否存在
# 如果文件夹不存在,创建文件夹,-d 参数判断 $folder 是否存在
if [ ! -d "/myfolder" ]; then
mkdir /myfolder
fi
--------------------------------------------------------------------
# -f 参数判断 $file 是否存在
if [ ! -f "$file" ]; then
touch "$file" // 如果不存在,则新建文件
fi
if [ -f "$file" ]; then
cat "$file" // 如果存在,则列出内容
fi
--------------------------------------------------------------------
# -n 判断一个变量是否有值
if [ ! -n "$var" ]; then
echo "$var is empty"
exit 0
fi
--------------------------------------------------------------------
# 判断两个变量是否相等
if [ "$var1" = "$var2" ]; then
echo '$var1 eq $var2'
else
echo '$var1 not eq $var2'
fi
--------------------------------------------------------------------
folder="/var/www/"
# -x 参数判断 $folder 是否存在并且是否具有可执行权限
if [ ! -x "$folder"]; then
mkdir "$folder"
fi
RPM 包相关
1、安装:
命令:rpm -ivh 包全名
示例:[root@localhost ~]# rpm -ivh /mnt/cdrom/Packages/httpd-2.2.15-15.el6.centos.1.i686.rpm
说明:
- -i:安装(install);
- -v:显示更详细的信息(verbose);
- -h:打印 #,显示安装进度(hash);
注:可以一次性安装多个软件包,仅需将包全名用空格分开即可,如下所示:
[root@localhost ~]# rpm -ivh a.rpm b.rpm c.rpm
2、升级:
命令:rpm -Uvh 包全名
说明:-U(大写)选项的含义是:如果该软件没安装过则直接安装;若安装则升级至最新版本。
命令:rpm -Fvh 包全名
说明:-F(大写)选项的含义是:如果该软件没有安装,则不会安装,必须安装有较低版本才能升级。
3、卸载:
命令:rpm -e 包名
说明:-e 选项表示卸载,也就是 erase 的首字母。RPM 软件包的卸载命令支持使用“-nocteps”选项,即可以不检测依赖性直接卸载,但此方式不推荐大家使用,因为此操作很可能导致其他软件也无法正常使用。
4、模糊搜索当前系统中的rpm包:
rpm -qa | grep xxx
[gandalf@VRM01 ~]$ rpm -qa | grep KRM
GalaX-KRM-Registry-8.5.RC1-50.x86_64
GalaX-KRM-Portal-8.5.RC1-50.noarch
GalaX-KRM-8.5.RC1-50.noarch
5、其他常用命令:
rpm -qi 包名:查看一个包的详细信息
rpm -qf 文件名:查看一个文件是由哪个包安装的
举例:
VRM01:/opt/galax/vrm/tomcat/webapps # rpm -qf /opt/galax/vrm/tomcat/webapps/ROOT.war
GalaX-VRM-8.3.0-97.x86_64
rpm -ql 包名:查看一个包安装了哪些文件
rpm -qa:查看系统中安装了哪些包
举例:
Q:rpm -qi 后面如果跟一个未安装的包名,会显示什么信息?
A:包名 is not installed
Q:请找出 vim 这个命令是由哪个 rpm 包安装来的?
A:rpm -qf 'which vim'
Q:rpm 安装某个包有依赖关系时,如何忽略依赖关系,强制安装该包?
A:rpm -i --nodeps 包名