掌握 Shell 编程,一篇就够了

本文由 简悦 SimpRead 转码, 原文地址 zhuanlan.zhihu.com

蓝湖内推(北京尽微致广信息技术有限公司 ),前后端测开大量 HC,坐标北京朝阳区望京 SOHO,福利多多,欢迎小伙伴们投递。

内推码: BSW6GF7

投递链接: jwzg.jobs.feishu.cn

没想到收藏数这么高,文末更新一波福利。

本文首先介绍了 Shell 编程是什么,并带大家快速入门,随后讲解 Shell 的基本语法并结合案例重点分析用法。包括 Shell 流程控制和自定义函数等。建议收藏。 谁需要学习 Shell 编程?

  1. Linux 运维工程师:编写 Shell 程序进行服务集群管理。
  2. Python 和 JavaEE 程序员:编写 Shell 脚本程序或者是服务器的维护,比如编写一个定时备份数据库的脚本。
  3. 大数据程序员:编写 Shell 程序来管理集群。

Shell 是什么? Shell 是一个命令解释权,它为用户提供了一个向 Linux 内核发送请求以便运行程序界面系统级程序,用户可以用 Shell 来启动、挂起、停止甚至是编写一些程序。

https://pic4.zhimg.com/v2-77c3eba030e869991d0050b20efd6cc7_b.jpg

Shell 编程快速入门 进入 Linux 终端,编写一个 Shell 脚本 hello.sh :

1
2
#!/bin/bash 
echo 'hello world!'

运行:

1
2
3
4
5
6
# 方法1 
sh hello.sh  

# 方法2 
chmod +x hello.sh 
./hello.sh

终端打印出 hello world!说明:

  • #! 告诉系统这个脚本需要什么解释器来执行。
  • 文件扩展名 .sh 不是强制要求的。
  • 方法 1 直接运行解释器,hello.sh 作为 Shell 解释器的参数。此时 Shell 脚本就不需要指定解释器信息,第一行可以去掉。
  • 方法 2 hello.sh 作为可执行程序运行,Shell 脚本第一行一定要指定解释器。

Shell 变量 定义 Shell 变量分为系统变量自定义变量。系统变量有 $HOME、$PWD、$USER 等,显示当前 Shell 中所有变量:set 。 变量名可以由字母、数字、下划线组成,不能以数字开头。 基本语法

  • 定义变量: 变量名 = 变量值,等号两侧不能有空格,变量名一般习惯用大写。
  • 删除变量: unset 变量名 。
  • 声明静态变量: readonly 变量名,静态变量不能 unset。
  • 使用变量: $ 变量名

将命令返回值赋给变量(重点)

  • A=ls 反引号, 执行里面的命令
  • A=$(ls) 等价于反引号

Shell 环境变量 定义

https://pic4.zhimg.com/v2-42d4bc4444463d62135b3508aa4ad7f3_r.jpg

基本语法

  1. export 变量名 = 变量值,将 Shell 变量输出为环境变量。
  2. source 配置文件路径,让修改后的配置信息立即生效。
  3. echo $ 变量名,检查环境变量是否生效

位置参数变量 基本语法

  • $n :$0 代表命令本身、$1-$9 代表第 1 到 9 个参数,10 以上参数用花括号,如 ${10}。
  • $* :命令行中所有参数,且把所有参数看成一个整体。
  • $@ :命令行中所有参数,且把每个参数区分对待。
  • $# :所有参数个数。

实例: 编写 Shell 脚本 positionPara.sh ,输出命令行输入的各个参数信息。

1
2
3
4
5
6
#!/bin/bash     
# 输出各个参数 
echo $0 $1 $2 
echo $* 
echo $@ 
echo 参数个数=$#

运行:

1
2
chmod +x positionPara.sh 
./positionPara.sh 10 20

运行结果:

1
2
3
4
./positionPara.sh 10 20 
10 20 
10 20 
参数个数=2

预定义变量 定义 在赋值定义之前,事先在 Shell 脚本中直接引用的变量。 基本语法

  • $$ :当前进程的 PID 进程号。 $$
  • $! :后台运行的最后一个进程的 PID 进程号。
  • $? :最后一次执行的命令的返回状态,0 为执行正确,非 0 执行失败。

实例: 编写 Shell 脚本 prePara.sh ,输出命令行输入的各个参数信息。

1
2
3
4
5
6
#!/bin/bash     
echo 当前的进程号=$$ 
# &:以后台的方式运行程序 
./hello.sh & 
echo 最后一个进程的进程号=$! 
echo 最后执行的命令结果=$?

运行结果:

