Shell 脚本

其他连接:
shell中各种括号的作用'()''{}''[]'

Shell 环境

他扩展名一般为sh, 不起作用就是为了见名知意。

shell脚本的执行方法一定为./test.sh, 不能少了./, 少了就是去PATH(/bin, /sbin, /usr/bin,/usr/sbin)寻找了。

一般shell脚本需要在第一行确定解释器,比如#!/bin/bash, Linux 的 Shell 种类众多,常见的有:

bash 复制代码
Bourne Shell(/usr/bin/sh或/bin/sh)
Bourne Again Shell(/bin/bash)
C Shell(/usr/bin/csh)
K Shell(/usr/bin/ksh)
Shell for Root(/sbin/sh)
...... 

Bash 也是大多数Linux 系统默认的 Shell。

1. Shell 变量

正确定义变量,下面举几个例子,判断正误:

bash 复制代码
your_name = "runoob" (x)变量名和等号之间不能有空格。
0your_name="runoob" (x)不能以数字开头
then="runoob" (x)不能用shell关键字
?your_n&ame="runoob" (x)不能用特殊符号
pi=3.1415926 (x)常量要大写,小写不推荐
your name="runoob" (x)不能有空格
your_name="runoob" (√)
RUNOOB="www.runoob.com"(√)
LD_LIBRARY_PATH="/bin/"(√)
_var="123"(√)
var2="abc"(√)

我蹦出来一个问题:看上面都在用双引号,单引号可以吗?
shell中定义字符串变量(shell默认变量为字符串)用双引号和单引号以及不用引号的区别

单引号:所见即所得,强引用。

双引号:弱引用,如果其中有命令,变量,特殊转义,会解析后再输出结果。

无引号:shell遇到空格会阶段,有可能得不到想要的

比如:

1.1 使用变量

变量名前面加美元符号即可,加{}是为了做区分 ,有时候不区分会造成混淆,都加上{}养成良好编程习惯,比如下面这种造成混淆:

bash 复制代码
for skill in Ada Coffe Action Java; do
    echo "I am good at ${skill}Script"
done

for skill in Ada Coffe Action Java; do
    echo "I am good at $skillScript" #这得不到想要的结果
done

变量定义后可以重新赋值,方法是:

bash 复制代码
name="tom"
echo ${name}
name="lily"
echo ${name}
$mame="lily" #这行会报错,只有使用变量时才用$

只读变量不能被改变:

bash 复制代码
myUrl="https://www.google.com"
readonly myUrl
myUrl="https://www.runoob.com" #这行会报错

删除变量

bash 复制代码
myUrl="https://www.google.com"
readonly myUrl
name="tom"
echo ${name}
unset name
echo ${name} #会输出空行,但没报错
unset myUrl #会报错,不让删除

1.2 变量类型:

分:字符串,整数,数组,环境,特殊 这几种变量。

字符串变量 :用单引号 ' 或双引号 " 来定义字符串。
整数变量 :可以用declare声明一下,比如:declare -i my_integer=42声明告诉 Shell 将 my_integer 视为整数。
数组变量:比如:

c 复制代码
my_array=(1 2 3)
for num in "${my_arry[@]}"; do
    echo ${num}
done
bash 复制代码
#运行结果
1
2
3
c 复制代码
#关联数组变量
declare -A associative_array
associative_array["name"]="John"
associative_array["age"]=30

for element in "${associative_array[@]}"; do
    echo "$element"
done
bash 复制代码
#运行结果
30
John

1.3 环境变量

环境变量为系统或用户设置的特殊变量,用于限制行为或影响环境,常用的有:

