shell脚本基础知识(入门)

2023-05-16

简介

本文会全面介绍shell脚本的基础知识。

脚本格式

要把shell命令放到一个“脚本”当中,有一个要求:脚本的第一行必须写成类似这样的格式:

#!/bin/bash

bash是一个shell解释器,用来解释shell命令。

我们先来写一个最简单的shell脚本,文件命名为1.sh:

#!/bin/bash

echo "hello!"

如果我们的系统使用的是其他的解释器,就要将/bin/bash修改成相应的名字。

注意:脚本文件默认是没有执行权限的,想执行这个脚本必须给它添加权限:

zzc@zzc-virtual-machine:~/share/example$ ./1.sh
-bash: ./1.sh: 权限不够
zzc@zzc-virtual-machine:~/share/example$ chmod +x 1.sh
zzc@zzc-virtual-machine:~/share/example$ ./1.sh
hello!

变量

shell脚本语言是一种弱类型语言,在脚本当中使用变量不需要也无法指定变量的“类型”。
默认状态下,shell脚本的变量都是字符串,即一连串的单词列表。

(1)变量的定义和赋值

myname=“hello kitty”

注意:赋值号两边没有空格;在shell脚本中,任何时候给变量赋值,赋值号两边一定不能有空格。
另外,变量名也有类似于C语言那样的规定:只能包含英文字母和数字,且不能以数字开头。

(2)变量的引用
在变量前面加上一个美元符号,表示对变量进行引用:
$myname

zzc@zzc-virtual-machine:~/share/example$ myname="hello kitty"
zzc@zzc-virtual-machine:~/share/example$ echo $myname
hello kitty

(3)变量的种类
shell脚本中有如下几种变量。

  • 普通的用户自定义变量,如上面的myname
  • 系统预定义好的环境变量,如PATH
  • 命令行变量,如 $#、$*。

如何查看系统的环境变量?
可以输入以下命令:

zzc@zzc-virtual-machine:~/share/example$ env
SHELL=/bin/bash
WINDOWID=8388615
QT_ACCESSIBILITY=1
COLORTERM=truecolor
XDG_CONFIG_DIRS=/etc/xdg/xdg-ukui:/etc/xdg
XDG_SESSION_PATH=/org/freedesktop/DisplayManager/Session0
GTK_IM_MODULE=fcitx
LANGUAGE=zh_CN:en_US:en
QT_AUTO_SCRENN_SET_FACTOR=0
......

如何设置环境变量?
以PATH为例,如果想要将其修改为dir/,可以这么做:

export PATH=dir/

PATH环境变量的作用是保存系统中可执行程序和shell命令的所在路径,因此他的值都是一些以分号隔开的目录。
我们经常这么用:

export PATH=$PATH:dir/

这样做不会改变它原来的值,只是增加了一个目录。
还有,这样做只会在当前的shell中临时有效,如果想要永久有效,就必须把命令export PATH=$PATH:dir/写入~/.bashrc中,然后执行以下命令使之生效:

source ~/.bashrc

而shell脚本中的所谓命令行变量,是指在脚本内部使用用户从命令行中传递进来的参数,如:

./1.sh abcd 1234

我们在执行1.sh时给它传递了两个参数,分别是abcd和1234,要访问这两个参数,就必须使用命令行变量。
下面列出所有的命令行变量及含义:

命令行变量含义
$#命令行参数个数
$*表示所有参数
$@表示所有参数
$n第n个参数
$?代表最后一个命令执行之后的返回值
$$表示当前shell的进程PID

注:$符号后面跟着的就是命令行变量

特殊符号

shell脚本有几种特殊符号:引号、竖杠(管道)、大于号和小于号(重定向)。

引号

引号有三种,他们是双引号" "、单引号’ '、反引号` `。

(1)双引号的作用是把一些“单词”括起来形成单个的“值”,比如:

myname=“lady gaga”

1、双引号可以包含对变量的引用,比如:

fruit=apple
mytree="$fruit tree"

由于对变量进行了引用,所以mytree的最终值是apple tree。

2、双引号也可以包含一个命令,比如:

today="today is `date`"