1
2
3
当前的进程号=41752 
最后一个进程的进程号=41753 
最后执行的命令结果=0 # hello world!

运算符 基本语法

  • $((运算式)) 或$[运算式]
  • expr m + n 注意 expr 运算符间要有空格
  • expr m - n
  • expr *,/,% 分别代表乘,除,取余

实例

1
2
3
4
5
6
7
8
9
# 第1种方式 $(()) 
echo $(((2+3)*4))   

# 第2种方式 $[],推荐 
echo $[(2+3)*4]  

# 使用 expr 
TEMP=`expr 2 + 3` 
echo `expr $TEMP \* 4`

条件判断 基本语法 [condition] 注意 condition 前后要有空格。非空返回 0,0 为 true,否则为 false 。 实例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/bin/bash 
if [ 'test01' = 'test' ] 
then
     echo '等于' 
fi  

# 20是否大于10 
if [ 20 -gt 10] 
then
     echo '大于' 
fi  

# 是否存在文件/root/shell/a.txt 
if [ -e /root/shell/a.txt ] 
then
     echo '存在' 
fi  

if [ 'test02' = 'test02' ] && echo 'hello' || echo 'world' 
then
     echo '条件满足,执行后面的语句' 
fi

运行结果:

1
2
3
大于 
hello 
条件满足,执行后面的语句

流程控制

if 判断

基本语法

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

# 或者(推荐)
if [ 条件判断式 ]
then
    程序
elif [ 条件判断式 ]
then
    程序
fi

实例

编写 Shell 程序:如果输入的参数大于 60,输出 “及格”,否则输出 “不及格”。

1
2
3
4
5
6
7
8
#!/bin/bash
if [ $1 -ge 60 ]
then
    echo 及格
elif [ $1 -lt 60 ]
then
    echo "不及格" 
fi

case 分支

基本语法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
case $变量名 in
"值1")
如果变量值等于值1,则执行此处程序1
;;
"值2")
如果变量值等于值2,则执行此处程序2
;;
...省略其它分支...
*)
如果变量值不等于以上列出的值,则执行此处程序
;;
esac

实例

当命令行参数为 1 时输出 “周一”,2 时输出 “周二”,其他情况输出 “其它”。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
case $1 in
"1")
echo 周一
;;
"2")
echo 周二
;;
*)
echo 其它
;;
esac

for 循环

基本语法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 语法1
for 变量名 in 值1 值2 值3...
do
    程序
done

# 语法2
for ((初始值;循环控制条件;变量变化))
do
    程序
done

实例

  1. 打印命令行输入的参数。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#!/bin/bash  

# 使用$* 
for i in "$*" 
do     
    echo "the arg is $i" 
done 
echo "=================="  

# 使用$@ 
for j in "$@" 
do     
    echo "the arg is $j" 
done

运行结果(回顾一下 $* 和$@ 的区别):

1
2
3
4
5
the arg is 1 2 3 
================== 
the arg is 1 
the arg is 2 
the arg is 3
  1. 输出从 1 加到 100 的值。
1
2
3
4
5
6
7
8
#!/bin/bash 
SUM=0  
for ((i=1;i<=100;i++)) 
do     
    SUM=$[$SUM+$i] 
done 

echo $SUM

while 循环

基本语法

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

实例

输出从 1 加到 100 的值。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#!/bin/bash
SUM=0
i=0

while [ $i -le $1 ]
do
    SUM=$[$SUM+$i]
    i=$[$i+1]
done       
echo $SUM

读取控制台输入

基本语法

read(选项)(参数) 选项

  • -p:指定读取值时的提示符
  • -t:指定读取值时等待的时间(秒),如果没有在指定时间内输入,就不再等待了。

参数

  • 变量名:读取值的变量名

实例

读取控制台输入一个 num 值。

1
2
3
4
5
6
7
#!/bin/bash

read -p "请输入一个数num1=" NUM1
echo "你输入num1的值是:$NUM1"

read -t 10 -p "请在10秒内输入一个数num2=" NUM2
echo "你输入num2的值是:$NUM2"

运行结果:

1
2
3
4
请输入一个数num1=10
你输入num1的值是:10
请在10秒内输入一个数num2=20
你输入num2的值是:20

函数

和其它编程语言一样,Shell 编程有系统函数和自定义函数,本文只举两个常用系统函数。

系统函数

  • basename,删掉路径最后一个 / 前的所有部分(包括 /),常用于获取文件名。 基本语法
  • basename [pathname] [suffix]
  • basename [string] [suffix]
  • 如果指定 suffix,也会删掉 pathname 或 string 的后缀部分。

