• 一、Shell概述


    什么是Shell

    解释Shell脚本名词之前,我们先来说下什么是Shell?

    Shell是一个命令解释器,它在操作系统的最底层,负责直接与用户对话,把用户的输入解释给操作系统,并处理各种各样的操作系统的输出结果,输出到屏幕返回给用户。这种对话方式可以是交互的方式(从键盘输入命令,可以立即得到shell的回应),或非交互(脚本)的方式。

    命令行解释器:使用户得以与内核进行沟通的“翻译官”

    下面图中黄色部分就是命令解释器shell。

    提示:Shell在英文中的意思是贝壳,从上图我们可以看出,命令解释器shell就像一个贝壳一样包住了系统核心。

    解释:

    用户下达指令,shell把用户的指令翻译成`系统内核`能识别的二进制数,然后传达给系统内核,系统内核会把指令进行查看分析,然后下达“进程调度”和“资源分配”指令给CPU,让CPU去执行,然后由计算机硬件来显示结果。

    什么是shell脚本

    Linux  Shell脚本 :实现某种功能的,有执行权限的文本文件

    内核的作用:资源分配和进程调度

    命令或语句不在命令行执行,而是通过一个程序文件执行时,该程序就被称为shell脚本或Shell程序,shell程序很类似DOS系统下的批处理程序(*.bat)。用户可以在shell脚本中敲入一系列的命令或命令语句。这些命令、变量和流程控制语句等有机的结合起来就形成了一个功能强大的shell脚本。

     Windows下利用批处理程序bat开发的备份网站及数据库的脚本

    @echo off
    set date=%date:~0,4%-%date:~5,2%-%date:~8,2%
    mysqldump -uroot -poldboy -A -B > D:ak"%date%".sql
    rar.exe a -k -r -s -m1 D:ak"%date%".sql.rar D:ak"%date%".sql
    del D:ak*.sql
    rar.exe a -k -r -s -m1 D:ak"%date%"htdocs.rar D:workPHPnowhtdocs

    范例1:清除.var/log下messages日志文件的简单命令脚本

    #把所有命令放在一个文件里堆积起来就形成了脚本,下面就是一个最简单的命令堆积形成的shell脚本。

    #必须使用root身份来运行这个脚本

    #清除日志脚本,版本1

    cd /var/log
    cat /dev/null > messages
    echo "Logs cleaned up."
    #提示:/var/log/messages是系统的日志文件

    看完这个脚本有什么想法?

    1)不是root执行不了
    
    2)没有流程控制,就是没有逻辑性

    范例2:包含命令、变量和流程控制语句的清除/var/log下messages日志文件的shell脚本

    #!/bin/bash
    #清除日志脚本,版本2
    LOG_DIR=/var/log
    ROOT_UID=0   #$UID=0的时候,用户才具有root用户的权限
    #要使用root用户来运行.
    if [ "$UID" -ne "$ROOT_UID" ]
    then
        echo "Must be root to run this scripts."
        exit 1
    fi
    cd $LOG_DIR || {
        echo"Cannot change to necessary directory." >&2
        exit 1
    }
    
    cat /dev/null > messages && echo "Logs cleaned up."
    exit 0 
    #退出之前返回0表示成功,返回1表示失败

    扩展:

    清除日志的三种方法:

    [root@Web ~]# echo >test.log 
    [root@Web ~]# >test.log 
    [root@Web ~]# cat /dev/null >test.log 

    应用场景:保留文件,清空内容

     脚本在执行时会启动一个子shell进程;

    1.命令行中启动的脚本会继承当前shell环境变量;

    2.系统自动执行的脚本(非命令行启动)就需要自我定义需要各环境变量;

    编写脚本

    1.指定脚本的执行环境  #!/bin/bash

    2.注释信息

    3.脚本的功能体

    进程和shell的关系

    1.在每个进程看来,当前主机上只存在内核和当前进程,不知道别的进程存在

    2.进程是程序的副本,进程是程序执行实例

    3.进程可同名,进程号不同就是不同的进程

    4.进程是程序的副本,进程是程序执行示例

    5.多个用户同时登录shell,每个Shell都会有所不同,彼此之间各不相干

    6.允许同一个用户登录多次,每个Shell都会有所不同,彼此之间各不相干

    7.同一个用户在不同地方登录的Shell都会不同,都对应不同的Shell进程
    8.bash自身是一个外部程序,但启动以后会带有内部命令
    9.父Shell的设定对于子Shell是无效的,所之亦然。
    10.shell之间可以交互打开

    shell脚本与perl,php,python语言的差别?

      Shell的优势在于处理操作系统底层的业务(大量的命令为它做支撑,例如,grep,sed,awk)。

    一键安装,报警脚本,常规的业务应用,shell开发更简单快速。

      php,python优势在于开发运维工具,web界面的管理工具

     Shell脚本很擅长处理纯文本类型的数据,而linux中几乎所有的配置文件、日志文件(如nfs,rsync,httpd,nginx,lvs等)都是纯文本类型的文本。因此,如果学好Shell脚本语言,就可以利用它Linux系统中发挥巨大的作用。

    Linux系统支持的Shell

    类型                说明
    bsh:Bourne Shell        70年代中期诞生于贝尔实验室,脚本编程功能较强
    csh                80年代早期诞生于加利福尼亚大学,使用C语言风格,命令行交互方便
    ksh:Korn Shell         兼容Bsh的语法和C语法的交互特性
    bash:Bourne-Again Shell    bsh的升级版,吸取了ksh的一些特点,是大多数Linux的默认Shell程序
    
    zsh                兼备各种Shell程序的优点,交互式操作效率更高

      Shell脚本语言是弱类型语言,较为通用的shell有标准的Bourne shell (sh)和 C shell (csh)。其中 Bourne shell (sh)。其中 Bourne shell (sh)已经被bash shell取代。

    当前的Linux主机支持哪些Shell

    #cat /etc/shells
    /bin/sh                 //多数Unix默认的Shell
    /bin/bash            //多数Linux默认使用的Shell
    /sbin/nologin      //非登录Shell
    /bin/tcsh           
    /bin/csh          
    /bin/csh         
    在RHEL系统中,实际上sh是bash的符号链接

    CentOS Linux系统默认的shell是():

    [root@Web ~]# echo $SHELL 
    /bin/bash
    [root@Web ~]# grep root /etc/passwd
    [root@Web ~]# grep  root /etc/passwd
    root:x:0:0:root:/root:/bin/bash

     Bash的基本特性

    Tab键补全、快捷键
    命令历史
    命令别名
    标准输入输出
    重定向
    管道操作

     

     

    Tab键补全

    命令补全    搜索PATH环境变量所指定的每个路径下以我们给出的字符串 开头的可执行文件,如果多于一个,两次tab,可以给出列表;否则将直接补全;
    路径补全    搜索我们给出的起始路径下的每个文件名,并试图补全

    快捷键

    快捷键    注释
    Ctrl+a    跳到命令行首
    Ctrl+e    跳到命令行尾
    Ctrl+u    删除光标至命令行首的内容
    Ctrl+k    删除光标至命令行尾的内容
    Ctrl+l    清屏

    命令历史

    默认记录1000条最近执行过的命令

    存放位置:~/.bash_history

    环境变量    注释
    PATH      命令搜索路径
    HISTSIZE    命令历史缓冲区大小

    命令历史:history

    参数选项    注释
    -c    清空命令历史
    -d OFFSET [n]    删除指定位置的命令
    -w    保存命令历史至历史文件中

    命令历史的使用技巧:

    参数选项    注释
    !n       执行命令历史中的第n条命令
    !-n      执行命令历史中的倒数第n条命令;
    !!       执行上一条命令;
    !string    执行命令历史中最近一个以指定字符串开头的命令
    !$       引用前一个命令的最后一个参数
    Esc, .    
    Alt+.    

    命令别名

    语法:

    alias CMDALIAS='COMMAND [options] [arguments]'

    有效范围

    在shell中定义的别名仅在当前shell生命周期中有效
    别名的有效范围 仅为当前shell进程

    相关操作

    相关操作                注释
    aliase              列出已定义的命令别名
    unaliase 别名          删除指定的命令别名
    unaliase -a           清空所有别名
    alias 别名='实际命令'      定义新的命令别名

    如何让定义的别名命令生效?

    vim  ~/.bashrc    只对某个用户生效
    vim /etc/bashrc    对所有用户生效

    标准输入输出

    1.标准输入:从该设备接收用户输入的数据

    2.标准输出:从该设备向用户输出的数据

    3.标准错误:通过该设备报告执行中的出错信息

    类型      设备文件      文件描述号    默认设备
    标准输入    /dev/stdin      0        键盘
    标准输出    /dev/stdout     1        显示器
    标准错误    /dev/stderr     2        显示器

    重定向

    重定向:改变标准输入/输出/错误输出的方向

    set -C    禁止对已经存在文件使用覆盖重定向
            强制覆盖输出,则使用 >|
    set +C    关闭上述功能

    类型

    类型                    操作符          用途
    重定向输入                  <        将文本输入来源由键盘改为指定的文件
    重定向输出                  >        将命令行的正常执行输出保存到文件,而不是直接显示在屏幕上
                           >>        与>类似,但操作是追加而不是覆盖
    重定向错误                  2>       将命令行的执行错误信息保存到文件,而不是直接显示在屏幕上
                            2>>       与2>类似,但操作是追加而不是覆盖
    混合重定向(重定向正确和错误输出信息)      &>      相当于>和2>,覆盖到同一个文件
                           &>>      相当于>和2>>,追加到同一个文件

    管道

    管道:前一个命令的输出,作为后一个命令的输入

    管道操作符”|“    
    cmd1 | cmd2 [...|cmdn]    将cmd1的输出结果,作为cmd2的输入

    例如:

    #pgrep -l "." | wc -l————统计运行的进程数
    #ifconfig | grep  "HWaddr"————查看本机的MAC地址信息

    shell脚本的建立和执行

     shell脚本(bash shell程序)通常是在编辑器(如vi/vim)中编写,由Unix/Linux命令、bash shell命令、程序结构控制语句和注释等内容组成。

    脚本开头(第一行)

      一个规范的shell脚本第一行会指出由哪个程序(解释器)来执行脚本中的内容,在linux bash编程中一般为:

    #!/bin/bash
    或
    #!/bin/sh

    “#!”又称为幻数,在执行bash脚本的时候,内核会根据它来确定该用哪个程序来解释脚本中的内容。这一行必须在脚本顶端的第一行,如果不是则为注释。

    sh和bash的区别:

    [root@Web ~]# ls -l /bin/sh
    lrwxrwxrwx. 1 root root 4 9月  25 2014 /bin/sh -> bash
    [root@Web ~]# ll /bin/sh 
    lrwxrwxrwx. 1 root root 4 9月  25 2014 /bin/sh -> bash

    提示:sh为bash的软链接,这里推荐用标准写法#!/bin/bash

    下面是linux常用脚本语言开头的编码写法,不同语言脚本的开头一般都要加上如相应语言的开头标识内容

    #!/bin/sh
    #!/bin/bash
    #!/usr/bin/awk
    #!/bin/sed
    #!/usr/bin/tcl
    #!/usr/bin/expect
    #!/usr/bin/perl
    #!/usr/bin/env python

     Centos和RedHat Linux下默认的Shell均为bash。因此,在写shell脚本的时候,我们的脚本的开头也可以不加#!/bin/bash。但如果当前的shell非你默认的shell时,比如tcsh,那么就必须要写#!了。否则脚本文件就只能执行命令的集合,不能够使用shell内建的指令了,建议读者养成习惯,不管什么脚本最好都加上开头语言标识。

    如果脚本的开头不指定解释器,那么,就要用对应的解释器来执行脚本。例如:

    bash test.sh
    python test.py

    脚本注释

    shell脚本中,跟在(#)井号后面的内容表示注释,用来对脚本进行注释说明,注释部分不会被当作程序执行,仅仅是给人看的,系统解释器看不到的,注释可自成一行,也可以跟在脚本命令后面与命令在同一行。开发脚步时,如果没有注释,其他人就很难理解脚本究竟在做什么,时间长了自己也会忘记。因此,我们要尽量养成对所做的工作(脚本等)书写注释的习惯,不光是方便别人,也方便自己,否则,写完一个脚本后也许几天后就记不起脚本的用途了,再重新阅读也会浪费很多宝贵时间。对于团队的协作也不利。

    Shell脚本的执行

    shell脚本以非交互式的方式运行时,它会先查找环境变量ENV,该变量指定了一个环境文件(通常是.bashrc),然后从该环境变量文件开始执行,当读取了ENV文件后,SHELL才开始执行shell脚本中的内容。

    Shell脚本的执行通常可以采用一下三种方式:

    bash script-name 或sh script-name(推荐使用)
    path/script-name或./script-name(当前路径下执行脚本)
    source script-name或. script-name    #注意”.”点号

    执行说明:

    1.第一种方法是当脚本文件本身没有可执行权限(即文件x位为-号)时常使用的方法,这里推荐使用bash执行,或者文件开头没有指定解释器(推荐的方法)

    2.第二种方法需要先将脚本文件的权限改成可执行文件(即文件加x位),具体方法:chmod u+x script-name或chmod 755 script-name。

    然后通过脚本路径就可以直接执行脚本了。

    在生产环境中,不少读者在写完shell脚本后,由于疏忽忘记给予该脚本文件执行权限,就直接应用了,结果导致脚本没有按自己的意愿手动或定时执行。

    避免的方法是用第一种方法替代第二种。

    3.第三种方法是使用source或者”.”点号读入或加载指定的shell脚本文件(如san.sh),然后依次执行指定shell脚本文件san.sh中的所有语句。这些语句将作为当前父shell脚本father.sh进程的一部分运行。因此,使用source或者”.”点号可以将san.sh自身脚本中的变量的值或函数等的返回值传递到当前的父shell脚本father.sh中使用。这是第三种方法和前两种的最大区别。也是值得读者注意的地方。

    Source或者'.'点号命令的功能是在当前shell中执行source或者'.'点号加载并执行的相关脚本文件中的命令及语句,而不是产生一个子shell来执行命令文件中的命令

    下面我们举例说明

    [zgy@Web ~]$ cat >test.sh                   #编辑test.sh脚本文件
    
    echo 'I am zgy' echo 'I am zgy'  

    输入"echo 'I am oldboy'"内容后按回车,然后在按ctrl+d组合键即可结束编辑。此操作作为特殊编辑方法,作为cat用法的扩展提及(在使用中去记忆是个好习惯)。

    [zgy@Web ~]$ cat test.sh  
    
    echo 'I am zgy'   
    
    [zgy@Web ~]$ sh test.sh         #使用第一种方式的sh命令执行test.sh脚本文件
    
    I am zgy
    
    [zgy@Web ~]$ bash test.sh      #使用第一种方式的bash命令执行test.sh脚本命令
    
    I am zgy

    我们使用第一种方法,发现均可以执行并得到预期的结果。

    zgy@Web ~]$ ls -l test.sh
    -rw-rw-r--. 1 zgy zgy 22 4月  11 12:45 test.sh
    
    [zgy@Web ~]$ ./test.sh         #使用第二种方式"./"在当前目录下执行test.sh脚本文件,可以发现,这个地方无法自动补全。
    -bash: ./test.sh: 权限不够    #提示:此处没有可执行权限

    但是可以用source或者"."点号执行。

    [zgy@Web ~]$ . test.sh
    I am zgy
    [zgy@Web ~]$ source test.sh
    I am zgy

    提示:"."点号和source命令的功能相同,都是读入脚本并可以执行脚本

    test.sh加执行权限

    [zgy@Web ~]$ chmod u+x test.sh 
    [zgy@Web ~]$ ./test.sh 
    I am zgy

    可以看到,给test.sh加完可执行权限就可以执行了。

    现在测试第三种source或者"."点号的特殊的传递变量值到当前shell的例子

    [zgy@Web ~]$ echo 'userdir=`pwd`'>testsource.sh  #行的内容通常用echo很方便。
    [zgy@Web ~]$ cat testsource.sh
    userdir=`pwd`
    [zgy@Web ~]$ sh testsource.sh
    [zgy@Web ~]$ echo $userdir

    #此处为空,并没有出现当前路径输出,这是为什么呢?

    根据上面例子,可以发现,通过sh或bash命令执行过的脚本,脚本结束后在当前shell窗口查看userdir变量的值,发现变量值是空的。现在以同样的步骤改用source执行,然后再看看userdir变量的值

    [zgy@Web ~]$ source testsource.sh 
    [zgy@Web ~]$ echo $userdir
    /home/zgy

    再看看系统Nfs服务的脚本如何使用”.”号的。

    # Source function library.
    . /etc/rc.d/init.d/functions

    提示:操作系统及服务自带的脚本是学习的标杆和参考(虽然有时感觉不是很规范)

    结论:

    通过source或”.”点号加载执行过的脚本,在脚本结束后脚本中的变量值(包括函数)在当前Shell中依然存在,而是sh和bash则不行。

    因此,在做Shell脚本开发时,如果脚本中有需求引用其他脚本的内容或者配置文件时,最好用”.”点号或source在脚本开头加载该脚本或配置文件,

    然后再下面的内容用可以调用source加载的脚本及文件中的变量及函数等内容

    某互联网公司Linux运维职位实际面试笔试

    1、已知如下命令返回结果,请问echo $user的返回的结果为()
    [zgy@Web ~]$ cat test.sh
    user=`whoami`
    [zgy@Web ~]$ sh test.sh
    [zgy@Web ~]$ echo $user
    问:执行echo $user命令的结果是什么?
    1)当前用户
    2)zgy
    3)空(无内容输出)   #这个是正确答案

     

    Shell脚本开发基本规范及习惯

    1)开头指定脚本解释器

    #!/bin/sh或#!/bin/bash

    2)开头加版权版本等信息

    #Date: 16:29 2015-4-11
    
    #Author Created by zgy
    
    #Mail 1092327070@qq.com
    
    #Function:  This scripts function is...
    
    #Version 1.1

    3)脚本中不用中文注释

    尽量用英文注释,防止本机或切换系统环境后中文乱码的困扰

    4)脚本以.sh为扩展名

    例:script-name.sh

    5)代码书写优秀习惯

    1.成对内容的一次写出来,防止遗漏。如:

    []、{}、''、``、""

    2.[]中括号两段要有空格,书写时即可留出空格[  ],然后在退格书写内容。

    3.流程控制语句一次书写成,再添加内容,如:

     if语句格式一次完成:

    if  条件内容
    then  
    内容
    fi

    for语句格式一次完成:

    for  
    do
       内容
    done

    提示:while和util,case等语句也是一样

    6)通过缩进让代码易读

    if 条件内容
    then
        内容
    fi

    提示:好的习惯可以避免很多不必要的麻烦,提升很多的工作效率。

  • 相关阅读:
    Muduo 网络编程示例之五: 测量两台机器的网络延迟
    “过家家”版的移动离线计费系统实现
    一种自动反射消息类型的 Google Protobuf 网络传输方案
    Muduo 设计与实现之一:Buffer 类的设计
    为什么 muduo 的 shutdown() 没有直接关闭 TCP 连接?
    Muduo 网络编程示例之八:用 Timing wheel 踢掉空闲连接
    C++ 工程实践(5):避免使用虚函数作为库的接口
    分布式系统中的进程标识
    Ormlite在一般java环境中操作Sqlite
    android游戏开发框架libgdx的使用(十八)—简单的AVG游戏效果实现
  • 原文地址:https://www.cnblogs.com/zhongguiyao/p/8933731.html
Copyright © 2020-2023  润新知