这里date是一个shell命令,用来获取系统的当前时间,默认情况下脚本会把它当成一个普通的单词,所以需要用反引号(``)来让脚本识别他是一个命令。

(2)单引号

如果一个字符串被单引号包含,那么其内部的所有成分都被视为普通的字符。如下:

var='$myname, today: `date`'

这个变量var的值不会引用myname,也不会执行date命令。

(3)反引号
他用来在双引号中标识出命令。

竖杠(管道)

我们常常需要将一个命令达成的结果给到另外一个命令进行再加工,这时候就需要用到管道,例如:

zzc@zzc-virtual-machine:~/share/example$ ls -l | wc
     15     128     695

管道就像水管一样,把前面命令的执行结果输送给后面的命令。

管道不仅可以连接2个命令,还可以连接多个命令,如下:

cat /etc/passwd | awk -F/ '{print $1}' | wc

大于号> 、小于号<(重定向)

每一个进程在刚开始运行的时候系统会默认为他们打开3个文件:标准输入、标准输出、标准出错,如下图1-30所示。
2
当我们打开普通文件(a.txt,b.doc)之后,系统也会为这些文件产生文件描述符,如下图1-31:
2

1、我们可以使用重定向符号对标准输入和输出进行重定向,比如把ls命令的成功的输出结果重定向到a.txt文件中去,方法如下:

ls 1> a.txt

原理如下图所示:
3

2、我们也可以把ls命令的失败的输出结果重定向到a.txt文件,方法如下:

ls 2> a.txt

原理如下图:
4

3、我们把标准输入重定向到b.doc,方法如下:

echo 0< b.doc

原理如下图:
3

另外,在shell脚本中,在重定向符的右边,标准输入/输出设备文件描述符要写成&0,&1,&2;
比如,把一句话输出到标准出错设备中去:

echo "hello world" 1>&2

字符串处理

shell中对字符串的处理,可以使用awk、sed这些神器,有兴趣的朋友请自行查阅相关资料。
下面介绍一些简单场合下的办法。

1、计算一个字符串的字符个数:

zzc@zzc-virtual-machine:~/share/example$ var="apple tree"
zzc@zzc-virtual-machine:~/share/example$ echo "${#var}"
10

2、删除一个字符串左边部分字符