实例

1
2
3
4
5
6
7
8
# basename /usr/bin/sort  
sort  

# basename include/stdio.h  
stdio.h  

# basename include/stdio.h .h 
stdio
  • dirname,删掉路径最后一个 / 后的所有部分(包括 /),常用于获取文件路径。 基本语法
  • dirname pathname
  • 如果路径中不含 / ,则返回 ‘.’ (当前路径)。

实例

1
2
3
4
5
6
7
8
9
# dirname /usr/bin/  
/usr  

# dirname dir1/str dir2/str 
dir1 
dir2  

# dirname stdio.h 
.

自定义函数

基本语法

1
2
3
4
5
6
7
8
[ function ] funname[()]
{
    Action;
    [return int;]
}

# 调用
funname 参数1 参数2...

实例

计算输入两个参数的和。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#!/bin/bash

function getSum(){
    SUM=$[$n1+$n2]
    echo "sum=$SUM"
}   

read -p "请输入第一个参数n1:" n1
read -p "请输入第二个参数n2:" n2

# 调用 getSum 函数
getSum $n1 $n2

恭喜!你已经掌握了 Shell 的基本语法,入门很简单。想要更系统的探索 Shell 编程,可以和我一起深入学习接下来的高级篇。老样子,向大家推荐一本最值得购入和收藏的程序员必读好书:

《深入理解计算机系统》属于圣经级别的众多国内外名校教材(北大清华上海交大,国外简直更多了),罕见的豆瓣评分 9.5,也是我最喜欢的专业书。读过好几遍了,不管是学生还是开发老手,这绝对是程序员最值得投资并且之后不会后悔的书,有点难,但很有用,想要电子版的也可以私信我,免费送你

如果本文对你有帮助,收藏完了也点个赞互相鼓励一下吧~ 另外欢迎大家关注我的主页,会不定期输出有价值的内容。

更多干货

不看后悔系列:

Linux 针对很多常用命令增加了很多新的更加有用高效的新命令,这些命令不但可以大大提高工作效率和体验,让你在同事面前装那啥的利器,如果能在面试新工作时提起这类命令,更是能给面试官眼前一亮的加分项,下面展示了几个在工作中最常用的命令。

  • 如果你想要有语法高亮的 cat,可以试试 ccat 命令。
  • exa 增强了 ls 命令,如果你需要在很多目录上浏览各种文件 ,ranger 命令可以比 cdcat 更有效率,甚至可以在你的终端预览图片。
  • fd 是一个比 find 更简单更快的命令,他还会自动地忽略掉一些你配置在 .gitignore 中的文件,以及 .git 下的文件。
  • fzf 会是一个很好用的文件搜索神器,其主要是搜索当前目录以下的文件,还可以使用 fzf --preview 'cat {}'边搜索文件边浏览内容。
  • grep 是一个上古神器,然而,ackagrg 是更好的 grep,和上面的 fd一样,在递归目录匹配的时候,会使用你配置在 .gitignore 中的规则。
  • rm 是一个危险的命令,尤其是各种 rm -rf …,所以,trash 是一个更好的删除命令。
  • man 命令是好读文档的命令,但是 man 的文档有时候太长了,所以,你可以试试 tldr 命令,把文档上的一些示例整出来给你看。
  • 如果你想要一个图示化的ping,你可以试试 prettyping
  • 如果你想搜索以前打过的命令,不要再用 Ctrl +R 了,你可以使用加强版的 hstr
  • htop 是 top 的一个加强版。然而,还有很多的各式各样的 top,比如:用于看 IO 负载的 iotop,网络负载的 iftop, 以及把这些 top 都集成在一起的 atop
  • ncdu 比 du 好用多了。另一个选择是 nnn
  • 如果你想把你的命令行操作录制成一个 SVG 动图,那么你可以尝试使用 asciinemasvg-trem
  • httpie 是一个可以用来替代 curlwget 的 http 客户端,httpie 支持 json 和语法高亮,可以使用简单的语法进行 http 访问: http -v github.com
  • tmux 在需要经常登录远程服务器工作的时候会很有用,可以保持远程登录的会话,还可以在一个窗口中查看多个 shell 的状态。
  • sshrc 是个神器,在你登录远程服务器的时候也能使用本机的 shell 的 rc 文件中的配置。
  • goaccess 这个是一个轻量级的分析统计日志文件的工具,主要是分析各种各样的 access log。

往期高收藏:

小晶:从原理到实战,彻底搞懂 Nginx!小晶:从原理到实战,彻底搞懂 Nginx!(高级篇)小晶:Kafka 概述:深入理解架构