常用命令

head 命令可用于查看文件的开头部分的内容,有一个常用的参数 -n 用于显示行数,默认为 10,即显示 10 行的内容。

命令格式:

1
head [参数] [文件]

参数:

  • -q 隐藏文件名
  • -v 显示文件名
  • -c<数目> 显示字节数
  • -n<行数> 显示行号

查看/etc/passwd 文件前10行

1
head /etc/passwd

查看/etc/passwd 文件前5行

1
head -n5 /etc/passwd

查看/etc/passwd文件前20个字节

1
head -c20 /etc/passwd

tail

tail 命令可用于查看文件的末尾内容,有一个常用的参数 -n 用于显示行数,默认为 10,即显示 10 行的内容。

命令格式:

1
tail [参数] [文件]

参数:

  • -f 循环读取
  • -q 不显示处理信息
  • -v 显示详细的处理信息
  • -c<数目> 显示的字节数
  • -n<行数> 显示文件的尾部 n 行内容
  • –pid=PID 与-f合用,表示在进程ID,PID死掉之后结束
  • -q, –quiet, –silent 从不输出给出文件名的首部
  • -s, –sleep-interval=S 与-f合用,表示在每次反复的间隔休眠S秒

查看test.log文件后10行

1
tail test.log

查看test.log文件后5行

1
tail -n5 test.log

查看test.log的实时增长情况

1
tail -f test.log

此命令显示test.log 文件的最后 10 行。当将某些行添加至 test.log 文件时,tail 命令会继续显示这些行。

cut

取出文本指定的列,默认以空格或table键进行分割

参数:

  • -d 指定分割符
  • -f 指定获取的列号

test.txt

取出test.txt文件中的第二列

1
cut -f2 test.txt

uniq

去除重复的内容。

参数:

  • -d 仅输出有重复的元素
  • -c 输出重复元素的个数

test.txt

1
2
3
4
5
6
7
$ cat test.txt 
annie
annie
luna
luna
luna
milo

去除重复元素

1
2
3
4
$ uniq test.txt
annie
luna
milo

输出有重复的元素

1
2
3
$ uniq -d test.txt 
annie
luna

输出元素重复的个数

1
2
3
4
$ uniq -c test.txt 
2 annie
3 luna
1 milo

sort

sort 命令将以默认的方式将文本文件的第一列以ASCII 码的次序排列,并将结果输出到标准输出。

参数:

  • -r 倒序

1
2
3
4
5
6
$ cat test.txt 
luna
annie
milo
bob
asher

使用sort对文件内容排序

1
2
3
4
5
6
$ sort test.txt 
annie
asher
bob
luna
milo

对文件内容按照倒序排列

1
2
3
4
5
6
$ sort -r test.txt 
milo
luna
bob
asher
annie

wc(word count)

计算文件的Byte数、字数、或是列数。

参数

  • -c或–bytes或–chars 只显示Bytes数。
  • -l或–lines 显示行数。
  • -w或–words 只显示字数。

1
2
3
4
5
6
$ cat test.txt 
luna
annie
milo
bob
asher

统计test.txt文件的行数、单词数、字节数

1
2
$ wc test.txt 
5 5 26 test.txt

只统计test.txt文件的行数

1
2
$ wc -l test.txt 
5 test.txt

只统计test.txt文件的单词数

1
2
$ wc -w test.txt 
5 test.txt

只统计test.txt文件的行数的字节数

1
2
$ wc -c test.txt 
26 test.txt

注:

更多linux命令可以参考:linux命令大全

变量

全局变量

查看全局变量
1
2
3
4
5
6
7
$ env
XDG_SESSION_ID=16536
TERM_PROGRAM=vscode
HOSTNAME=VM-0-2-centos
TERM=xterm-256color
SHELL=/bin/bash
HISTSIZE=3000
定义全局变量
  • 方法一:

    1
    2
    test_var=12345 # 变量名=变量值
    export test_var # export 变量名
  • 方法二:

    1
    export test_var=12345 # export 变量名=变量值