zzc@zzc-virtual-machine:~/share/example$ path="/etc/rc0.d/K20openbsd-inetd"
zzc@zzc-virtual-machine:~/share/example$ level=${path#/etc/rc[0-9].d/[SK]}
zzc@zzc-virtual-machine:~/share/example$ echo $level
20openbsd-inetd

3、删除一个字符串右边部分的字符

zzc@zzc-virtual-machine:~/share/example$ path="/etc/rc0.d/K20openbsd-inetd"
zzc@zzc-virtual-machine:~/share/example$ level=${path#/etc/rc[0-9].d/[SK]}
zzc@zzc-virtual-machine:~/share/example$ level=${level%%[a-zA-Z]*}
zzc@zzc-virtual-machine:~/share/example$ echo $level
20

测试语句

test命令专门用来实现所谓的测试语句,用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试。

1、数值测试

参数说明
-eq等于则为真
-ne不等于则为真
-gt大于则为真
-ge大于等于则为真
-lt小于则为真
-le小于等于则为真

示例如下:

#!/bin/bash

num1=100
num2=100

if test $[num1] -eq $[num2]
then
    echo '两个数相等!'
else
    echo '两个数不相等!'
fi

运行结果如下:

zzc@zzc-virtual-machine:~/share/example$ ./1.sh
两个数相等!

2、字符串测试

参数说明
=等于则为真
!=不相等则为真
-z 字符串字符串的长度为零则为真
-n 字符串字符串的长度不为零则为真

示例如下:

#!/bin/bash

num1="ru1noob"
num2="runoob"
if test $num1 = $num2
then
    echo '两个字符串相等!'
else
    echo '两个字符串不相等!'
fi

运行结果如下:

zzc@zzc-virtual-machine:~/share/example$ ./1.sh
两个字符串不相等!

3、文件测试

参数说明
-e 文件名文件存在则为真
-r 文件名如果文件存在且可读则为真
-w 文件名如果文件存在且可写则为真
-x 文件名如果文件存在且可执行则为真
-s 文件名如果文件存在且至少有一个字符则为真
-d 文件名如果文件存在且为目录则为真
-f 文件名如果文件存在且为普通文件则为真
-c 文件名如果文件存在且为字符型特殊文件则为真
-b 文件名如果文件存在且为块特殊文件则为真

示例如下:

#!/bin/bash

cd /bin
if test -e ./bash
then
    echo '文件已存在!'
else
    echo '文件不存在!'
fi

运行结果如下:

zzc@zzc-virtual-machine:~/share/example$ ./1.sh
文件已存在!

另外,shell脚本也可以使用方括号来代替test语句,看起来更顺眼:

#!/bin/bash

if [ -e 1.c ] && [ -r 1.c ]
then
	cat 1.c
fi

注意:括号左右两边必须有空格

脚本语法单元

与C语言很类似,shell脚本也需要一套基本单元来控制整个逻辑的执行,包括所谓的控制流(分支控制和循环控制)、函数、数值处理等。

分支控制

示例如下:

if [ -e file ] && [ -r file ]
then
	cat file
fi

语法要点如下:
(1)每一个if语句都有一个fi(if倒过来写)作为结束标记
(2)分支结构中使用then作为起始语句
(3)当且仅当if后面的语句执行结果为真时,then以下的语句才会被执行;if也可以跟else配对使用,如下所示:

#!/bin/bash

if [ -e 1.c ] && [ -r 1.c ]
then
	cat 1.c	#如果文件存在且可读,则显示文件内容
elif [ -e 1.c ]
then
	chmod u+r 1.c	#如果文件存在且不可读,就加读权限再显示其内容
	cat 1.c
else
	touch 1.c	#如果文件不存在就创建该空文件
fi

如果是多路分支,可以使用case语句。
例如,要实现这么一个功能:要求用户输入一个数字,判断如果输入的是1,则输出one;如果输入的是2,则输出two;输入其他数字就输出unknown。
示例如下:

#!/bin/bash

read VAR		# 从键盘输入一个数字
case $VAR in            # 判断用户输入的值$VAR
	1) echo "one"	#如果$VAR的值是1,就显示one	
		;;
	2) echo "two"	#如果$VAR的值是2,就显示two	
		;;
	*) echo "unknown" # 星号*是shell中的通配符,代表任意字符
esac

注意:
1、变量VAR的值实际上是字符串,因此上述代码中的1可以写成"1"。
2、整个case结构必须以esac作为结束标记。

循环控制

shell脚本中有三种可用的循环结构,他们分别是while循环、until循环和for循环。
先来看while循环和until循环,假设现在要实现打印1~100的功能,分别用这两种语句实现,如下:

  • while循环语句:
#!/bin/bash

declare -i n=1			# 在定义变量n前面加上declare -i表示该变量是数值
while [ $n -le 100 ]	# 如果变量n的值小于等于100,则循环
do						# 循环体用do和done包含起来
	echo "$n"	
	n=$n+1				#使n的值加1
done
  • until循环语句
#!/bin/bash

declare -i n=1			# 在定义变量n前面加上declare -i表示该变量是数值
until [ $n -gt 100 ]	# 如果变量n的值大于100,则退出循环
do						# 循环体用do和done包含起来
	echo "$n"	
	n=$n+1				#使n的值加1
done

下面再来看for循环,假设现在要实现:列出当前目录下每个普通文件所包含的行数。
示例代码如下:

#/bin/bash

files=`ls`			# 在当前目录下执行ls,将所有文件名保存在变量files中
for a in $files		#循环地将files里面的每个单词付给a
do
	if [ -f $a ]	#如果文件$a是一个普通文件,就计算他的行数
	then
		wc -l $a
	fi
done

注意,for循环中,in后面接的是一个字符串,字符串里面包含几个单词循环体就执行几遍,每执行一遍a的值都轮换地等于字符串里边的各个单词。

函数

shell脚本有些时候也可以编写模块化代码,将具有某一特定功能的代码封装起来,以供别处调用。
比如,编写一个可以检测某用户是否在线的函数,如下所示:

