一,什么是shell
Shell是一个命令解释器,它为用户提供了一个向Linux内核发送请求以便运行程序的界面系统级程序,用户可以用Shell来启动、挂起、停止甚至是编写一些程序。
如下图所示,最底层是硬件,然后通过内核来管理硬件,但外层的应用程序是内核无法识别的,需要先对程序进行“翻译”成内核可以识别的二进制,这就需要Shell命令解释器来完成。
- Shell是一个功能相当强大的编程语言,易编写,易调试,灵活性强。Shell是解释执行的脚本语言,在Shell可以直接调用Linux系统命令。
Linux支持的Shell
查看路径/etc/shells,可以看到所支持的Shell。
这些Shell之间是可以切换的,只要在命令行输入名称即可切换,输入切换成sh,输入exit退出。
二,shell脚本的执行方式
2.1 echo输出命令
echo [选项][输出内容]
选项:
-e: 支持反斜线控制的字符转换
下面举一些例子:
1,\b删除左侧字符
2,\t制表符,\n换行符
3,\x按照十六进制ASCII码输出字符
查表可知十六进制61的ASCII码为a,其他依次类推。
4,使输出带特定颜色
\e[1为开启颜色输出,\e[0m表示结束颜色输出,其中31m代表红色。color可替换为任意内容。
其他颜色:
30m=黑色 31m=红色 32m=绿色 33m=黄色
34m=蓝色 35m=洋红 36m=青色 37m=白色
2.2 第一个脚本
创建脚本
vim hello.sh
脚本开头加上以下一行内容,注意这不是注释,是标称,标称我以下写的内容是shell脚本,除了这一句,其他用#开头的就属于注释了。
#!/bin/Bash
然后随便写一些内容,保存退出。
2.3 执行脚本
有两种方法(第一种比较常用):
- 赋予执行权限,用相对路径或绝对路径执行。
chmod 755 hello.sh
#用绝对路径执行,注意这个sh文件夹是我自己创建的,用于储存脚本文件
/root/sh/hello.sh
#用相对路径执行,前提是已经在sh文件夹下
./hello.sh
- 通过Bash调用执行文本,不用添加执行权限。
bash hello.sh
注意:Windows下的shell脚本在Linux下执行时,记得要进行格式转换!,用以下命令:
dos2unix [要转换的文件] #Windows->Linux
unix2dos [要转换的文件] #Linux->Windows
三,bash的基本功能
3.1 历史命令与命令补全
history
选项:
-c: 清空历史命令(不建议使用)
-w: 把缓存中的历史命令写入历史命令保存文件~/.bash_history
注意,当前登录状态输入的命令不会保存在文件~/.bash_history,需要退出后才会保存,如果要立刻保存,则可以执行以下命令。
history -w [历史命令保存文件]
历史命令默认会保存1000条,可以在环境变量配置文件/etc/profile中进行修改。
vim /etc/profile
补充【历史命令的文件】
历史命令的调用
- 使用上、下箭头调用以前的历史命令。
- 使用“!n"重复执行第n条历史命令。
- 使用"!!"重复执行上一条命令。
- 使用“!子串”重复执行最后一条以该字串开头的命令。
3.2 别名与快捷键
3.2.1 设定命令别名
alias 别名='原命令'
3.2.2 查询命令别名
alias
3.2.3 命令执行时顺序
- 第一顺位执行用绝对路径或相对路径执行命令。
- 第二顺位命令执行别名。
- 第三顺位执行Bash的内部命令。
- 第四顺位执行按照$PATH环境变量定义的目录查找顺序找到的第一个命令。
例如,我用vi作为别名代替vim:
alias vi='vim'
其实vi本身也是一个命令,但原本的vi处于第四顺位,别名的优先级更高,所以我执行vi时就相当于执行vim,如果要执行原来的vi则需要用绝对路径,虽然如此,但不建议将原有命令作为别名。
在说说$PATH环境变量,其实在Linux中所有命令的执行都是用绝对路径完成的,但实际上我们在执行命令时只需要输入命令名称即可,这是因为这些命令的绝对路径储存在PATH环境变量中。
3.2.4 让别名永久生效
修改文件.bashrc
vi /root/.bashrc
按下列格式添加即可:
3.3 bash常用快捷键
3.4 输入输出重定向
标准输入输出
由于设备文件名比较难记,故用文件描述符来描述。
3.4.1 输出重定向
举个例子,当我输入一个指令时,输出应该是在屏幕上,但现在我不让它输出到屏幕,把它输出到文件中,也就是改变了它的输出方向,叫做输出重定向。但为什么要输出到文件中呢?因为该文件可作为一个执行脚本,让系统在指定的时间可以执行该脚本。
3.4.2 标准输出重定向
例如,我将命令ls输出的结果保存在一个文件中:
ls > abc #将命令ls输出的结果保存在一个文件中
然后看一下文件abc的内容:
cat abc
我们会发现ls命令输出的结果已经保存在文件中:
如果们需要追加一些命令的输出,则需要用>>,不能用>,否则会将原输出覆盖。
3.4.3 标准错误输出重定向
例如将ls打成lsd,然后把错误输出到abc文件中,则需要多加一个2:
lsd 2>> abc
此时错误已经输出到了文件中。
实际上这种将正确输出和错误输出分别保存的方法用处不大,因为错误输出的前提是我们已经知道了这个命令是错误的,那我们把它改为正确的就好。
下面这种方式才是常用的:
前四种方法都是一样的,即把正确的输出和错误的输出同时保存在同一个文件中。
3.4.4 输入重定向
我们之前的输入都是通过键盘来输入的,如果我们通过文件来输入,这就叫输入重定向(用的不多,了解即可)。
wc [选项][文件名]
选项:
-c 统计字节数
-w 统计单词书
-l 统计行数
输入wc统计键盘输入命令,随便添加一些内容,然后Ctrl+d退出,会出现三个数字,从左到右依次是行数,单词数和字符数(包括回车)
输入重定向的格式如下:
输入重定向的格式如下:
3.5 多命令顺序与管道符
管道符
命令1的正确输出作为命令2的操作对象。
命令1 | 命令2
来看看管道符有什么用,如果我们想要查看etc下的文件,则可输入:
ll -a /etc/
但会发现文件非常多,不符合我们的查看习惯,这时候我们就可以用分页显示命令more,但如果单独执行more,无法分屏显示etc下的文件,那么我们可以使用管道符,在**ll -a /etc/**成功执行的情况下执行more。
ll -a /etc/ | more
3.6 通配符与其他特殊符号
3.6.1 通配符
如果我们要删除一个目录下的所有文件,就可以用*来表示。
例如我们可以进入文件夹tmp,删除该目录下所有文件,一定要确保已经进入tmp目录,别把根目录删了!!!
rm -rf *
然后我们再创建以下几个文件,来看看其他通配符的作用
touch abc
touch abcd
touch 123
touch 0abc
显示字符后面带有abc的文件:
ls *abc
3.6.2 其他特殊符号
【PDF补全】
四,Shell变量
4.1 用户变量
4.1.1 变量设置规则
- 变量名称可以由字母、数字和下划线组成,但不能以数字开头。
- 在Bash中,变量的默认类型都是字符串型,如果要进行数值运算,则必须修改指定变量类型为数值型。
- 变量用等号连接值,等号左右两侧不能有空格。
- 变量的值如果有空格,需要使用单引号或双引号包括。
- 在变量的值中,可以使用“\”转义符。
- 如果需要增加变量的值,那么可以进行变量值的叠加。不过变量需要用双引号包含“$变量名”或用${变量名}包含,这里比较抽象,下面的本地变量那里会有例子。
- 如果是把命令的结果作为变量值赋予变量,则需要使用反引号或$()包含命令,例如我们将命令date的输出结果赋值给变量,则需要写成:
name=$(date)
- 环境变量名建议大写,便于区分。
4.1.2 变量分类
- 用户自定义变量。
- 环境变量:这种变量主要保存的是和系统操作环境相关的数据。
- 位置参数变量:这种变量主要是用来向脚本当中传递参数或数据的,变量名不能自定义,变量作用是固定的。
- 预定义变量:是Bash中已经定义好的变量,变量名不能定义,变量作用也是固定的。
从上到下限制越来越严格!
4.1.3 本地变量
即:用户自定义变量
变量定义:
a=12
变量叠加:
a=12
b=${a}34
变量调用:
echo $b
变量查看:
set
变量删除(例如删除变量a):
unset a
4.2 环境变量
用户自定义变量只在当前的Shell中生效,而环境变量会在当前Shell和这个Shell的所有子Shell当中生效。如果把环境变量写入相应的配置文件,那么这个环境变量就会在所有的Shell中生效。
例如我们在命令行中输入bash就会进入子Shell,我们用命令pstree就可以查看进程树,可以看到我们当前所在的Shell。
如果我们用命令exit退回到父Shell,在父Shell中声明环境变量,那么此时在它的所有子Shell中该环境变量也生效。
用户自定义变量和环境变量的区别?
- 用户自定义变量只对当前的shell生效
- 环境变量会对当前shell和这个shell所有的子目录生效
4.2.1 设置环境变量
申明变量:
export 变量名=变量值
查询变量:
env
删除变量:
unset 变量名
环境变量也是可以直接用echo调用的,和本地变量调用方法一样。
4.2.2 系统常见环境变量
PATH
系统查找命令的路径PATH,用冒号分割。
这也解释了为什么我们执行命令时不需要用绝对路径,如果在这几个路径中都找不到我们输入的命令,那么该命令就会报错,如果我们把自己写的shell脚本拷贝到这些路径下,也可以不需要输入绝对路径或者相对路径就可以执行,但如果要这么做,推荐下面这种方法。
添加路径到PATH中:
例如我把我用来装shell文件的文件夹添加到PATH中。(临时生效,重启失效,永久需改变配置文件)
PATH=$(PATH):/root/sh
PS1
PS1是定义系统提示符的变量。
\d:显示日期,格式为“星期 月 日”。
\h:显示简写主机名。如默认主机名“localhost”。
\t:显示24小时制时间,格式为“HH:MM:SS”。
\T:显示12小时制时间,格式为“HH:MM:SS”。
\A:显示24小时制时间,格式为“HH:MM”。
\u:显示当前用户名。
\W:显示当前所在目录的完整名称。
\w:显示当前所在目录的完整路径。
\#:执行的第几个命令。
\$:提示符,如果是root用户会显示提示符为“#”,如果是普通用户会显示提示符为“$”。
我们先来看看当前PS1的默认值:
然后我们可以来修改一下,例如我让**\h也就是输出用户名改为输出时间\t**。
对比发现,提示符用户名的位置确实变成了时间,如果希望显示其他东西,可以自行定义提示符。
4.3 位置参数变量
解释一下上面的参数变量:
例如我创建一个shell脚本,然后输入内容如下:
然后执行该脚本:
chmod 755 canshu1.sh
./canshu1.sh
我们发现并没有参数输出,这是因为我们没有写入参数,如果我们在执行脚本文件前传入相应的参数,例如我这里传进12,13,14作为参数传给$1, $2 ,$3,再执行则会有相应的参数输出:
./canshu1.sh 12 13 14
创建一个加法脚本:
#!/bin/bash
sum=$(( $1+$2 ))
echo $sum
[root@markling user1]# ./jiafa.sh 11 22 #输入第一个和第一个参数
33
则会相应输出12 13 14
-
- $n
创建一个shell脚本,然后输入内容如下:
随意输入一些参数并执行脚本:
chmod 755 canshu2.sh ./canshu2.sh 11 222 333 4 555 66 7
注意:$*和$@虽然输出一样,但$*是把所有参数看成一个整体,相当于一个参数,而$@代表的参数是独立开来的。
-
- $*,$@,$#
4.4 预定义变量
4.4.1 预定义变量
解释一下上面的变量:
例如执行命令ls后,我输入echo $?,输出0,则说明ls正确执行。
即127是预定义的错误输出
[root@markling ~]# find /root -name hellow.sh & #执行find命令放到后台
[1] 25878
[root@markling ~]# echo $! #输出最后一个后台的进程号
25878
[1]+ Done find /root -name hellow.sh
[root@markling ~]# $$
-bash: 25674: command not found #输出当前进程号
[root@markling ~]# echo $$
25674
创建一个shell脚本,然后输入内容如下,&是指将该脚本放入后台:
执行脚本:
chmod 755 canshu3.sh
./canshu3.sh
4.4.2 接受键盘输入
read [选项][变量名]
选项:
-p "提示信息":在等待read输入时,输入提示信息。
-t 秒数:read命令会一直等待输入,使用此选项可以指定等待时间。
-n 字符数:read命令智慧接收指定的字符数,就会执行。
-s 隐藏输入的数据,使用与机密信息的输入。
例如创建一个脚本,写入如下内容:
#!/bin/bash
read -t 30 -p "Please input your name:" name
#提示输入名字并等待30s,把用户的输入保存在变量name中
echo "Name is $name"
read -s -t 30 -p "Please enter your age:" age
#年龄是隐私,用“-s”隐藏输入
echo -e "\n"
read -n 1 -t 30 -p "Please select your gender[M/F]:" gender
#使用“-n 1”选项只接收一个输入字符就会执行(都不用输入回车)
echo -e "\n"
echo "Sex is $gender"
-
- $?
- $$,$!