Shell
发表于:2023-10-05 | 分类: 工具
字数统计: 3k | 阅读时长: 11分钟 | 阅读量:

1. Shell概述

Shell是一个命令行解释器,用于接受应用程序/用户命令,然后调用操作系统内核;Shell还是一个功能相当强大的编程语言,易编写、调试且灵活性强。

Linux提供的Shell解析器有多种:

1
2
3
4
5
6
7
[root@hadoop100 ~]# cat /etc/shells
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash
/bin/tcsh
/bin/csh

sh 是 bash 的软连接;CentOS默认的解析器是 bash,可以通过 echo $SHELL 查看环境变量

第一个Shell脚本:

1
2
3
4
[root@hadoop100 ~]# touch hello.sh
[root@hadoop100 ~]# cat hello.sh
#!/bin/bash
echo "hello,world!"

脚本一般以 #!/bin/bash 开头:指定解析器

脚本常用的执行方式:

方式一:

1
bash/sh 脚本的相对路径或绝对路径

此方式不需要赋予脚本可执行权限(x)

5797f1b702bddeffc0d7aaf2d6eb454c.png

方式二:

采用输入脚本的绝对路径或相对路径执行脚本(必须具有可执行权限)

817249656491c03abfa67fd1a7994cc2.png

在目录使用相对路径时,不能直接输入名字,会当成命令执行

方式三:

在脚本的路径前加上“.”或者 source

1
./source 脚本路径

前两种方式都是在当前 shell 中打开一个子 shell 来执行脚本内容,当脚本内容结束,则 子 shell 关闭,回到父 shell 中。在脚本路径前加“.”或者 source 的方式,可以使脚本内容在当前 shell 里执行,这也是为什么我们每次要修改完/etc/profile 文件以后,需要 source

开子 shell 与不开子 shell 的区别就在于,环境变量的继承关系,如在子 shell 中设置的当前变量,父 shell 是不可见的

2. 变量

系统预定义变量

1
2
3
4
5
6
常用系统变量:$HOME、$PWD、$SHELL、$USER 等
[root@hadoop100 ~]# echo $变量名 #查看系统变量的值
[root@hadoop100 ~]# env #查看所有的全局环境变量
[root@hadoop100 ~]# printenv #查看所有的系统全局变量,和env差不多
[root@hadoop100 ~]# printenv 变量名 #查看系统变量的值
[root@hadoop100 ~]# set #查看当前 Shell 中所有变量

自定义变量

定义变量:变量名=变量值,注意,=号前后不能有空格

撤销变量:unset 变量

声明静态变量(只读变量/常量):readonly 变量,注意:不能 unset

把变量提升为全局环境变量:export 变量

203040577d6e3e37bd1b3da62f1fbdaa.png

在 bash 中,变量默认类型都是字符串类型,无法直接进行数值运算;如 a=1+2 是以字符串的形式存储

变量的值如果有空格,需要使用双引号或单引号括起来;

变量定义规则:

  1. 变量名称可以由字母、数字和下划线组成,但是不能以数字开头,环境变量名建议大写。
  2. 等号两侧不能有空格

特殊变量

  1. $n

$n (功能描述:n 为数字,$0 代表该脚本名称,$1-$9 代表第一到第九个参数,十以上的参数需要用大括号包含,如${10})

1
2
3
4
5
6
7
[root@hadoop100 ~]# ./hello.sh a

cho "hello,world!"
echo '====$n===='
echo $0
echo $1
echo $2

3d438b234747dafbe75f4f92ea335728.png

单引号不解析内部内容;双引号会解析。没有输入的参数没有输出

  1. $#

$# (功能描述:获取所有输入参数个数,常用于循环,判断参数的个数是否正确以及加强脚本的健壮性)

1
2
3
4
5
6
7
8
9
[root@hadoop100 ~]# ./hello.sh a

cho "hello,world!"
echo '====$n===='
echo $0
echo $1
echo $2
echo '====$#===='
echo $#

844007d3e90a35fffe413fddd5337c99.png

  1. $*、$@

$* (功能描述:这个变量代表命令行中所有的参数,$*把所有的参数看成一个整体)

$@ (功能描述:这个变量也代表命令行中所有的参数,不过$@把每个参数区分对待)

$* 相当于将所有参数组合为 ‘a b’ 的字符串;$@ 可以看作将参数放到一个数组中

  1. $?

$? (功能描述:最后一次执行的命令的返回状态。如果这个变量的值为 0,证明上一 个命令正确执行;如果这个变量的值为非 0(具体是哪个数,由命令自己来决定),则证明 上一个命令执行不正确了。)

0588115cbd9734a0a81e6714fb1c753a.png

3. 运算符