通过export定义全局变量后,可在env中查看到。

需要注意的是,export定义的全局变量是临时的,即退出终端或换个shell就没有了。

如果想要定义永久的全局变量,有两种方法:

  • 方法一:

    shell启动的时候会先启动这两个文件,因此可以在这个文件里写入export var=xxx来定义全局变量,这样每个终端打开后都会执行这句来定义全局变量。

  • 方法二:

    /etc/profile文件中写入export var=xxx来定义全局变量。

局部变量

定义

局部变量定义的四种方式:

1
2
3
4
5
6
7
8
# 方式一:变量名=变量值,变量值必须是一个整体,中间没有特殊字符,等号两侧不能有空格
a=1
# 方式二:变量名='变量值',引号内是什么内容就输出什么内容
a='test test'
# 方式三:同上
a="test test"
# 方式四:变量名=$(linux命令)
a=$(ls)

shell脚本中无法使用局部变量:

定义一个局部变量x并在脚本test.sh中引用了变量x

1
$ x='这是一个局部变量'

test.sh

1
2
3
4
#! /bin/bash
# Description: 测试局部变量

echo "测试局部变量:$x"

运行脚本,无法引用变量x

1
2
$ sh test.sh
测试局部变量:

若将x定义为全局变量

1
2
3
$ export x='这是一个局部变量'
$ sh test.sh
测试局部变量:这是一个局部变量

x引用成功。

引用变量

通过$变量名来引用变量。

注意

''单引号""双引号引用变量是有区别的:

内置变量

符号 含义
$0 获取当前执行的shell脚本文件名,包括脚本路径
$n 获取当前执行的shell脚本的第n个参数值,n=1~9,如果n大于9就要用大括号括起来${10}
$# 获取当前shell命令行中的参数总个数
$? 获取执行上一个指令的返回值(0为成功,非0为失败)
*$** 获取所有参数

test.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/bash
# Description: 内置变量示例说明

echo "当前脚本名:$0"
echo "第一个参数:$1"
echo "第二个参数:$2"
echo "第三个参数:$3"
echo "第四个参数:$4"
echo "第五个参数:$5"
echo "第六个参数:$6"
echo "第七个参数:$7"
echo "第八个参数:$8"
echo "第九个参数:$9"
echo "第十个参数:${10}"
echo "当前脚本的参数总数:$#"
echo "当前脚本的参数分别是:$*"

执行test.sh脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ sh test.sh 10 20 30 40 50 60 70 80 90 100
当前脚本名:test.sh
第一个参数:10
第二个参数:20
第三个参数:30
第四个参数:40
第五个参数:50
第六个参数:60
第七个参数:70
第八个参数:80
第九个参数:90
第十个参数:100
当前脚本的参数总数:10
当前脚本的参数分别是:10 20 30 40 50 60 70 80 90 100

shell基本运算符

算术运算符

定义变量a=10,变量b=20

运算符 说明 举例
+ 加法 expr $a + $b 结果为 30。
- 减法 expr $a - $b 结果为 -10。
* 乘法 expr $a \* $b 结果为 200。
/ 除法 expr $b / $a 结果为 2。
% 取余 expr $b % $a 结果为 0。
= 赋值 a=$b 将把变量 b 的值赋给 a。
== 相等。用于比较两个数字,相同则返回 true。 [ $a == $b ] 返回 false。
!= 不相等。用于比较两个数字,不相同则返回 true。 [ $a != $b ] 返回 true。

特别注意: 在进行乘法运算的时候,由于*有特殊含义,所以必须加上\进行转义,\*才能实现乘法运算。expr出现><要用\转义,不转义会被当做重定向符。

另外,算术表达式有两种写法:

  • $((算术表达式))

    1
    2
    3
    4
    5
    6
    [root@VM-0-2-centos ~] a=10
    [root@VM-0-2-centos ~] b=20
    [root@VM-0-2-centos ~] echo $((a+b)) # 变量可以不加$
    30
    [root@VM-0-2-centos ~] echo $(($a+$b))
    30
  • expr 算术表达式

    如上列表所示。

