shell编程是在Linux的基础知识中所必须掌握的语言,它也是一个命令的集合,如果学会的话,我们将会事半功倍,将重复的操作通过shell编程脚本来实现,减轻了一定的负担,那么现在就来讲一下shell编程的基础。

一、编程语言的分类

  其实根据其语言的分类来讲,强类型的编程语言首先要编译成为该平台的二进制程序文件所能运行,当程序文件可以运行时,编译器就不在参与,而在弱类型的编程语言当中,使用解释器将源代码边解释边执行,那么相当于前者来说,其执行的速度比后者要快得多,但是如果编译完成之后有问题的话,就会查询整个源代码开始调试,这也就是常说的debug,解决完成之后还得需要在进行编译一次,虽然很麻烦,但是的确执行很快,而解释型的语言虽然效率来讲没有那么快,但是在程序运行当中万一某个逻辑功能错误的话,只需改一下就可以运行,不用经过编译,两种都有长处和短处。那么我们来总结一下编程语言的分类:

   编程语言的分类:根据运行方式来进行分类
    编译运行:源代码 --> 编译器(编译) --> 程序文件;
  解释运行:源代码 --> 运行时启动解释器,由解释器边解释边运行;

  所以我们可以分为两类,一种是脚本语言,这里我们指的是shell编程,那么另一种是完全独立的编程语言,根据其编程过程中功能的实现是调用库还是调用外部的程序文件。可分为:

   shell脚本编程:
  利用系统上的命令及编程组件进行编程;
  完整编程:
  利用库或编程组件进行编程;

  而完整编程并不需要操作系统上必须事先存在其某个命令才能执行,它自己通过库的各种功能和所写的代码来完成操作。另外无论是脚本编程还是完整编程可分为另外两类,根据其编程模型来进行分类:一种是过程式编程语言,而另一种是对象式编程语言,我们又称之为面向过程和面向对象编程语言。
  我们之前也讲过,程序是由数据+指令组成
程序=数据+指令,那么在过程式语言而言,以数据+指令来说是以指令为中心来组织代码,其数据服务于代码,根据代码来组织数据结构,这是第一种,而第二种我们称之为对象式编程语言,它的特性就在于是以数据为中心来组织代码,围绕数据来组织指令(换句话说就是指令服务于数据)
  对于过程式语言来说,其程序的执行逻辑无非就是顺序执行、选择执行和循环执行,最好的代表就是C语言和Bash编程。
  而在对象式语言当中,首先要生成许多对象,相当于是一种特殊的数据结构,开发程序时要编写许多的类(class),类可以实例化对象,其代表的编程语言有:Java和C++以及Python语言等。
  我们来总结一下其编程模型:

   编程模型:过程式编程语言,对象式编程语言(面向对象)
    程序=指令+数据
       过程式:以指令为中心来组织代码,且数据服务于代码;
           顺序执行:依次执行;
           选择执行:符合某个逻辑条件执行部分的程序代码;
           循环执行:将循环体中的代码反复执行n次;
           代表:C, bash
       对象式:以数据为中心来组织代码,围绕数据来组织指令(且指令服务于数据)
           类(class):实例化对象,method;
           代表:Java, C++, Python

  而shell脚本是过程式编程语言,它是依靠解释器来解释运行的,并非是独立完整语言,需要依赖于外部的程序文件来运行。

二、如何编写Shell脚本

  所有的编程语言都要遵循某种编程格式,在脚本文件的第一行的顶格处声明某个shell解释器路径的类型,我们称之为给出shebang,用于指明执行当前脚本的解释其程序文件。那么在脚本编程当中常见的解释器有以下几类:

   #!/bin/bash
  #!/usr/bin/perl
  #!/usr/bin/python

  我们在编写shell的源代码时,是使用普通的文本编辑器来进行编辑,在Linux当中编辑器分为两类,一种是行编辑器,另一种是全屏幕的编辑器,比如说vim编辑器就是全屏幕的编辑器,但是在刚刚上手时的门槛比较高一点,所以我们用另一个全屏幕的编辑器,nano编辑器。

   文本编辑器:nano
    行编辑器:sed
    全屏幕编辑器:nano, vi, vim

  我们来进行一个实例,我们复制/etc/fstab文件到/tmp文件来,用nano进行编辑,我们直接使用nano来打开,然后是可以直接编辑,输入一个How Are You,其界面如下:
1512539935(1).jpg  编辑完成之后,使用快捷键Ctrl+o就会写入,然后给你一个提示为:File Name to Write: /tmp/fstab,如果你确认保存的话直接回车,然后Ctrl+x退出。
  需要注意的是,如果你使用的是最小化安装的,默认是不会有该程序,所以你可能需要安装来解决这一问题。
  我们可以来查看一下:

# cat /tmp/fstab