Linux中特定的运算需要使用expr命令

0871a4b1d2ac49666eabe1a1b5696d7a.png

相当于向expr命令传输三个参数;其中*需要转义才能识别

当我们需要赋值给变量时,需要使用Linux中的命令替换(将一个命令的结果替换再进行操作)

1
2
[root@hadpoo100 ~]# a=$(expr 5 \* 2)
[root@hadpoo100 ~]# a=`expr 5 \* 2` #使用反引号

为了方便快捷的进行运算,我们可以使用Linux指定的运算符:

1
$((运算式)) 或 $[运算式]

d50a87f99a173dfebd8fd0888fca0a9e.png

为变量赋值:

1
[root@hadpoo100 ~]# a=$[(2+3)*2]

5. 条件判断

1
test 条件 或 [ 条件 ](注意 条件 前后要有空格)

条件非空即为 true,[ atguigu ]返回 true,[ ] 返回 false。

a092946f8a932a27334a713a4e274ee6.png

注意 = 两边需要空格,否则会被看作为一个字符串

  1. 两个整数之间比较
条件 功能
-eq 等于(equal)
-ne 不等于(not equal)
-lt 小于(less than)
-le 小于等于(less equal)
-gt 大于(greater than)
-ge 大于等于(greater equal)

可以用 “-z” 判断变量是否为空
如果是字符串之间的比较 ,用等号“=”判断相等;用“!=”判断不等。

  1. 按照文件权限进行判断
条件 功能
-r 有读的权限(read)
-w 有写的权限(write)
-x 有执行的权限(execute)
  1. 按照文件类型进行判断
条件 功能
-e 文件存在(existence)
-f 文件存在并且是一个常规的文件(file)
-d 文件存在并且是一个目录(directory)

6257d66391fb804898b2cab0a5f0fb53.png

多条件判断(&& 表示前一条命令执行成功时,才执行后一条命令,|| 表示上一 条命令执行失败后,才执行下一条命令)

类似Java中的三元运算符

6. 流程控制

if 判断

  1. 单分支
1
2
3
4
5
6
if [ 条件判断式 ];then 程序 ;fi

if [ 条件判断式 ]
then
程序
fi

Linux 中使用 ; 可以在一行执行多条命令

多条件判断:

1
2
3
4
5
6
7
8
9
if [ 条件判断式 ] && [ 条件判断式 ]
then
程序
fi

if [ 条件判断式 -a 条件判断式 ]
then
程序
fi

其中 -a :and;-o:or。

  1. 多分支
1
2
3
4
5
6
7
8
9
if [ 条件判断式 ]
then
程序
elif [ 条件判断式 ]
then
程序
else
程序
fi

if 后要有空

case 语句

1
2
3
4
5
6
7
8
9
10
11
12
case $变量名 in
"值 1")
如果变量的值等于值 1,则执行程序 1
;;
"值 2")
如果变量的值等于值 2,则执行程序 2
;;
……
*)
如果变量的值都不是以上的值,则执行此程序
;;
esac

case 行尾必须为单词“in”,每一个模式匹配必须以右括号“)”结束。

双分号“;;”表示命令序列结束,相当于 java 中的 break。

最后的“*)”表示默认模式,相当于 java 中的 default。

for 循环

语法一:

1
2
3
4
for (( 初始值;循环控制条件;变量变化 ))
do
程序
done

在 (( )) 中可以直接使用数学上的运算符

1
2
3
4
5
for ((i=0;i<$1;i++))
do
sum=$[$sum+$i]
done
echo $sum

语法二:

1
2
3
4
for 变量 in 值 1 值 2 值 3…
do
程序
done

此语法有点类似Java中的增强 for 循环

686dcf7b6553068b9f4095158b574851.png

Shell 中 {} 表示一个序列,如 {1..100} 表示1到100

$* 和 $@ 的区别:

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
echo '====$*===='
for i in $*
do
echo $i
done
echo '====$@===='
for i in $@
do
echo $i
done

1ad3e4140e828aa4eb7888c68b2d91d2.png

在不加双引号时,两者输出是相同的

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
echo '====$*===='
for i in "$*"
do
echo $i
done
echo '====$@===='
for i in "$@"
do
echo $i
done

8a3f8d33727e8ad85c0b61f1e4160a8c.png

while 循环

1
2
3
4
while [ 条件判断式 ]
do
程序
done

程序内部必须改变条件判断式的参数,否则会陷入死循环

1
2
3
4
5
6
7
8
9
10
#!/bin/bash
a=1
while [ a -lt $1 ]
do
#sum=$[$a+$sum]
#a=$[$a+1] 标准写法
let sum+=a
let a++
done
echo $sum