参考博客:[Tips] Shell中常用的环境变量与自定义环境变量
pwd:这个在shell中断的命令也可也(在使用时,我发现这个命令只有使用小括号()括起来才有效,即$(pwd),其他形式没用 ,所以请看shell中各种括号的作用
HOME: 使用者的/~目录地址
BASH: 目前使用的shell
PATH:系统默认的可执行文件搜索路径
LANG:系统使用的编码。很多程序是根据系统指定的编码去解析输入文件,如果编码不正确,很多文件就无法正确解析。
RANDOM:随机产生一个0~32767之间的随机数

$$:表示当前shell的pid
$0:表示脚本名称
$1:表示脚本第一个参数,$2就是第二个参数

$#:为传递给shell的参数数量

$?:为上一个命令的退出状态

1.4. Shell 字符串变量

shell字符串最常用,最有用,可以单引号,双引号,也可也不用引号。刚刚我说了使用他们的区别,单引号是强转,双引号要转换一下。但是还有一些好玩的没讲:

1.4.1 字符串拼接

c 复制代码
your_name="lily"
greeting="Hi, ${your_name}."
greeting_1="Hi, \"$your_name\"."

echo ${greeting}  ${greeting_1}

结果是

c 复制代码
Hi, lily. Hi, "lily".

单引号没这效果呦。

1.4.2 获取字符串长度

其实就是加了个#号呗。

c 复制代码
string="abcde"
echo ${#string} 
echo ${#string[0]} 

结果是

c 复制代码
5
5

1.4.3 提取子字符串

c 复制代码
string="runoob is a great site"
echo ${string:2:4}
echo ${string:2:7}

string后接:索引:长度。

字符串第一个字母索的引为0,结果你猜一猜

c 复制代码
noob
noob is

1.4.3 查找字符所在位置

查找字符 i 或 o 在字符串中第几个子字母位置(第一个字母位置为0)哪个先出现就返回哪个。

c 复制代码
string="runoob is a great site"
echo `expr index "$string" is`  # 其中 ` 是反引号
echo `expr index "$string" i`  # 其中 ` 是反引号
echo `expr index "$string" s`  # 其中 ` 是反引号

答案猜一下:

c 复制代码
8
8
9

1.5 数组变量

仅支持一维数组,不支持多维。

使用下标表示成员与c语言类似。

下表中可以为数字或算数表达式。

1.5.1 定义数组

shell中用空格来定义数组,元素之间用空格分开。

c 复制代码
数组名=(值1 值2 值3 ... 值n)

可以这样定义:

c 复制代码
array_name=(
value0
value1
value2
value3
)

也可也分别定义数组的各个元素:

c 复制代码
array_name[0]=value0
array_name[1]=value1
array_name[2]=value2
array_name[7]=value7
for value in ${array_name[@]}; do
        echo ${value}
done

可以不使用连续的下标,而且下标的范围没有限制, 结果是:

c 复制代码
value0
value1
value2
value7

1.5.2 读取数组

1.5.3 获取数组的长度

获取数组成员个数,以及获取某成员长度,加 #

c 复制代码
array_name[0]=value0
array_name[1]=value01
array_name[2]=value012
array_name[7]=value01234567

length1=${#array_name[*]} #获取数组成员个数
echo "length1=${#length1}"
length2=${#array_name[@]} #获取数组成员个数
echo "length2=${#length2}"

length3=${#array_name[7]} #获取某数组成员 的 字符串长度
echo "length3=${#length3}"

echo "array_name=${array_name[@]}" #获取全部成员
echo "array_name=${array_name[*]}" #获取全部成员

揭晓答案:

c 复制代码
length1=4
length2=4
length3=13
array_name=value0 value01 value012 value01234567
array_name=value0 value01 value012 value01234567

2. Shell 注释

以 # 开头的行就是注释,会被解释器忽略。

多行注释可以是这样的,不咋用,详情看看shell注释

c 复制代码
: <<'COMMENT'
这是注释的部分。
可以有多行内容。
COMMENT

3. 传递参数

前面说了,使用美元符号加数字来传递参数。例如:

c 复制代码
不举例子了,太简单,比如:
./test.sh 11 22 33
其中test.sh中的内容为:
#!/bin/bash
echo $0
echo $1
echo $2
echo $3

运行结果为:

c 复制代码
./test.sh
11
22
33

在前面1.3一节总结了一些特殊参数,不全,再汇总一下

参数 解释
$# 脚本输入参数的个数
$* 当用""括起来,输出所有参数为"参数1 参数2 ..."
$? 判断退出状态
$! 最后运行的进程的ID号
$@ 当用""括起来,输出所有参数格式为:"参数2" "参数1" ...
$- 显示Shell使用的当前选项,与set命令功能相同
$$ shell脚本所在进程ID号

其中$*$#的不同比较难理解,看个例子就懂了:

c 复制代码
echo "-- \$* 演示 ---"
for i in "$*"; do
    echo $i
done

echo "-- \$@ 演示 ---"
for i in "$@"; do
    echo $i
done
c 复制代码
#./test.sh 11 22 33
# 结果为:
-- $* 演示 ---
11 22 33
-- $@ 演示 ---
11
22
33

4. 数组

之前介绍了数组变量,咱也稍微熟悉了,知道shell中只存在一维数组,不存在高维数组。数组用括号来表示,元素之间用空格分隔,第一个元素索引为0,而且可以分别定义元素的值且可以不连续。

例子:

c 复制代码
array_name=(value1 value2 ... valuen)

读取数组元素的值:使用美元符号,养成使用{}的好习惯,格式如下:

c 复制代码
${array_name[index]}

关联数组

概念:可以使用任意字符串作为下标来访问数组元素。听着挺唬人的哈,其实就是键值对儿。直接举例子:

c 复制代码
declare -A array_name # 声明一个关联数组
array_name["xiaoming"]="good boy"
array_name["xiaohong"]="good girl"
array_name["zhangsan"]="luck dog"

echo ${array_name["xiaohong"]}

输出就是good girl

使用 @* 可以获取数组中的所有元素加上#可以获取数组成员个数或者单个成员字符个数,都在1.5 数组变量这一小节已经讲过了,去复习吧。

5. 运算符

shell中有算数,关系,布尔,字符串,文件运算符等,bash不支持简单的数学运算,但是可以通过其他命令实现,举例子用expr表达式计算工具实现两数相加:

bash 复制代码
#!/bin/bash
val=`expr 2 + 2`
echo "两数之和为 : $val"
val1='expr 2 + 2' # 不是单括号,而是反引号,注意啊
echo "两数之和为 : $val1"
val2=`expr 2+2`   # 表达式与运算符之间要有空格
echo "两数之和为 : $val2" 

输出结果:

bash 复制代码
两数之和为 : 4
两数之和为 : expr 2 + 2
两数之和为 : 2+2

5.1 算数运算符

包括+ - * / % = == !=这些。但需要注意:
+ - * / % 需要用expr表达式,遵守expr表达式规范。

其中乘法*运算符需要加反斜杠\才能实现乘法运算,即:

c 复制代码
val=`expr $a \* $b`

= 经常用,两边不能有空格我就不说了。
== !=这两个条件表达式 要放在中括号[]里,且必须要有空格 ,强烈建议只能用于字符串比较,不要用于整数比较,整数比较用关系运算符(如果发现被比较对象不是数组会报警告的)。

比如:a=str, b=str, [ $a == $b ]四个空格为正确,即 取值运算必须被空格包围[$a==$b]错误[$a == $b]错误 ,就是说正确表达中空格少任何一个会报错command not found

5.2 关系运算符

关系运算符只支持数字 ,不支持字符串,除非字符串的值是数字,多用于if语句。假定变量 a=10b=20举例说明:

关系运算符 含义 举例
-eq equal [ $a -eq $b ] false
-ne not equal [ $a -ne $b ] true
-gt greater than [ $a -gt $b ]false
-lt less than [ $a -lt $b ]true
-ge greater and equal [ $a -ge $b ] false
-le less and equal [ $a -le $b ] true

5.3 布尔运算符

共三个:!-a-o., 简单猜一下哪个是取反,and运算符 ,or运算符?

恭喜你,猜对了。呱唧呱唧!

备注:用在 [ ... ] 内部组合条件(如 test 命令)

举例子:

bash 复制代码
a=10
b=300

if [$a -gt 5 -a $a -lt 15]; then
	echo "a greater than 5 and a less than 15, true"
else
	echo "a greater than 5 and a less than 15, false !"
fi

if [ $b -gt 100 -a $b -lt 200 ]; then
	echo "b greater than 100 and b less than 200, true"
else
	echo "b greater than 100 and b less than 200, false !"
fi
如果你细心的话,会发现上面的代码有一行是错误的。
且你运行会发现,这个错误不会报错没有提示。好尼玛隐蔽的错误。
提示:有一行少了两个空格,请修正代码。

下面是修正代码后的输出:

bash 复制代码
a greater than 5 and a less than 15, true
b greater than 100 and b less than 200, false !

5.4 逻辑运算符

俩:&&||,是实现逻辑AND与OR的两个命令,我新生疑惑,这跟5.3的布尔运算符不重了吗?

问题:布尔运算符 -a -o 与 逻辑运算符 && || 在使用时的差异?

答:

布尔运算符:可用于test命令,即 [ ... ] 内部。

逻辑运算符:是shell本身的逻辑与或操作符,用于连接两个独立的命令,可用于控制执行流程。

例1:

bash 复制代码
mkdir dir && cd dir  # 创建目录成功后进入
grep "hello" file.txt || echo "Not found"  # 未找到文本时提示
上面这里的逻辑运算符不能被替换为布尔运算符

例2:

bash 复制代码
if [ $b -gt 100 -a $b -lt 200 ]; then
...
上面的布尔运算符不能被替换为&&,会报错的

如果想要使用&&实现相同的逻辑有两种方法,

bash 复制代码
# 法1使用双中括号,原理参见篇首链接
if [[ $b -gt 100 && $b -lt 200 ]]; then 
# 法2,拆封成两个单独的命令,保证逻辑运算符用于连接两个单独的命令的规则
if [ $b -gt 100 ] && [ $b -lt 200 ]; then 

5.5 字符串运算符

有五个,=!=-z(长度为0),-n(长度不为0),$

分别用于:检查两个字符串,相等,不等,长度是否为0,长度是否不为0,是否为空字符串。
注意事项 1= 是 POSIX 标准,== 是 Bash 扩展,虽然都能实现字符串比较,但推荐使用=
注意事项 2 :字符串变量在test进行取值比较时,变量取值操作需要加上双引号"" ,防止字符串存在空格导致的语法错误
注意事项 3-n$都能实现检查字符串长度,但推荐使用前者,更容易理解。

举例,看看不加双引号的可能的后果:

bash 复制代码
b="a b"

if [ -n "$b" ]; then
   echo "字符串长度不为0"
fi

if [ -n "$b" ]; then
   echo "字符串长度不为0"
fi

输出,看报错了吧:

bash 复制代码
字符串长度不为0
./test.sh: line 38: [: a: binary operator expected

5.6 文件测试运算符

-c:检测文件是否字符设备
-b:检测文件是否块设备
-d:检测文件是目录
-f:是否普通文件
-r:文件是否可读
-w:文件是否可写
-x:文件是否可执行

还有一堆,看个例子吧:

bash 复制代码
if [ -d "/root/test/" ]; then
        echo "this is driectory"
fi

5.7 自增和自减操作符

本节主要讲解几点,暂时用的不多,到时候再学:

使用 let 命令进行算数运算;

使用 $(( )) 进行算术运算;

使用 expr 进行算术运算;

使用 (( )) 进行算术运算;

6. echo命令

  1. 显示普通字符串:echo "It is a test" 注意使用双引号。
  2. 显示转义字符,跟c语言的printf用法差不多echo "\"It is a test\""
  3. 显示变量:
c 复制代码
#!/bin/sh
read name  //read命令从标准输入读入一行并把值给到name变量
echo "$name"
以下为输出结果
[root@www ~]# sh test.sh
OK123     #标准输入
OK123     #输出
  1. 显示换行:
bash 复制代码
echo -e "OK! \n" # -e 开启转义
echo -e "OK! \c" # -e 开启转义 \c 不换行
  1. 重定向到文件
bash 复制代码
echo "It is a test" > myfile
  1. 单引号不进行转义
bash 复制代码
echo '$name\"'

输出结果
$name\"

7. printf命令

暂时没学,没见过

8. test命令

在 POSIX 标准中定义。[ 实际上是一个命令名,与 test 功能完全相同,但需要以 ] 结尾。比如说:

bash 复制代码
[ -f "file.txt" ]  # 等价于 test -f "file.txt"

尽管功能相同,但 [ ... ] 更符合 shell 脚本的传统和可读性.
提示:无论用 test 还是 [ ... ],变量必须用双引号保护,防止空值或空格导致语法错误。

9. 流程控制

9.1 if 语句

写成一行适用于中断命令提示符:

c 复制代码
if [ $(ps -ef | grep -c "ssh") -gt 1 ]; then echo "true"; fi
bash 复制代码
if condition1; then
    command1
elif condition2 ; then 
    command2
else
    commandN
fi

经常与 test 命令结合使用,也就是用[], 使用时注意到处都是空格。

9.2 for 循环

c 复制代码
for i in {1..5} ; do
    echo "The value is: $i"
done

也可换成:for ((i=1; i<5; i++)) ; do

9.3 while 循环

bash 复制代码
a=1
while [ $a -lt 5 ]; do
        echo "a = $a"
        let "a++" #也可换成 a=`expr $a + 1`
done
bash 复制代码
echo '按下 <CTRL-D> 退出'
echo -n '输入你最喜欢的网站名: '
while read FILM
do
    echo "是的!$FILM 是一个好网站"
done

当while后面没东西就是无线循环了(没试):

c 复制代码
while :
do
    command
done

9.4 until 循环

使用 until 命令来输出 0 ~ 9 的数字:

bash 复制代码
b=0
until [ $b -gt 9 ]; do
        echo "b = $b"
        let "b++" #也可换成b=`expr $b + 1`
done

9.5 case 语句

case ... esac 为多选择语句,与其他语言中的 switch ... case 语句类似,记住结构:

bash 复制代码
#!/bin/sh

site="google"

case "$site" in
   "baidu") echo "百度"
   ;;
   "google") echo "Google 搜索"
   ;;
   "taobao") echo "淘宝网"
   ;;
esac

9.6 break 与 continue

break 命令允许跳出所有循环(终止执行后面的所有循环)。

continue 命令与 break 命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。

10. 函数

11. 输入输出重定向

12. 文件包含

相关推荐
小狗爱吃黄桃罐头12 分钟前
正点原子[第三期]Arm(iMX6U)Linux移植学习笔记-12.1 Linux内核启动流程简介
linux·arm开发·学习
地衣君1 小时前
Ubuntu 配置使用 zsh + 插件配置 + oh-my-zsh 美化过程
linux·运维·ubuntu
2401_858286111 小时前
OS11.【Linux】vim文本编辑器
linux·运维·服务器·编辑器·vim
朱包林1 小时前
day27-shell编程(自动化)
linux·运维·服务器·网络·shell脚本
kaede1 小时前
Linux实现线程同步的方式有哪些?
linux·运维·云计算
德先生&赛先生1 小时前
Linux编程:2、进程基础知识
linux
子正2 小时前
一键编译包含多个独立模块和应用的工程(linux cmake)
linux·运维·cmake
小阳睡不醒2 小时前
小白成长之路-Linux Shell脚本练习
linux·运维·服务器
dessler2 小时前
代理服务器-LVS的DR模式
linux·运维·云计算
梦星辰.3 小时前
VSCode CUDA C++进行Linux远程开发
linux·c++·vscode