Shell的作用是解释执行用户的命令,用户输入一条命令,Shell就解释执行一条,这种方式成为交互式(Interactive)。Shell还有一种执行命令的方式称为批处理(Batch),即用户事先写一个Shell脚本(Script),其中有很多条命令,让Shell一次把这些命令执行完,而不必一条一条地敲命令。Shell脚本和编程语言很相似,也有变量和流程控制语句,但Shell脚本是解释执行的,不需要编译。
用户在命令行输入命令后,一般情况下Shell会fork并exec该命令,但是Shell的内建命令例外,执行内建命令相当于调用Shell进程中的一个函数,并不创建新的进程。以前学过的cd、alias、umask、exit等命令即是内建命令,凡是用which命令查不到程序文件所在位置的命令都是内建命令,内建命令没有单独的man手册,要在man手册中查看内建命令,应该
$ man bash-builtins
如export、shift、if、eval、[、for、while等等。内建命令虽然不创建新的进程,但也会有Exit Status,通常也用0表示成功非零表示失败,虽然内建命令不创建新的进程,但执行结束后也会有一个状态码,也可以用特殊变量$? 读出。
Shell脚本中用#表示注释,但如果#位于第一行开头,并且是#!则例外,它表示该脚本使用后面指定的解释器/bin/sh解释执行。
vim t1.sh
#! /bin/sh
cd ..
ls
权限不足的 两种执行方式 (ls -l 查看权限)
第一种: /bin/sh t1.sh
第二种: chmod a+x t1.sh ./t1.sh
执行过程:
Shell会fork一个子进程并调用exec执行./script.sh这个程序,exec系统调用应该把子进程的代码段替换成./script.sh程序的代码段,并从它的_start开始执行。然而script.sh是个文本文件,根 本没有代码段和_start函数,怎么办呢?其实exec还有另外一种机制,如果要执行的是一个文本文件,并且第一行 指定了解释器,则用解释器程序的代码段替换当前进程,并且从解释器 的_start开始执行,而这个文本文件被当作命令行参数传给解释器。因此,执行上述脚本相当于执行程序
按照惯例,Shell变量由全大写字母加下划线组成,有两种类型的Shell变量:
1.环境变量
环境变量可以从父进程传给子进程,因此Shell进程的环境变量可以从当前Shell进程传给fork出来的子进程,用printenv命令可以显示当前Shell进程的环境变量
2.本地变量
只存在于当前Shell进程,用set命令可以显示当前Shell进程中定义的所有变量(包括本地变量和环境变量)和函数。
环境变量和本地变量的区别:
环境变量是任何进程都有的概念,而本地变量是Shell特有的概念。在Shell中,环境变量和本地变量的定义和用法相似。在Shell中定义或赋值一个变量:
VARNAME=value
注意等号两边都不能有空格,否则会被Shell解释成命令和命令行参数。
本地变量 转为 环境变量:
一个变量定以后仅存在于当前Shell进程,它是本地变量,用export命令可以把本地变量导出为环境变量,定义和导出环境变量通常可以一步完成:
export VARNAME=value
也可以分两步完成:
VARNAME=value
export VARNAME
env 打印的是环境变量
set 显示当前进程所有变量
先用 VARNAME=value 定义好本地变量diner=gongbaojiding,然后进行测试验证。
用unset命令可以删除已定义的环境变量或本地变量。
unset VARNAME
注意: 在定义变量时不用 , 取 变 量 值 时 用 ,取变量值时用 ,取变量值时用,和C语言不通的是,Shell变量不需要明确定义类型,事实上Shell变量的值都是字符串,比如我们定义VAR=45,其实VAR的值是字符串45而非整数。Shell变量不需要先定义后使用,如果对一个没有定义的变量取值,则值为空字符串
————
接收用户输入(重点)
语法:read -p 提示信息 变量名 三个空格
案例:编写一个脚本test6.sh,要求执行之后提示用户输入文件的名称(路径),然后自动为用户创建该文件
#touch test6.sh
vim test6.sh
#!/bin/sh
read -p ‘请输入需要创建的文件路径:’ filepath
touch $filepath
echo ‘文件创建成功’
ls -l $filepath
:x
用于匹配的字符成为通配符
通配符
* 匹配0个或多个任意字符
? 匹配一个任意字符
[若干字符] 匹配方括号中任意一个字符的一次出现
[root@localhost ~]# touch ch0.doc ch1.doc ch2.doc chh.doc chch.doc
[root@localhost ~]# ls ch*.doc
ch0.doc ch1.doc ch2.doc chh.doc chch.doc
[root@localhost ~]# ls ch?.doc
ch0.doc ch1.doc ch2.doc chh.doc
[root@localhost ~]# ls ch[0-2].doc
ch0.doc ch1.doc ch2.doc
由 反引号 括起来的也是一条命令,Shell先执行该命令,然后将输出结果立刻代换到当前命令行中,例如定义一个变量存放date命令的输出:
$ DATE=`date`
$ echo $DATE
Sun Mar 15 17:41:45 PST 2020
命令代换也可以用$()表示:
$ DATE=$(date)
用于算术代换,$ (())中的Shell变量取值将转换成整数,同样含义的$[]等价例如:
$ VAR=45
$ echo $(($VAR+3))
$(())中只能用+-*/和()运算符,并且只能整数运算。
$ [base#n],其中base表示进制,n按照base进制解释,后面再有运算数,按十进制解释
echo $[2#10+11]
echo $[8#10+11]
echo $[10#10+11]
$ echo $SHELL
/bin/bash
$ echo \$SHELL
$SHELL
$ echo \\
\
比如创建一个文件名为" $ $ "的文件可以这样:
$ touch \$\ \$
还有一个字符虽然不具有特殊含义,但是要用它做文件名也很麻烦,就是-号。
即使加上\转义也还是报错
因为各种UNIX命令都把-号开头的命令行参数当作命令的选型,而不会当作文件名。如果非要处理以-号开头的文件名,可以有两种办法:
$ touch ./-hello
或者
$ touch -- -hello
\还有一种用法,在\后敲回车表示续行,Shell并不会立刻执行命令,而是把光标移到下一行,给出一个续行命令提示符>,等待用户继续输入,最后把所有的续行接到一起当作一个命令执行。例如:
$ ls \
> -l
(ls -l命令的输出)
补充:
默认表通配符
\* 表乘号
单引号:
单引号用于保持引号内所有字符的字面值,即使引号内的 \ 和回车也不例外,但是字符不能出现单引号。其次,如果引号没有配对就输入回车,Shell会给出续行提示符,要求用户把引号配上对。例如:
$ echo 'SHELL'
$SHELL
$ echo 'ABC\(回车)
> DE'(再按一次回车结束命令)
ABC\
DE
双引号:
被双引号括住的内容,将视为单一字串,它防止通配符扩展,但允许变量扩展。
双引号括起来的变量会展开,单引号的不会展开
关于单双引号的问题:
双引号能够识别变量,双引号能够实现转义 (类似于 “ * ” 表乘号)
单引号是不能识别变量,只会原样输出。
if [ $a -eq $b ]
then
echo "$a -eq $b : a 等于 b"
else
echo "$a -eq $b: a 不等于 b"
fi
1.关系运算符
2.逻辑运算符
3.字符串运算符
[ = ] 是字符串比较, [ == ]是数值比较
4.文件测试运算符(重点)
文件测试运算符用于检测 Unix /Linux 文件的各种属性。 特有的
块设备——U盘,光盘 重点记住绿色的
5.shell脚本附带选项运算符
问题描述:在Linux shell中如何处理tail -n 10 access.log这样的命令行选项
步骤:
调用tail指令
系统内核把后续选项传递给tail
tail先去打开指定的文件
取出最后10行
$0表脚本 $1表参数1 $2表参数2…
❤练习:创建自定义指令 “ user ”,可以直接执行(#user -add xx),要求该指令具备以下语法和功能:
a.#user -add 用户名 【添加用户】
b.#user -del 用户名 【删除用户及其家目录】
(原本系统自带的是 useradd 和 userdel)
userdel -r USERNAME,-r代表把用户相对应的目录一并删除
[root@localhost ~]# vim useradd.sh
[root@localhost ~]# chmod +x ./useradd.sh
[root@localhost ~]# ./useradd.sh -add user0405
#cat /etc/passwd
#ls /home/ 验证成功
但这个不是完整的,要自定义指令,所以就
#vim ~/.bashrc //添加链接 ,即别名
alias user=’/root/useradd.sh’
#su //切换用户使刚才的操作生效
#user -add aabbcc