Linux-Shell学习笔记教程,快速学习
Linux-Shell
思维导图结构:
介绍
shell是什么呢?其实是我们一直在用的,所有的命令都是通过shell去实现的,看来自菜鸟教程的介绍:
Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。Shell 既是一种命令语言,又是一种程序设计语言。
Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。
Ken Thompson 的 sh 是第一种 Unix Shell,Windows Explorer 是一个典型的图形界面 Shell。
它所说的界面,可能就是我们一直用的黑框框吧,是不是一说界面会想到图形化界面,还在纳闷呢!哈哈
大家已经写过一个脚本了hello world,在之前的章节中,我们已经写过一个小小的脚本,万能的hello world;
快速上手
菜鸟教程对这里部分基础写的非常好,这里大量参考菜鸟教程Linux-shell部分加上自己的理解,帮大家进行一遍梳理;
这里我们再重新认识下,新建一个文件为hello.sh,使用vim在里面编辑:
#!/bin/bash #! 是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行echo "Hello World !" #命令用于向窗口输出文本。
当然了,这个时候还不能运行./hello.sh
试一下,提示权限不够,是因为文件并没有执行权限,我们需要给文件添加执行权限,chmod +x hello.sh
,再次执行,成功!
[root@localhost test]# vim hello.sh[root@localhost test]# ./hello.sh-bash: ./hello.sh: 权限不够[root@localhost test]# chmod +x hello.sh[root@localhost test]# lshelloNos.txt hello.sh helloS.txt hello.txt[root@localhost test]# ./hello.shhello,world[root@localhost test]#
注意,一定要写成 ./hello.sh,而不是 hello.sh,运行其它二进制的程序也一样,直接写 test.sh,linux 系统会去 PATH 里寻找有没有叫 hello.sh 的,而只有 /bin, /sbin, /usr/bin,/usr/sbin 等在 PATH 里,你的当前目录通常不在 PATH 里,所以写成 test.sh 是会找不到命令的,要用 ./hello.sh 告诉系统说,就在当前目录找。
所以知道为什么执行命令需要加./
了吧
可能还有疑惑,后缀名就一定.sh是这样吗?不是的,我们来试一下,我修改了文件的名字,为hello.t再次执行还是没有问题,因为这里不像C、java、python需要固定的后缀名.c .java .py 文件名是什么都没有任何问题,.sh后缀是一种很好的规范,到时候你和别人都可以知道这是一个shell脚本文件:
[root@localhost test]# mv hello.sh hello.t[root@localhost test]# lshelloNos.txt helloS.txt hello.t hello.txt[root@localhost test]# ./hello.thello,world[root@localhost test]#
Shell语法
变量
变量定义:
定义变量时,变量名不加美元符号($,我们使用变量时需要),如:
your_name=“runoob.com”
注意,变量名和等号之间不能有空格,这可能和你熟悉的所有编程语言都不一样。同时,变量名的命名须遵循如下规则:
- 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。
- 中间不能有空格,可以使用下划线 _。
- 不能使用标点符号。
- 不能使用bash里的关键字(可用help命令查看保留关键字)。
有效的 Shell 变量名示例如下:
RUNOOBLD_LIBRARY_PATH_varvar2
无效的变量命名:
?var=123user*name=runoob
使用变量
使用一个定义过的变量,只要在变量名前面加美元符号即可,如:
实例
your_name="qinjx"echo $your_nameecho ${your_name}
只读变量
使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。
下面的例子尝试更改只读变量,结果报错:
实例
#!/bin/bashmyUrl="https://www.google.com"readonly myUrlmyUrl="https://www.runoob.com"
运行脚本,结果如下:
/bin/sh: NAME: This variable is read only.
删除变量
使用 unset 命令可以删除变量。语法:
unset variable_name
变量被删除后不能再次使用。unset 命令不能删除只读变量。
实例
#!/bin/shmyUrl="https://www.runoob.com"unset myUrlecho $myUrl
变量类型
运行shell时,会同时存在三种变量:
- 1) 局部变量 局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。
- 2) 环境变量 所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也可以定义环境变量。
- 3) shell变量 shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了shell的正常运行
Shell 字符串
字符串是shell编程中最常用最有用的数据类型(除了数字和字符串,也没啥其它类型好用了),字符串可以用单引号,也可以用双引号,也可以不用引号。
单引号
单引号字符串的限制:
- 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;
- 单引号字串中不能出现单独一个的单引号(对单引号使用转义符后也不行),但可成对出现,作为字符串拼接使用。
双引号
双引号的优点:
- 双引号里可以有变量
- 双引号里可以出现转义字符
拼接字符串
实例
your_name="runoob"# 使用双引号拼接greeting="hello, "$your_name" !"greeting_1="hello, ${your_name} !"echo $greeting $greeting_1# 使用单引号拼接greeting_2='hello, '$your_name' !'greeting_3='hello, ${your_name} !'echo $greeting_2 $greeting_3
输出结果为:
hello, runoob ! hello, runoob !hello, runoob ! hello, ${your_name} !
获取字符串长度
实例
string="abcd"echo ${#string} # 输出 4echo ${#string[0]} # 输出 4
变量为数组时,KaTeX parse error: Expected '}', got '#' at position 2: {#̲string}** 等价于 *…{#string[0]}:
提取子字符串
实例
以下实例从字符串第 2 个字符开始截取 4 个字符:
string="runoob is a great site"echo ${string:1:4} # 输出 unoo
查找子字符串
查找字符 i 或 o 的位置(哪个字母先出现就计算哪个):
实例
string="runoob is a great site"echo `expr index "$string" io` # 输出 4注意: 以上脚本中 ` 是反引号,而不是单引号 ',不要看错了哦。
Shell 数组
bash支持一维数组(不支持多维数组),并且没有限定数组的大小。
类似于 C 语言,数组元素的下标由 0 开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于 0。
定义数组
在 Shell 中,用括号来表示数组,数组元素用"空格"符号分割开。定义数组的一般形式为:
数组名=(值1 值2 ... 值n)例如:array_name=(value0 value1 value2 value3)或者array_name=(value0value1value2value3)还可以单独定义数组的各个分量:array_name[0]=value0array_name[1]=value1array_name[n]=valuen
注意:可以不使用连续的下标,而且下标的范围没有限制。
读取数组
读取数组元素值的一般格式是:
${数组名[下标]}
例如:
valuen=${array_name[n]}
使用 @ 符号可以获取数组中的所有元素,例如:
echo ${array_name[@]}
获取数组的长度
获取数组长度的方法与获取字符串长度的方法相同,例如:
实例
# 取得数组元素的个数length=${#array_name[@]}# 或者length=${#array_name[*]}# 取得数组单个元素的长度lengthn=${#array_name[n]}
shell注释
单行注释
以 # 开头的行就是注释,会被解释器忽略。
通过每一行加一个 # 号设置多行注释,像这样:
#--------------------------------------------# 这是一个注释
多行注释
多行注释还可以使用以下格式:
:<<EOF注释内容...注释内容...注释内容...EOF
实例
EOF 也可以使用其他符号:
:<<'注释内容...注释内容...注释内容...':<<!注释内容...注释内容...注释内容...!
Shell运算
Shell 传递参数
我们可以在执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为:$n。n 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推……
实例
以下实例我们向脚本传递三个参数,并分别输出,其中 $0 为执行的文件名(包含文件路径):
实例
#!/bin/bash# author:菜鸟教程# url:www.runoob.comecho "Shell 传递参数实例!";echo "执行的文件名:$0";echo "第一个参数为:$1";echo "第二个参数为:$2";echo "第三个参数为:$3";
为脚本设置可执行权限,并执行脚本,输出结果如下所示:
$ chmod +x test.sh $ ./test.sh 1 2 3Shell 传递参数实例!执行的文件名:./test.sh第一个参数为:1第二个参数为:2第三个参数为:3
另外,还有几个特殊字符用来处理参数:
参数处理 | 说明 |
---|---|
$# | 传递到脚本的参数个数 |
$* | 以一个单字符串显示所有向脚本传递的参数。 如"$*“用「”」括起来的情况、以"$1 $2 … $n"的形式输出所有参数。 |
$$ | 脚本运行的当前进程ID号 |
$! | 后台运行的最后一个进程的ID号 |
$@ | 与 ∗ 相 同 , 但 是 使 用 时 加 引 号 , 并 在 引 号 中 返 回 每 个 参 数 。 如 " *相同,但是使用时加引号,并在引号中返回每个参数。 如" ∗相同,但是使用时加引号,并在引号中返回每个参数。如"@“用「”」括起来的情况、以"$1" “ 2 " … " 2" … " 2"…"n” 的形式输出所有参数。 |
$- | 显示Shell使用的当前选项,与set命令功能相同。 |
$? | 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。 |
逻辑运算
不做过多赘述了,这里大家直接去参考菜鸟教程吧!
看我列出的知识点:
- bool
- test
- if···then
- while
- for
- until
- case … esac
- break
- continue
- 算数运算符
- 关系运算符
- 布尔运算符
- 字符串运算符
- 文件测试运算符
echo我们已经使用了很多了,printf也差不多,大家看一眼即可;
来吧给大家打开一个,传送门
Shell函数
linux shell 可以用户定义函数,然后在shell脚本中可以随便调用。
shell中函数的定义格式如下:
定义格式:
[ function ] funname [()]{ action;#代码体 [return int;] #返回值}
说明:
- 1、可以带function fun() 定义,也可以直接fun() 定义,不带任何参数。
- 2、参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。 return后跟数值n(0-255)
例子
#!/bin/bashdemoFun(){ echo "这是我的第一个 shell 函数!"}echo "-----函数开始执行-----"demoFunecho "-----函数执行完毕-----"
输出结果:
-----函数开始执行-----这是我的第一个 shell 函数!-----函数执行完毕-----
下面定义一个带有return语句的函数:
#!/bin/bashfunWithReturn(){ echo "这个函数会对输入的两个数字进行相加运算..." echo "输入第一个数字: " read aNum#read读取输入的数值 echo "输入第二个数字: " read anotherNum echo "两个数字分别为 $aNum 和 $anotherNum !" return $(($aNum+$anotherNum))}funWithReturnecho "输入的两个数字之和为 $?"
输出类似下面:
这个函数会对输入的两个数字进行相加运算...输入第一个数字: 1输入第二个数字: 2两个数字分别为 1 和 2 !输入的两个数字之和为 3
函数返回值在调用该函数后通过 $? 来获得。
函数参数
在Shell中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数…
带参数的函数示例:
#!/bin/bashfunWithParam(){ echo "第一个参数为 $1 !" echo "第二个参数为 $2 !" echo "第十个参数为 $10 !" echo "第十个参数为 ${10} !" echo "第十一个参数为 ${11} !" echo "参数总数有 $# 个!" echo "作为一个字符串输出所有参数 $* !"}funWithParam 1 2 3 4 5 6 7 8 9 34 73
输出结果:
第一个参数为 1 !第二个参数为 2 !第十个参数为 10 !第十个参数为 34 !第十一个参数为 73 !参数总数有 11 个!作为一个字符串输出所有参数 1 2 3 4 5 6 7 8 9 34 73 !
注意, 10 不 能 获 取 第 十 个 参 数 , 获 取 第 十 个 参 数 需 要 10 不能获取第十个参数,获取第十个参数需要 10不能获取第十个参数,获取第十个参数需要{10}。当n>=10时,需要使用${n}来获取参数。
另外,还有几个特殊字符用来处理参数:
参数处理 | 说明 |
---|---|
$# | 传递到脚本或函数的参数个数 |
$* | 以一个单字符串显示所有向脚本传递的参数 |
$$ | 脚本运行的当前进程ID号 |
$! | 后台运行的最后一个进程的ID号 |
$@ | 与$*相同,但是使用时加引号,并在引号中返回每个参数。 |
$- | 显示Shell使用的当前选项,与set命令功能相同。 |
$? | 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。 |
函数与命令的执行结果可以作为条件语句使用。
要注意的是,和 C 语言不同,shell 语言中 0 代表 true,0 以外的值代表 false。
实践小程序
作业:
- 完成一个二人猜年龄游戏,程序需要询问第一个人的年龄(x)是多少,然后再询问第二个人猜测第一个人的年龄(y)是多少,
y猜的比x大,反馈猜大了,相反,反馈猜小了,x=y时,游戏结束,恭喜答对;
- 大家还可以写下算法小程序,归并排序、快速排序、冒泡排序······
IO重定向
什么是IO重定向呢?
我们拆开理解,IO就是输入和输出,我们知道我们的程序都是在对数据的输入输出(传递),那什么是重定向呢,就是改变输入输出的方向,我们知道默认模式下,输入输出都是在终端,你对终端输入,程序对终端输出,但有时候我们又不想输入输出重定向,比如:日志和错误,都是应该保存文件中,供我们找出问题,查找问题,这时候就需要输入输出重定向了;
很简单:只需要知道、 >&、>、<<
命令 | 说明 |
---|---|
command > file | 将输出重定向到 file。 |
command < file | 将输入重定向到 file。 |
command >> file | 将输出以追加的方式重定向到 file。 |
n > file | 将文件描述符为 n 的文件重定向到 file。 |
n >> file | 将文件描述符为 n 的文件以追加的方式重定向到 file。 |
n >& m | 将输出文件 m 和 n 合并。 |
n <& m | 将输入文件 m 和 n 合并。 |
<< tag | 将开始标记 tag 和结束标记 tag 之间的内容作为输入。 |
需要注意的是文件描述符 0 通常是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。
这里做一个小案例即可:
我里有个程序,需要输入日志到执行位置,以便后期维护和查看:
程序代码:
#!/bin/bashecho '程序执行成功,没有bug'
需要输入到相同目录下的log文件中:
#新建文件和程序[root@localhost ~]# cd /home/test[root@localhost test]# lshelloNos.txt helloS.txt hello.t hello.txt[root@localhost test]# vim logTest.sh[root@localhost test]# lshelloNos.txt helloS.txt hello.t hello.txt logTest.sh[root@localhost test]# cat logTest.sh #!/bin/bashecho '程序执行成功,没有bug'[root@localhost test]# chmod +x logTest.sh #执行程序没有问题[root@localhost test]# ./logTest.sh 程序执行成功,没有bug
查看结果
#将程序执行日志放入log中,我们可以看到,我们程序执行产生的日志都会在log中,供我们维护和查看[root@localhost test]# ./logTest.sh > log[root@localhost test]# lshelloNos.txt helloS.txt hello.t hello.txt log logTest.sh[root@localhost test]# cat log程序执行成功,没有bug[root@localhost test]#
那么>>
就是想文件末尾追加,以及>&
和<<
不难理解了哈!
小结
希望篇文章对大家有帮助,可以用来参考!