为了代码方便书写,Shell 添加了 let 命令可以使用其它编程语言的语法

读取控制台输入

1
read (选项) (参数)

-p:指定读取值时的提示符;
-t:指定读取值时等待的时间(秒)不加表示一直等待

参数:指定读取值的变量名

1
2
3
#!/bin/bash
read -t 7 -p "Enter your name in 7 seconds :" name
echo $name

7. 函数

系统函数

  1. basename
1
basename [string / pathname] [suffix]

basename 命令会删掉最后一个(‘/’)及之前的字符,然后将字符串显示出来。basename 可以理解为取路径里的文件名称

suffix 为后缀,如果 suffix 被指定了,basename 会将 pathname 或 string 中的 suffix 去掉。

  1. dirname
1
dirname 文件绝对路径

从给定的包含绝对路径的文件名中去除文件名 (非目录的部分),然后返回剩下的路径(目录的部分)。dirname 可以理解为取文件路径名称

1
2
3
#!/bin/bash
echo name:$(basename $0 .sh)
echo path:$(cd $(dirname $0);pwd)

命令替换可以看作系统函数的调用方式

自定义函数

1
2
3
4
5
[ function ] funname[()]
{
Action;
[return n;]
}

其中 [] 中的内容都可以省略

shell 脚本是逐行运行。不会像其它语言一 样先编译,必须在调用函数地方之前,先声明函数。

函数返回值,只能通过$?系统变量获得,如果不加,将以最后一条命令运行结果,作为返回值。return 后跟数值 n(0-255)

1
2
3
4
5
6
7
8
9
#!/bin/bash
function add(){
s=$[$!+$2]
echo $s
}
read -p "请输入第一个整数" a
read -p "请输入第二个整数" b
sum=${add $a $b}
echo "结果:"$sum

这样函数的返回值不会受到 $? 的限制,且返回值sum可以进行进一步计算

8. 文本处理工具

cut

cut 的工作就是“剪”,具体的说就是在文件中负责剪切数据用的。cut 命令从文件的每一行剪切字节、字符和字段并将这些字节、字符和字段输出。

1
2
cut [选项参数] filename
说明:默认分隔符是制表符
选项参数 功能
-f 列号,提取第几列
-d 分隔符,按照指定分隔符分割列,默认是制表符“\t”
-c 按字符进行切割后加 n 表示取第几列,比如 -c 1
1
2
#查看ip
[root@hadoop100 ~]# ifconfig |grep netmask |cut -d " " -f 10

awk

一个强大的文本分析工具,把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行分析处理。

1
2
3
awk [选项参数] ‘/pattern1/{action1} /pattern2/{action2}...’ filename
pattern:表示 awk 在数据中查找的内容,就是匹配模式
action:在找到匹配内容时所执行的一系列命令
选项参数 功能
-F 指定输入文件分隔符
-v 赋值一个用户定义变量
1
2
3
4
5
6
7
8
9
10
11
12
#搜索 passwd 文件以 root 关键字开头的所有行,并输出该行的第 1 列和第 7 列,中间以“,”号分割。
[root@hadoop100 ~]# awk -F : '/^root/{print $1","$7}' /etc/passwd
/bin/bash
#只显示/etc/passwd 的第一列和第七列,以逗号分割,且在所有行前面添加列名 user,shell 在最后一行添加 end,end。
[root@hadoop100 ~]# cat /etc/passwd |awk -F : 'BEGIN{print "user,shell"}{print $1","$7}END{print "end,end"}'
user,shell
root,/bin/bash
...
leamon,/bin/bash
end,end
#将 passwd 文件中的用户 id 加 1 并输出
[root@hadoop100 ~]# awk -v i=1 -F : '{print $3+i}' /etc/passwd

BEGIN 在所有数据读取行之前执行;END 在所有数据执行之后执行。

内置变量 说明
FILENAME 文件名
NR 已读的记录数(行号)
NF 浏览记录的域的个数(切割后,列的个数)
1
2
3
4
#统计 passwd 文件名,每行的行号,每行的列数
[root@hadoop100 ~]# awk -F : '{print "filename:"FILENAME",linenum:"NR",col:"NF}' /etc/passwd
#切割 IP
[root@hadoop100 ~]# ifconfig ens33 | awk '/netmask/ {print $2}'

awk 中如果前面很多空格,默认删除

发送消息

可以使用Linux 自带的 mesg 和 write 工具,向其它用户发送消息。

1
2
3
4
who #查看所有用户
who -T #查看所有用户是否打开mesg;+:打开
mesg #查看是否打开mesg;mesg y/n (打开/关闭)
write 用户名 控制台 #向指定用户发消息
上一篇:
Spring
下一篇:
定时任务