关系运算符

关系运算符只支持数字,不支持字符串,除非字符串的值是数字。

定义变量a=10,变量b=20

运算符 说明 举例
-eq 检测两个数是否相等,相等返回 true。 [ $a -eq $b ] 返回 false。
-ne 检测两个数是否不相等,不相等返回 true。 [ $a -ne $b ] 返回 true。
-gt 检测左边的数是否大于右边的,如果是,则返回 true。 [ $a -gt $b ] 返回 false。
-lt 检测左边的数是否小于右边的,如果是,则返回 true。 [ $a -lt $b ] 返回 true。
-ge 检测左边的数是否大于等于右边的,如果是,则返回 true。 [ $a -ge $b ] 返回 false。
-le 检测左边的数是否小于等于右边的,如果是,则返回 true。 [ $a -le $b ] 返回 true。

逻辑运算符

定义变量a=10,变量b=20

运算符 说明 举例
&& 逻辑的 AND [[ $a -lt 100 && $b -gt 100 ]] 返回 false
|| 逻辑的 OR [[ $a -lt 100 || $b -gt 100 ]] 返回 true

布尔运算符

定义变量a=10,变量b=20

运算符 说明 举例
! 非运算,表达式为 true 则返回 false,否则返回 true。 [ ! false ] 返回 true。
-o 或运算,有一个表达式为 true 则返回 true。 [ $a -lt 20 -o $b -gt 100 ] 返回 true。
-a 与运算,两个表达式都为 true 才返回 true。 [ $a -lt 20 -a $b -gt 100 ] 返回 false。

字符串运算符

定义a="test"b="test1"

运算符 说明 举例
= 检测两个字符串是否相等,相等返回 true。 [ $a = $b ] 返回 false。
!= 检测两个字符串是否不相等,不相等返回 true。 [ $a != $b ] 返回 true。
-z 检测字符串长度是否为0,为0返回 true。 [ -z $a ] 返回 false。
-n 检测字符串长度是否不为 0,不为 0 返回 true。 [ -n “$a” ] 返回 true。
$ 检测字符串是否为空,不为空返回 true。 [ $a ] 返回 true。

文件测试运算符

操作符 说明 举例
-b file 检测文件是否是块设备文件,如果是,则返回 true。 [ -b $file ] 返回 false。
-c file 检测文件是否是字符设备文件,如果是,则返回 true。 [ -c $file ] 返回 false。
-d file 检测文件是否是目录,如果是,则返回 true。 [ -d $file ] 返回 false。
-f file 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 [ -f $file ] 返回 true。
-g file 检测文件是否设置了 SGID 位,如果是,则返回 true。 [ -g $file ] 返回 false。
-k file 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。 [ -k $file ] 返回 false。
-p file 检测文件是否是有名管道,如果是,则返回 true。 [ -p $file ] 返回 false。
-u file 检测文件是否设置了 SUID 位,如果是,则返回 true。 [ -u $file ] 返回 false。
-r file 检测文件是否可读,如果是,则返回 true。 [ -r $file ] 返回 true。
-w file 检测文件是否可写,如果是,则返回 true。 [ -w $file ] 返回 true。
-x file 检测文件是否可执行,如果是,则返回 true。 [ -x $file ] 返回 true
-s file 检测文件是否为空(文件大小是否大于0),不为空返回 true。 [ -s $file ] 返回 true。
-e file 检测文件(包括目录)是否存在,如果是,则返回 true。 [ -e $file ] 返回 true。

条件表达式

返回值

条件成立返回0,不成立返回1

1
2
3
4
5
6
$ [ 1 -eq 1]
$ echo $? # 获取执行上一个指令的返回值(0为成功,非0为失败)
0
$ [ 1 -eq 2]
$ echo $?
1

if-else简写