#/bin/bash

check_user()			#定义一个函数,注意括号里没有空格
{
	if [ $1 = "quit" ]	#若函数的第一个参数$1是"quit",就立即结束脚本
	then
		exit
	fi

	USER=`who | grep $1 | wc -l`
	if [ $USER -eq 0 ]	#判断用户是否在线,在线返回1,不在线返回0
	then
		return 0
	else
		return 1
	fi
}

while  true 

do
	echo  -n "input a user name:"
	read USERNAME

	check_user $USERNAME
	
	if [ $? -eq 1 ]
	then
		echo "[$USERNAME] online."
	else
		echo "[$USERNAME] offline."
	fi
done

注意几点:
1、函数的定义中,括号里不能写任何东西
2、函数必须定义在调用之前
3、给函数传参时,在函数定义里用 n 来 代 表 第 n 个 参 数 , 如 果 是 第 10 个 或 之 后 的 参 数 , 必 须 用 n来代表第n个参数,如果是第10个或之后的参数,必须用 nn10{10}表示
4、$?表示函数调用的返回值

trap

当脚本收到某个信号时,需要处理一些清理工作,然后再退出,类似于POSIX编程中的信号处理。脚本中使用trap来达到这个目的。

trap “” INT

上面语句的含义是:当脚本收到信号SIGINT时,忽略该信号。在LINUX中所支持的信号可以使用命令trap -l来查看。信号名称的前缀要省略。trap除了可以“忽略”信号,也可以捕获信号,例如:

trap do_something INT QUIT HUP

这句话意思是当脚本收到INT、QUIT、HUP信号时执行函数do_something。
此处还可以指定脚本正常退出时的默认动作,例如:

trap on_exit EXIT

这句话是指当脚本正常退出时,执行函数on_exit。

有的时候,当shell脚本收到某个信号时,我们需要立即终止脚本,且要执行正常退出时的清理函数,该情况下可以这么做:

trap on_exit EXIT
trap ":" INT HUP

以上两句的意思是当脚本正常退出时调用函数on_exit,当脚本收到信号INT或HUP时执行空指令(此处冒号代表一个空指令,如果没有冒号,脚本将完全忽略该信号,不做响应,不能立即退出),完毕后正常退出,此时触发EXIT从而执行函数on_exit。

printf命令

printf 命令模仿 C 程序库(library)里的 printf() 程序。

printf 由 POSIX 标准所定义,因此使用 printf 的脚本比使用 echo 移植性好。

printf 使用引用文本或空格分隔的参数,外面可以在 printf 中使用格式化字符串,还可以制定字符串的宽度、左右对齐方式等。默认的
printf 不会像 echo 自动添加换行符,我们可以手动添加 \n。

printf 命令的语法:

printf format-string [arguments…]

参数说明:

  • format-string: 为格式控制字符串
  • arguments: 为参数列表。

简单示例:

$ echo "Hello, Shell"
Hello, Shell
$ printf "Hello, Shell\n"
Hello, Shell
$

复杂示例:

#!/bin/bash
 
printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg  
printf "%-10s %-8s %-4.2f\n" 郭靖 男 66.1234
printf "%-10s %-8s %-4.2f\n" 杨过 男 48.6543
printf "%-10s %-8s %-4.2f\n" 郭芙 女 47.9876

效果如下:
体重

解释如下:

%s %c %d %f 都是格式替代符,%s 输出一个字符串,%d 整型输出,%c 输出一个字符,%f 输出实数,以小数形式输出。

%-10s 指一个宽度为 10 个字符(- 表示左对齐,没有则表示右对齐),任何字符都会被显示在 10 个字符宽的字符内,如果不足则自动以空格填充,超过也会将内容全部显示出来。

%-4.2f 指格式化为小数,其中 .2 指保留2位小数。

多种用法示例如下:

#/bin/bash

# format-string为双引号
printf "%d %s\n" 1 "abc"

# 单引号与双引号效果一样
printf '%d %s\n' 1 "abc"

# 没有引号也可以输出
printf %s abcdef

