shell脚本编程:
编程语言的分类:根据运行方式
编译运行:源代码----->编译器(编译)---->程序文件
C语言:
解释运行:源代码--->运行时启动解释器,由解释器边解释边运行;
根据其编程过程中功能的实现是调用库还是调用外部的程序文件:
shell脚本编程:
利用系统上的命令及编程组件进行编程;
完成编程:
利用库或编程组件运行编程
编程模型:
过程式编程语言,面向对象的编程语言
程序=指令+数据
过程式:以指令为中心来组织代码,数据是服务于代码;
顺序执行
选择执行
循环执行
(C语言,bash脚本语言)
对象式:以数据为中心来组织代码,围绕数据来组织指令;
类(class):实例化对象,method;
(JAVA,C++,Python)
shell脚本编程:(特性)
(1)过程式编程
(2)解释运行
(3)依赖于外部程序文件
如何写shell脚本:
脚本文件的第一行,顶格写,给出shebang,解释器路径,用于指明解释执行当前脚本的解释器程序文件
常见的解释器:
#!/bin/bash
#!/usr/bin/python
#!/usr/bin/perl
文本编辑器:nano
行编辑器:sed
全屏幕编辑器:nano,vim,vi
使用namo命令:
ctrl+G:求助 ctrl+O:写入 ctrl+R:读档 ctrl+Y:上页 ctrl+V:下页
ctrl+K:剪切文字 ctrl+U:还原剪切 ctrl+C:游标位置 ctrl+T:拼写检查
ctrl+X:离开 ctrl+J:对齐
一般而言shell脚本都以“.sh”结尾。
shell脚本是什么?
命令的堆积;
但是很多命令不具有幂等性,需要用程序逻辑来判断运行条件是否满足,以避免其运行 中发生错误;
编写第一个shell脚本:
nanomyshell.sh
脚本内容为:
#!/bin/bash
useradduser3
echo"user3" | passwd --stdin user3
mktemp-d /tmp/test.XXX
最后脚本写完后,直接按“ctrl+X”保存退出
要想执行脚本,先看一下这个脚本的权限
[[email protected]~]# ls -l myshell.sh
-rw-r--r--.1 root root 86 Oct 26 22:51 myshell.sh
再看当前用户是以什么身份登入的:
[[email protected]~]# whoami
root
给用户加执行权限:
[[email protected]~]# chmod +x myshell.sh
[[email protected]~]# ls -l myshell.sh
-rwxr-xr-x.1 root root 86 Oct 26 22:51 myshell.sh
[[email protected]~]#
最后执行脚本:
[[email protected] ~]# ./myshell.sh
Changing password for user user3.
passwd: all authentication tokens updatedsuccessfully.
/tmp/test.noG
[[email protected] ~]#
注意当我们再次运行上面的脚本时,我们的脚本还会执行,只是第一个创建用户的命令创建失败了,下面的两条命令执行成功了。
这说明,在shell脚本中并不是所有的命令失败,都会导致脚本终止的,而是命令执行失败,如果产生严重的错误,这种严重的错误比如会“exit”,一般而言任何脚本遇到“exit”都会终止或者我们使用某种判断机制将其强行终止。或者我们的脚本运行中出现了语法错误。脚本才有可能终止。
所以我我们可以将上面的脚本创建用户时,加一个判断,用户在就不创建,不在就创建,那么就是将第一个命令改为“id user3||useradd user3”这样再次执行脚本就不会报错了
运行脚本:
(1)赋予执行权限,并执行运行此程序文件;
chmod +x /PATH/TO/SCRIPT_FILE //一加x所有的用户都有了执行权限
./SCRIPT_FILE //相对路径
/PATH/TO/SCRIPT_FILE //绝对路径
(2)直接运行解释器,将脚本以命令行参数传递给解释器程序
bash /PATH/TO/SCRIPT_FILE
(解释:
使用解释器运行脚本,本质上其实就是利用了linux内核有一个特性,他可以通过文本的前n个字节,来判定这是一个什么格式的文件,尤其是看到文件前两个字节是“#!”就知道后面是shell脚本程序文件,我们不需要直接运行这个文件,而是先运行“#!”后面的解释器程序,然后由这个解释器程序来解释运行这个文件中的程序代码。)
演示:
[[email protected] ~]# ll myshell.sh
-rw-r--r--. 1 root root 98 Oct 26 23:12myshell.sh
[[email protected] ~]# bash myshell.sh
uid=502(user3) gid=502(user3)groups=502(user3)
Changing password for user user3.
passwd: all authentication tokens updatedsuccessfully.
/tmp/test.BkL
[[email protected] ~]#
这也说明了,shell是严重依赖命令的,不然怎么说shell脚本是命令的堆积呢
练习1:写一个脚本,实现如下功能:
(1)显示/etc/目录下所有以大写P或小写p开头的文件或目录本身;
(2)显示/var目录下的所有文件或目录本身,并将显示结果中的小写字母转换为大写后显示:
(3)创建临时文件/tmp/myfile.XXXX;
解:
#!/bin/bash
ls -d /etc/[Pp]*
ls -d /var/* | tr [:lower:] [:upper:]
或
ls -d /var/* | tr ‘a-z‘ ‘A-Z‘
mktemp /tmp/myfile.XXXX
(拓展:
上面的脚本我们还可以进行优化:
上面的脚本执行过程中没有执行后的反馈,我们可以进一步的进行完善:
如下:
#!/bin/bash
echo "Show some under /etc" //echo的内容都是直接显示在屏幕上
ls -d /etc/[Pp]*
echo "Traslate lower to upper"
ls -d /var/* | tr ‘a-z‘ ‘A-Z‘
echo "Create a temp file"
mktemp /tmp/myfile.XXXX
)
注意:
(1)脚本中的空白行直接被忽略,所以不会输出空白行
(2)脚本中,除了shebang,余下所有以#开头的行,都会被视作注释行而被忽略;此 即为注释行
(3)如果想从脚本中输出一个空白行,则在脚本中加上一行,并且这行中只写一个echo
(4)shell脚本的运行是通过运行一个子shell进程实现的;
bash的配置文件:(定义别名)
(我们之前讲的,在shell进程中定义的别名,当我们的系统重启以后,当我们的shell登出再登录以后,我们在shell下定义的特性,包括变量,他的生命周期仅是当前shell进程,如果想突破这样的生命周期,我们应该在他的配置文件中定义)
bash程序有两大类的配置文件:
profile类
bashrc类
profile类:为交互式登录的shell进程提供配置
bashrc类:为非交互式登录的shell进程提供配置
登录类型:
交互式登录shell进程;
直接通过某终端输入账号和密码后登录打开的shell进程;
使用su命令:su -USERNAME,或者使用 su -l USERNAME执行的登录切换;
非交互式登录shell进程:
suUSERNAME执行的登录切换;
图形界面下打开的终端;
运行脚本时
profile类:
全局:对所有用户都生效;
/etc/profile
/etc/profile.d/*.sh
用户个人:仅对当前用户有效;
~/.bashrc_profile
功能:
(1)用于定义环境变量;
(2)运行命令或脚本;
bashrc类:
全局:
/etc/bashrc
用户个人:
~/.bashrc
功用:
(1)定义本地变量;
(2)定义命令别名;
注意:
(1)定义命令别名时,定义在全局文件“/etc/profile”或“/etc/bashrc”还是定义在用户个人文件“~/.bashrc_profile”或“~/.bashrc”文件中,要看我们自己的需要,如果是想要定义对所有的用户有效的命令别名,则就定义在文件“/etc/profile”或“/etc/bashrc”中,如果是只想对特定用户有效,那么就将命令别名定义在用户本身的家目录文件“~/.bashrc_profile”或“~/.bashrc”中。
(2)只用管理员用户才有权限定义全局的,普通用户是没有权限进行编辑的。
(3)当我们真正登录一个shell时,shell有这么多的配置文件,那么我们的shell应该先读取哪一个,后读取哪一个文件?
举例:我们在全局文件中,定义“name=jerry”,在个人配置文件中定义“name=tom”,那么这个变量name,到底等于谁?是先读取的生效,还是后读取的生效?
解:
后读的才是最终生效,因此配置文件的读取的次序才是至关重要。
配置文件的读取次序:
交互式登录shell进程:
/etc/profile------->/etc/profile.d/*------->~/.bash_profile------>~/.bashrc----->/etc/bashrc
非交互式登录shell进程:
~/.bashrc------>/etc/bashrc------->/etc/profile.d/*
(所以我们自己编辑的脚本在运行时,他的环境是取决于“~/.bashrc---->/etc/bashrc---->/etc/pprofile.d/*”这三个文件的,如果是登录系统是,则就取决于“/etc/profile---->/etc/profile.d/*----->~/.bashrc_profile------>~/.bashrc------>/etc/bashrc”这五个文件的。此前我们也知道,在命令行中定义的,不会永久有效,但是在change文件中定义的他不会立即有效,在配置文件中定义的,只对下次新登录的shell进程有效,对以往的老shell进程是无效的,)
命令行中定义的特性,例如变量和别名作用于当前的shell进程的生命周期,即立即生效
配置文件中定义的特性,只对随后新启动的shell进程有效;
让通过配置文件定义的特性立即生效:
(1)通过命令行重复定义一次;
(2)让shell进程重读配置文件;
重读配置文件,执行下面的命令:(点号就相当于source)
~]#source /PATH/FROM/CONF_FILE
或
~]#. /PATH/FROM/CONF_FILE(配置文件的路径)
注意:定义别名,主要是定义在/etc/bashrc或~/.bashrc文件中。
需求:
如果我们希望每次用户登录成功以后,都会echo一下,显示说“欢迎登录系统,你已经进入监控区域,你的所有行为都将被记录,请注意您的言行”那么应该怎么实现呢?
分析:
要想让用户登录以后,能执行命令,应该在bashrc的配置文件中定义还是在profile文件中定义?我们知道,profile命令的两个功能:(1)用于定义环境变量;(2)运行命令或脚本;那么我们应该在全局中写还是在个人的家目录中写?如果要想让所有的用户登陆后都能显示,那就全局写,否则就在个人目录下写。
假设我们在全局下写,我们应该写在那呢?
profile类的文件有两大类,(1)/etc/profile(2)/etc/profile.d/*,那么我们要是在/etc/profile.d/目录下自己建立一个目录,以后管理也好管理,不用的时候直接删除就行。
因此在“/etc/profile.d/”下创建一个文件名随便起,但是要以“.sh”结尾的文件例如:
该文件叫“welcome.sh”
编辑的内容为:
echo "welcome aboard....."
然后当我们再次登录一个shell进程时,就会在shell刚开始前面出现我们编写内容:
所以,如果我们想在用户登录时给一些提示信息,那么我们就可以在“/etc/profile.d/”目录下定义一个“.sh”结尾的文件,或者在“/etc/profile”文件中定义。
有些配置不会立即生效,但是对后来的新登录的窗口有效。
我们还可以在这样的配置文件中定义一些环境变量的。
举例:
输出当前系统下的JAVA环境所在,我们通常输出一个环境变量叫JAVA_HOME
创建一个文件,vim /etc/profile.d/java.sh,(因为输出环境变量使用“export”或“declare -x”)然后在写的文件内容为:“export JAVA_HOME=/usr”这样就定义了一个环境变量“JAVA_HOME”,但是这么文件不会立即有效,需要重读这个配置为文件,使用命令点“.”或“source”重读文件java.sh,然后执行一下命令“export”或“declare -x”即可显示新定义的环境变量。
问题:
(1)如何定义对所有用户都生效的命令别名,例如 lftps=‘lftp 10.1.0.1/pub‘
(2)如何实现centos用户登录时,提示其已经登录,并显示当前系统时间(提示显示当前系统时间,可以通过命令引用来实现,也可以直接写在文件中)
(3)如何实现root用户的PATH环境查询多几个查询路径