1
2
3
4
5
6
$ test_file="/test_file"
$ [ -f "$test_file" ] && echo "it's a file" || echo "it's not a file"
it's not a file
$ touch /test_file
$ [ -f "$test_file" ] && echo "it's a file" || echo "it's not a file"
it's a file

[ 条件 ] && 结果1 || 结果2 该表达式相当于if-else的简写,当条件成立,执行&&后的语句,条件不成立,执行||后的语句。

shell脚本格式

格式要求

  • 在文件首行指定执行shell的程序以及相关说明

    1
    2
    3
    #!/bin/bash
    #Author: 古一
    #Description: 测试脚本
  • shell脚本文件后缀,建议为.sh

  • 脚本执行失败时,使用exit返回非零值,来退出程序

  • 默认缩进4个空格

  • shell脚本命名简单且见名知意

注释

  • 单行注释

    #

  • 多行注释

    1
    2
    3
    :<<!
    这是注释
    !

函数

格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 格式一:
函数名()
{
命令1
命令2
...
}

# 格式二:
function 函数名
{
命令1
命令2
...
}

编写shell脚本func_test.sh

1
2
3
4
5
6
7
8
9
10
#!/bin/bash
# Description: 函数定义

hello(){
echo "你好!召唤师"
ls /
[ $? -eq 0 ] && echo "命令执行成功" || echo "命令执行失败"
}
# 使用函数名调用函数,需要在函数名定义的下方调用
hello

运行结果:

1
2
3
4
$ bash func_test.sh 
你好!召唤师
bin boot data dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
命令执行成功

参数

1
2
3
4
5
6
# 函数体中调用参数
函数名(){
函数体 $n
}
# 函数传参
函数名 参数

在脚本内部传参

1
2
3
4
5
6
7
8
9
10
#!/bin/bash
# Description: 脚本内部传参

hello(){
echo "你好!$1"
[ $2 -eq 0 ] && echo "命令执行成功" || echo "命令执行失败"
}

# 脚本内部调用函数并传入实参
hello 巴斯光年 0

执行结果

1
2
3
$ sh func_test.sh 
你好!巴斯光年
命令执行成功

命令行传入参数

1
2
3
4
5
6
7
8
9
10
#!/bin/bash
# Description: 命令行传参

hello(){
echo "你好!$1"
[ $2 -eq 0 ] && echo "命令执行成功" || echo "命令执行失败"
}

# 函数调用并传入形参
hello $1 $2

执行结果

1
2
3
$ sh func_test.sh  mark 0
你好!mark
命令执行成功

接收用户输入参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/bash
# Description: 接收用户输入参数

hello(){
echo "你好!$1"
[ $2 -eq 0 ] && echo "命令执行成功" || echo "命令执行失败"
}

# 使用read可以接收用户输入的数据
# -p指定提示信息
# 在最后添加一个变量名可以接收输入的变量
# 如果不传变量名,会指定传递给REPLY内置变量

read -p "请输入用户名:" name
read -p "请输入一个数字:"
hello $name $REPLY

执行结果

1
2
3
4
5
$ bash func_test.sh 
请输入用户名:milo
请输入一个数字:0
你好!milo
命令执行成功

流程控制

if

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 格式一:
if [ 条件1 ]
then
指令1
elif [ 条件2 ]
then
指令2
else
指令3
fi

# 格式二:
if [ 条件1 ];then
指令1
elif [ 条件2 ];then
指令2
else
指令3

编写shell脚本condition_test.sh

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash
#Description: if条件语句

read -p "请输入你的年龄:" age

if [ "${age}" -ge 25 ];then
echo "年纪太大,回去养老吧"
elif [ "${age}" -le 15 ];then
echo "年纪太小,回去玩泥巴"
else
echo "可以,来打职业吧"
fi

执行结果:

