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-shellsub-shellparent-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 包名

posted on 2023-07-13 14:21  彦承  阅读(23)  评论(0编辑  收藏  举报