# 格式只指定了一个参数,但多出的参数仍然会按照该格式输出,format-string 被重用
printf %s abc def

printf "%s\n" abc def

printf "%s %s %s\n" a b c d e f g h i j

# 如果没有 arguments,那么 %s 用NULL代替,%d 用 0 代替
printf "%s and %d \n"

效果如下:
123

补充格式符 %b:
%b: 字符串–相对应的参数被视为含有要被处理的转义序列之字符串

示例如下:(%b对应的字符串里面\n被转义了)
123

%f 格式化浮点数默认支持 6 位小数:

#/bin/bash

printf "%d %s %c %f\n" 10 "abc" "def" "3.1415926"

效果如下:

zzc@zzc-virtual-machine:~/share$ ./2.sh
10 abc d 3.141593	# 格式化浮点数默认支持小数点后六位,后面多出的四舍五入

常见问题

语法问题

  • 变量赋值,等号左右有空格
  • if结构没有以fi结尾
  • 循环结构没有do或者没有done
  • if语句方括号左右没有空格

逻辑问题

  • 使用变量时,没有引用变量

权限问题

  • 无法执行shell脚本,没有执行权限

补充

1、从键盘输入的字串无法做数值运算
read var
var=$var+1
echo $var
假设从键盘输入1,则输出的结果是 1+1

总结

本篇文章对个人最近学习的shell脚本知识和所做的实践进行了总结,到这里也算是入门了;后面有时间再扩充相关的知识点和一些可能遇到的问题。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

shell脚本基础知识(入门) 的相关文章