1
2
3
4
5
6
7
8
9
[root@VM-0-2-centos ~] sh condition_test.sh
请输入你的年龄:16
可以,来打职业吧
[root@VM-0-2-centos ~] sh condition_test.sh
请输入你的年龄:28
年纪太大,回去养老吧
[root@VM-0-2-centos ~] sh condition_test.sh
请输入你的年龄:10
年纪太小,回去玩泥巴

for

1
2
3
4
5
6
7
8
9
10
11
12
# 格式一:
forin 列表
do
执行语句
done

# 格式二:
max=10
for ((i=1;i<=max;i++))
do
echo "$i"
done

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash
#Description: for循环语句

dirs=$(ls /)
i=1
for dir in $dirs
do
if [ -d "/$dir" ];then
echo "第$i个文件夹为:$dir"
else
echo "第$i个文件为:$dir"
fi
i=$((i+1))
done

执行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[root@VM-0-2-centos ~] bash condition_test.sh 
第1个文件为:asc.txt
第2个文件夹为:bin
第3个文件夹为:boot
第4个文件夹为:data
第5个文件夹为:dev
第6个文件夹为:etc
第7个文件夹为:home
第8个文件夹为:lib
第9个文件夹为:lib64
第10个文件夹为:lost+found
第11个文件夹为:media
第12个文件夹为:mnt
第13个文件夹为:opt
第14个文件夹为:proc
第15个文件夹为:root
第16个文件夹为:run
第17个文件夹为:sbin
第18个文件夹为:srv
第19个文件夹为:sys
第20个文件夹为:tmp
第21个文件夹为:usr
第22个文件夹为:var

指定循环次数

1
2
3
4
5
6
7
8
9
#!/bin/bash
#Description: for循环语句指定次数

read -p "请输入循环次数:" num

for ((i=1;i<=$num;i++))
do
echo "第$i次循环"
done

执行结果

1
2
3
4
5
6
7
[root@VM-0-2-centos ~] bash condition_test.sh 
请输入循环次数:5
第1次循环
第2次循环
第3次循环
第4次循环
第5次循环

while

1
2
3
4
5
# 只要条件满足,就一直循环
while 条件
do
执行语句
done

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
#Description: while循环语句

read -p "请输入循环最大数:" num
i=1

while [ "$i" -le "$num" ]
do
echo "第$i次循环"
i=$((i+1))
done

执行结果

1
2
3
4
5
6
7
8
[root@VM-0-2-centos ~] sh condition_test.sh 
请输入循环最大数:6
第1次循环
第2次循环
第3次循环
第4次循环
第5次循环
第6次循环

until

1
2
3
4
5
# 与while处理方式相反,condition 一般为条件表达式,如果返回值为 false,则继续执行循环体内的语句,否则跳出循环。
until 条件
do
执行语句
done

1
2
3
4
5
6
7
8
9
10
#!/bin/bash
#Description: until循环语句

i=1
# 注意这里与while的区别
until [ ! "$i" -le 6 ]
do
echo $i
i=`expr $i + 1`
done

执行结果

1
2
3
4
5
6
7
[root@VM-0-2-centos ~]# sh condition_test.sh 
1
2
3
4
5
6

case

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 格式
case 变量名 in
值1)
指令1
;;
值2)
指令2
;;
值3)
指令3
;;
*)
其它指令
;;
esac

利用case语句实现计算器

caculator.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#! /bin/bash
# Description:计算器

help_info(){
echo "Usage: $0 num1 +|-|\*|/ num2"
}

main(){
if [ "$#" -ne 3 ];then
echo "参数个数有误"
help_info
exit 1
fi

case "$2" in
+)
echo "两数相加结果:$(($1 + $3))"
;;
-)
echo "两数相减结果:$(($1 - $3))"
;;
\*)
echo "两数相乘结果:$(($1 * $3))"
;;
/)
echo "两数相除结果:$(($1 / $3))"
;;
*)
echo "$2为非法运算符"
help_info
exit 1
;;
esac
}

main $1 $2 $3

执行结果

1
2
[root@VM-0-2-centos ~] bash caculator.sh 1 + 2
两数相加结果:3