1512540205(1).jpg

  那么shell脚本是什么呢?或者说如何写?我们来总结一下:

   命令的堆积;
  但很多命令不具有幂等性,需要用程序逻辑来判断条件是否满足,以避免其运行中发成错误;
  (所谓幂等性就是重复率较高的命令,执行结果都一样的那种)

  我们来写第一个脚本:

   # nano first.sh
  #!/bin/bash
  id user3 || useradd user3
  echo "user3" | passwd --stdin user3

  mktemp -d /tmp/test.XXXX

  判断user3是否存在,如果不存在就创建该用户user3并且设置密码也为user3,最后建立一个在/tmp目录下建立一个临时目录。将脚本保存完成之后,我们就可以开始运行该脚本,那么运行该脚本有两种方法,一种是赋予权限,另一种是直接运行解释器,方法总结如下:

   运行脚本:
    (1) 赋予该脚本执行权限,并直接运行该程序文件;
        chmod +x /PATH/TO/SCRIPT_FILE
        /PATH/TO/SCTIPT_FILE
        ./SCRIPT_FILE
    (2) 直接运行解释器,将脚本以命令行参数传递给解释器程序;
        bash /PATH/TO/SCRIPT_FILE

  运行结果:

   # chmod +x first.sh
  # ./first.sh
  useradd: user 'user3' already exists
  Changing password for user user3.
  passwd: all authentication tokens updated successfully.
  /tmp/test.47Gf

  需要值得注意的是,在脚本中的空白行都会被解释器忽略,而且在脚本当中,除了在第一行定格写的shebang,剩下的所有以#号开头的行,都会被视作注释行而被忽略;说明它为注释行,那么shell脚本的运行是通过一个子shell进程来实现的。
  练习:写一个脚本,实现如下功能:
  (1) 显示/etc目录下所有以大写p或小写p开头的文件或目录本身;
  (2) 显示/var目录下的所有文件或目录的本身,并将显示结果中的小写字母转换为大写后显示;
  (3) 创建临时文件:/tmp/myfile.XXX

#!/bin/bash
echo -e "Show some under /etc \n"
ls -d /etc/[pP]*
echo

echo -e "Traslate lower to upper \n"
ls -d /var/* | tr 'a-z' 'A-Z'
echo

echo -e "Create a temp file \n"
mktemp /tmp/myfile.XXXX


三、bash的配置文件

  我们知道,在shell中所定义的变量和执行的命令,其生命周期也是在当前shell进程,退出之后就会被注销,重新登录shell的话之前所定义的变量和执行的命令机制也不会再有,这就像改朝换代一样,所以,我要成立联合政府,那么在bash当中,要想突破这个历史周期率,我们将定义好的命令功能等写入到配置文件当中,那么在bash当中的配置文件定义分为两大类,一类为profile类,另一类是bashrc类。

   profile类:为交互式登录的shell进程提供配置;
  bashrc类:为非交互式登录的shell进程提供配置;

  那么它们各自的区别是有以下方面,首先我们介绍以下登录类型。
  在交互式登录的shell进程就是我们通过在终端或命令行以及远程连接来进行登录,那其交互式登录shell进程总结的如下:

   直接通过某终端输入账号和密码打开的shell进程;
  使用su命令:su - USERNAME,或者使用su -l USERNAME执行的登录切换;

  还有一个是非交互式登录的shell进程,无需登录,就可以打开终端,其如下解释:

   su USERNAME执行的登录切换;
  图形界面下打开的终端;
  运行脚本;

  对于profile和bashrc类来说,其特性也是并不一样,在profile中,有两个子类,分别是全局和用户,其功用也是不一样的,总结如下:

   全局:对所有用户都生效;
    /etc/profile
    /etc/profile.d/*.sh
   
  用户个人:仅对当前用户有效;
    ~/.bash_profile
   
  功用:
    1、用于定义环境变量;
    2、运行命令或脚本;

  那么另一类就是bashrc类,也是有两个子类,也是全局和用户,其功用也是不一样的,总结如下:

   全局:
    /etc/bashrc

  用户个人:
    .bashrc
   
  功用:
    1、定义本地变量;
    2、定义环境别名;

  需要注意的是:仅管理员可修改全局配置文件;
  对于其系统的读取次序而言,全局和个人的配置文件的一次为:
  交互式登录shell进程:

   /etc/profile --> /etc/profile.d/* --> ~/.bash_profile --> ~/.bashrc --> /etc/bashrc

  非交互式登录的shell进程:

   ~/.bashrc --> /etc/bashrc --> /etc/profile.d/*

  不过在命令行中定义的特性,例如变量和别名作用域当作当前shell进程的生命周期;
  而在配置文件定义的特性,只对随后新启动的shell进程有效;
  不过针对以上的特性,我们可以通过让配置文件定义的特性立即生效;

   (1) 通过命令行重复定义一次;
  (2) 让shell进程重读配置文件:
    # source /PATH/FROM/CONF_FILE
    . /PATH/FROM/CONF_FILE

  由此,我们练习两个问题:
  问题1:定义所有用户都生效的命令别名,例如:pings='192.168.1.1'。

   # vim /etc/profile.d/pings.sh
  alias pings='ping 192.168.1.1'

  问题2:让centos用户登录时,提供其已经登录,并显示当前系统时间。

   # useradd centos
  # cd /home/centos
  # vim .bashrc
    echo "login sucessful"
    echo `date +"%F %T"`
  # su - centos