随机推荐

  • Qt之QMenu菜单去除投影效果(阴影)

    一 简述 我们使用Qt中的菜单 xff0c 正常情况下样式是跟随当前系统菜单的样式 xff0c 我们可以使用样式表进行修饰 xff0c 改变原有风格 xff0c 但是window系统上菜单边框四周会附带阴影的效果 xff0c 样式是无法取消
  • Qt 之 设置窗口边框的圆角

    Qt技术学习班开始了 xff0c 更多精彩 好玩的内容等着你 xff0c 赶紧报名吧 群号 xff1a 655815739 Qt在设置窗口边框圆角时有两种方式 xff0c 一种是设置样式 xff0c 另一种是在paintEvent事件中绘制
  • Qt 之 HTTP 请求下载(支持断点续传)

    简述 最近在研究了一下用Qt 的方法来实现http下载 xff0c Qt 中的Http请求主要用到了QNetworkAccessManager QNetworkReply QNetworkRequest 这三块 本篇文章主要叙述如何用Qt
  • Qt之实现录音播放及raw(pcm)转wav格式

    简述 在上一篇 Qt 之 WAV文件解析 中详细地分析了wav格式文件的文件头信息 通过QAudioInput实现录音功能 xff0c 但是录音生成的文件并不能用播放器打开 xff0c 就算更改后缀名也无法识别 xff08 有时候下载的一些
  • C++中 Unicode 与 UTF-8 编码互转

    1 简述 最近在发送网络请求时遇到了中文字符乱码的问题 xff0c 在代码中调试字符正常 xff0c 用抓包工具抓的包中文字符显示正常 xff0c 就是发送到服务器就显示乱码了 xff0c 那就要将客户端和服务器设置统一的编码 xff08
  • Qt 之 自定义按钮 在鼠标 悬浮、按下、松开后的效果

    Qt技术学习班开始了 xff0c 更多精彩 好玩的内容等着你 xff0c 赶紧报名吧 群号 xff1a 655815739 一 简述 在上一篇 Qt 之 去除窗口部件被选中后的焦点虚线框 中 xff0c 我们为了去除焦点虚线框 xff0c
  • Qt 之 自定义窗口标题栏

    Qt训练营开始了 xff0c 更多精彩 好玩的内容等着你 xff0c 赶紧报名吧 群号 xff1a 861353824 一 简述 今天晚上就如何用Qt自定义窗口标题栏 xff0c 写了一个小例子 xff0c 比较基础 xff0c 实用 在此
  • Qt 之 模仿 QQ登陆界面——旋转窗口篇

    一 简述 今天是新的一年第一篇博客 xff0c 有大半个月没有更新博客了 我想是时候 xff0c 打开电脑 拿起键盘 开始在我的代码之路上披荆斩棘 xff0c 斩杀恶龙 今天就继续来分享QQ登录界面的那些事 QQ登录界面的标题栏有一个小三角
  • Ubuntu配置无线路由器笔记记录

    参考文章 xff1a linux 开启制作无线路由器 ubuntu 1404 linux zhu的博客 CSDN博客 hostapd实现WIFI 热点 xff08 AP xff09 自由枫 的博客 CSDN博客 hostapd 终端get一
  • C++STL的使用心得汇总(vector,string,map,list)

    文章目录 find 函数vector的findstring的findmap的find count 函数vector的countstring的countmap的count vectorstringmap的各种排序方法转换相关 待完善 find
  • Qt 之 样式表的使用——设置样式的方法

    一 简述 我们通常在使用Qt开发的过程中都会使用样式表来美化我们的界面 xff0c 关于如何使用样式表的资料也很多 xff0c 样式表的使用方法也千变万化 为了搭建一个漂亮的界面那么必须学会如何使用样式表 xff0c Qt帮助文档中提供了非
  • 如何使QGraphicsItem不随QGraphicsView放大缩小而改变大小

    一 简述 在使用QGraphicsView过程中 xff0c 有时候我们需要对view进行缩放 xff0c 但是对于一般正常的加入view中的item都会随着view的大小变化而变化 xff0c 但是如果我们想让某些item不随view的缩
  • 【linux系统如何查看内核版本、操作系统版本等信息】

    有时候需要查看linux系统的内核版本 xff0c 可以有多种方法 xff0c 方法如下 xff1a xff08 下面以优麒麟系统为例 xff09 方法1 xff1a 打开mate终端 xff0c 在命令行输入以下命令 xff1a unam
  • 【linux系统如何安装arm交叉编译工具链】

    文章目录 前言一 arm交叉编译器介绍命名规则具体编译器 二 Arm GNU Toolchain安装总结 前言 本文简要介绍arm交叉编译器及工具链的安装方法 一 arm交叉编译器介绍 命名规则 交叉编译工具链的命名规则为 xff1a ar
  • 比较冒泡排序、选择排序和快速排序的时间(C语言实现)

    文章目录 前言代码设计代码实现运行结果结果分析稳定性测试 总结 前言 本文主要比较冒泡排序 快速排序 选择排序的时间 冒泡排序和快速排序的思想可以参考我转载的以下博文 xff1a https blog csdn net gogo0707 a
  • freertos应用程序常见错误排查

    freertos系统应用程序常见问题 对一些比较常见的问题 xff0c 下面简要的以 FAQ 问答 的形式给出可能的原因和解决方法 问题现象 xff1a 在一个 Demo 应用程序中增加了一个简单的任务 xff0c 导致应用程序崩溃 任务创
  • keil5编译工程常见问题汇总

    简介 我们在编译keil工程的时候总是遇到很多问题 xff0c 我把一些常见的问题和解决方案汇总下来 xff0c 仅供大家参考 问题汇总 问题1 问题描述 选择arm v6版本编译器 xff0c 编译keil5工程 xff0c 报错 xff
  • mdk arm debug配置

    简述 本文简要讲述启动调试之前如何配置debug 点击魔术棒 xff0c 进入debug选项界面 xff0c 如下图 xff1a 我们可以选择软件仿真 xff0c 也可以选择硬件仿真 xff08 软件仿真不需要接开发板和仿真器 xff09
  • stm32高级定时器实现pwm互补输出

    简介 stm32设备一般都有很多类型的定时器 xff0c 常见的有systick timer 基本定时器 通用定时器 高级定时器 看门狗定时器 RTC等等 xff0c 本文简单介绍高级定时器是如何实现pwm互补输出 详细 我这里使用的dev
  • shell脚本基础知识(入门)

    简介 本文会全面介绍shell脚本的基础知识 脚本格式 要把shell命令放到一个 脚本 当中 xff0c 有一个要求 xff1a 脚本的第一行必须写成类似这样的格式 xff1a bin bash bash是一个shell解释器 xff0c