美文网首页
shell介绍

shell介绍

作者: 4335151e8554 | 来源:发表于2019-07-10 09:55 被阅读0次

    语言分类

    任何代码最终都要被“翻译”成二进制的形式才能在计算机中执行。

    有的编程语言,如 C/C++、Pascal、Go语言、汇编等,必须在程序运行之前将所有代码都翻译成二进制形式,也就是生成可执行文件,用户拿到的是最终生成的可执行文件,看不到源码。

    这个过程叫做编译(Compile),这样的编程语言叫做编译型语言,完成编译过程的软件叫做编译器(Compiler)。

    而有的编程语言,如 Shell、JavaScript、Python、PHP等,需要一边执行一边翻译,不会生成任何可执行文件,用户必须拿到源码才能运行程序。程序运行后会即时翻译,翻译完一部分执行一部分,不用等到所有代码都翻译完。

    这个过程叫做解释,这样的编程语言叫做解释型语言或者脚本语言(Script),完成解释过程的软件叫做解释器。

    编译型语言的优点是执行速度快、对硬件要求低、保密性好,适合开发操作系统、大型应用程序、数据库等。

    脚本语言的优点是使用灵活、部署容易、跨平台性好,非常适合 Web 开发以及小工具的制作。

    Shell 就是一种脚本语言,我们编写完源码后不用编译,直接运行源码即可。

    shell脚本概述

    1️⃣shell的种类非常多,我这里学习bash

    sh 第一个流行的 Shell,是UNIX上的标准shell
    csh
    tcsh
    ash
    bash 是Linux上的默认shell
    

    在现代的 Linux 上,sh 已经被 bash 代替,/bin/sh往往是指向/bin/bash的符号链接
    查看当前默认使用的shell,输出 SHELL 环境变量即可查看

    echo $SHELL
    /bin/bash
    

    2️⃣shell脚本的文件扩展名为sh,扩展名并不影响使用,见名知意就好

    3️⃣shell脚本都以#!/bin/bash作为第一行,用于指定解释器,第一行的#不代表注释,除了第一行,其他各行如果以#开头都表示注释

    4️⃣命令不需要分号结尾

    5️⃣如何进入shell。我们平时输入命令的地方就是使用shell的地方,比如命令行终端,可以直接在里面输入shell命令。下面写出的代码不仅仅可以在shell脚本里写,也可以在终端输入

    5️⃣创建第一个shell脚本
    创建一个名为first.sh的文件

    vi first.sh
    

    输入如下内容并保存

    #!/bin/bash    //这一行用于指定解释器信息,否则调用方式为/bin/bash first.sh
    pwd
    

    #!是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即表示使用哪一种Shell;
    后面的/bin/bash就是指明了解释器的具体位置。如果不加第一行,那么在调用命令时需要指定使用哪个解释器

    修改权限为可执行

    chmod 766 first.sh
    

    全路径执行该文件(因为该命令还未加入环境变量配置的路径中,系统无法自行找到)

    /Users/goat/Desktop/first.sh 
    

    命令行输出结果为当前路径/Users/goat/Desktop

    6️⃣定义变量,注意=两边都不能有空格

    name="chenlong"
    age=12
    

    还可以使用语句给变量赋值,语句需要用$()包起来,或者``飘号包起来

    path1=$(pwd)
    或者
    path1=`pwd`
    

    变量的使用,使用一个定义好的变量,需要在变量名前加$

    name="chenlong"
    name2=$name
    echo $name
    echo $(name)
    

    只读变量,使用readonly命令可以将变量定义为只读,一旦定义不可被修改

    name="chenlong"
    readonly name
    

    删除变量,不能删除只读变量

    unset name
    

    变量类型

    运行shell时会同时存在三种变量
    1、局部变量  在脚本或命令中定义,只在当前shell实例中生效
    2、环境变量  所有的程序,包括shell启动的程序都能访问环境变量,
               shell脚本也可以定义环境变量
    3、shell变量  由shell程序设置的特殊变量
    

    7️⃣字符串,可以用单引号,可以用双引号,也可以不用引号

    name=chenlong
    name="chenlong"
    name='chenlong'
    单引号字符串的特殊情况:
    变量在单引号中无效,会原样输出,对单引号使用转义字符也无效
    单引号字符串中不可出现单个的单引号,要成对出现,作为字符串拼接使用
    

    获取字符串长度

    name="chenlong"
    ${#name}
    

    截取字符串

    name="chenlong"
    ${name:1:4}    //从第2个字符开始截取4个字符
    

    字符串拼接
    在脚本语言中,字符串的拼接(也称字符串连接或者字符串合并)往往都非常简单,例如:
    在 PHP 中,使用.即可连接两个字符串;
    JavaScript 中,使用+即可将两个字符串合并为一个。
    然而,在 Shell 中你不需要使用任何运算符,将两个字符串并排放在一起就能实现拼接

    name="Shell"
    url="http://c.biancheng.net/shell/"
    str1=$name$url  #中间不能有空格
    str2="$name $url"  #如果被双引号包围,那么中间可以有空格
    str3=$name": "$url  #中间可以出现别的字符串
    str4="$name: $url"  #这样写也可以
    str5="${name}Script: ${url}index.html"  #这个时候需要给变量名加上大括号
    

    8️⃣数组
    bash支持一维数组,不支持多维,数组元素用空格分开

    array_name=("chenlong" "hua" "wu")
    

    读取数组

    ${array_name[下标值]}
    

    使用@*可以获取数组中的所有元素,例如:

    ${nums[*]}
    ${nums[@]}
    
    nums=(29 100 13 8 91 44)
    echo ${nums[@]}  #输出所有数组元素
    nums[10]=66  #给第10个元素赋值(此时会增加数组长度)
    echo ${nums[*]}  #输出所有数组元素
    echo ${nums[4]}  #输出第4个元素
    
    运行结果:
    29 100 13 8 91 44
    29 100 13 8 91 44 66
    91
    

    获取数组长度,跟获取数组长度很像

    ${#array_name[@]} 或者 ${#array_name[*]}
    ${#array_name[下标值]}    //获取单个元素长度
    

    示例

    nums=(29 100 13)
    echo ${#nums[*]}
    #向数组中添加元素
    nums[10]="http://c.biancheng.net/shell/"
    echo ${#nums[@]}
    echo ${#nums[10]}
    #删除数组元素
    unset nums[1]
    echo ${#nums[*]}
    
    运行结果:
    3
    4
    29
    3
    

    数组合并
    拼接数组的思路是:先利用@或*,将数组扩展成列表,然后再合并到一起。具体格式如下:

    array_new=(${array1[@]}  ${array2[@]})
    array_new=(${array1[*]}  ${array2[*]})
    

    示例

    array1=(23 56)
    array2=(99 "http://c.biancheng.net/shell/")
    array_new=(${array1[@]} ${array2[*]})
    echo ${array_new[@]}  #也可以写作 ${array_new[*]}
    
    运行结果:
    23 56 99 http://c.biancheng.net/shell/
    

    9️⃣条件命令
    test用于检查条件是否成立,可以检查数值、字符串和文件
    if 语句的判断条件,从本质上讲,判断的就是命令的退出状态。
    下面是数值测试常用参数

    -eq 等于则为真
    -ne 不等则为真
    -gt 大于则为真
    -ge 大于或等于则为真
    -lt 小于则为真
    -le 小于或等于则为真
    

    示例

    num1=100
    num2=200
    if test $[num1] -eq $[num2]
    then 
        echo "相等"
    else
        echo "不相等"
    fi
    

    下面是字符串测试常用参数

    =   等于则为真
    !=  不等于则为真
    -z  字符串长度为零则为真
    -n  字符串长度不为零则为真
    

    示例

    name1="chenlong"
    name2="chenlong"
    if test $name1 = $name2
    then
        echo "字符串相等"
    else
        echo "字符串不相等"
    fi
    
    if test -z $name1
    then
        echo "字符串长度为零"
    else
        echo "字符串长度不为零"
    fi
    

    文件测试常用命令

    -e 文件存在则为真
    -r 文件存在且可读
    -w 文件存在且可写
    -x 文件存在且可执行
    -s 文件存在且至少有一个字符
    -d 目录存在
    -f 为普通文件
    -c 文件存在且为字符型特殊文件
    -b 文件存在且为块特殊文件
    

    示例

    if test -e ./test.txt
    then
        echo "文件存在"
    else
        echo "文件不存在"
    fi
    

    逻辑连接符

    -a  与
    -o  或
    !   非
    

    流程控制
    shell流程控制的各个条件分支执行代码不可为空,如果为空则不要写这个分支
    break命令跳出循环
    continue命令跳出当前循环

    if 条件
    then
        代码...
    else
        代码...
    fi
    
    if 条件
    then
        代码...
    elif 条件
    then
        代码...
    else
        代码...
    fi
    
    for num in 1 2 3 1 2
    do
        echo "$num"
    done
    
    while()
    do
    
    done
    
    //无限循环
    while true
    do
    
    done
    或者
    for ((;;))
    
    until 条件    //条件为真则停止
    do
    
    done
    
    case 值 in
    值1)
        代码...
        ;;
    值2)
        代码...
        ;;
    esac
    

    函数
    函数返回,可以显式的return返回,并且返回值是整数,整数范围0~255。也可以不加return返回,如果不加那么默认返回最后一条命令的运行结果

    不带return的函数

    say(){
        echo "说话"
    }
    

    带return的函数,函数调用后,可以通过$?拿到返回值

    add(){
        echo "请输入第一个数字"
        read num1
        echo "请输入第二个数字"
        read num2
        echo "求和"
        return $(($num1+$num2))
    }
    
    add
    
    echo "结果为 $?"
    

    函数调用直接使用函数名即可,需要注意的是函数需要先定义再使用

    say
    

    函数参数,在函数内部可以通过$n的形式获取参数,$1表示第一个参数...

    需要注意的是$后面跟的数字如果超过1位数,那么要用大括号括起来,否则无效

    funWithParam(){
        echo "第一个参数为 $1 !"
        echo "第二个参数为 $2 !"
        echo "第十个参数为 $10 !"
        echo "第十个参数为 ${10} !"
        echo "第十一个参数为 ${11} !"
        echo "参数总数有 $# 个!"
        echo "作为一个字符串输出所有参数 $* !"
    }
    funWithParam 1 2 3 4 5 6 7 8 9 34 73
    

    其他处理参数的字符

    $# 参数个数
    $* 以一个单字符串显示所有向脚本传递的参数
    $$ 脚本运行的当前进程id号
    $! 后台运行的最后一个进程id号
    $@ 与$*相同,但是使用时加引号,并在引号中返回每个参数。
    $- 显示Shell使用的当前选项,与set命令功能相同。
    $? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误
    

    学习了上面的函数等知识后,下面探究一下shell命令的选项和参数本质上是什么

    • 一个 Shell 内置命令就是一个内部的函数,一个外部命令就是一个应用程序。内置命令后面附带的所有数据(所有选项和参数)最终都以参数的形式传递给了函数,外部命令后面附带的所有数据(所有选项和参数)最终都以参数的形式传递给了应用程序。

    • 也就是说,不管是内置命令还是外部命令,它后面附带的所有数据都会被“打包”成参数,这些参数有的传递给了函数,有的传递给了应用程序。

    • 有编程经验的读者应该知道,C语言或者 C++ 程序的入口函数是int main(int argc, char *argv[]),传递给应用程序的参数最终都被 main 函数接收了。从这个角度看,传递给应用程序的参数其实也是传递给了函数。

    • 有了以上认知,我们就不用再区分函数和应用程序了,我们就认为:不管是内置命令还是外部命令,它后面附带的数据最终都以参数的形式传递给了函数。实现一个命令的一项重要工作就是解析传递给函数的参数。

    • 注意,命令后面附带的数据并不是被合并在一起,作为一个参数传递给函数的;这些数据是由空格分隔的,它们被分隔成了几份,就会转换成几个参数。例如getsum -s 1 -e 100要向函数传递四个参数,read -n 1 sex要向函数中传递三个参数。

    • 并且,命令后面附带的数据都是“原汁原味”地传递给了函数,比如getsum -s 1 -e 100要传递的四个参数分别是 -s、1、-e、100,减号-也会一起传递过去,在函数内部,减号-可以用来区分该参数是否是命令的选项。

    • 至于在函数内部如何解析这些参数,对于外部命令来说那就是 C/C++ 程序员的工作了。

    shell命令的本质

    • Shell 自带的命令称为内置命令,它在 Shell 内部可以通过函数来实现,当 Shell 启动后,这些命令所对应的代码(函数体代码)也被加载到内存中,所以使用内置命令是非常快速的
    • 更多的命令是外部的应用程序,一个命令就对应一个应用程序。运行外部命令要开启一个新的进程,所以效率上比内置命令差很多。
    • 用户输入一个命令后,Shell 先检测该命令是不是内置命令,如果是就执行,如果不是就检测有没有对应的外部程序:有的话就转而执行外部程序,执行结束后再回到 Shell;没有的话就报错,告诉用户该命令不存在

    一个外部的应用程序究竟是如何变成一个 Shell 命令的呢?

    • 应用程序就是一个文件,只不过这个文件是可以执行的。既然是文件,那么它就有一个名字,并且存放在文件系统中。用户在 Shell 中输入一个外部命令后,只是将可执行文件的名字告诉了 Shell,但是并没有告诉 Shell 去哪里寻找这个文件。

    • 难道 Shell 要遍历整个文件系统,查看每个目录吗?这显然是不能实现的。

    • 为了解决这个问题,Shell 在启动文件中增加了一个叫做 PATH 的环境变量,该变量就保存了 Shell 对外部命令的查找路径,如果在这些路径下找不到同名的文件,Shell 也不会再去其它路径下查找了,它就直接报错。

    shell命令提示符

    第一层命令提示符

    [mozhiyan@localhost ~]$
    或者
    [mozhiyan@localhost ~]#
    
    • [] 是提示符的分隔符号,没有特殊含义。
    • mozhiyan 表示当前登录的用户,我现在使用的是 mozhiyan 用户登录。
    • @ 是分隔符号,没有特殊含义。
    • localhost 表示当前系统的简写主机名(完整主机名是 localhost.localdomain)。
    • ~ 代表用户当前所在的目录为主目录(home 目录)。如果用户当前位于主目录下的 bin 目录中,那么这里显示的就是bin。
    $ 是命令提示符。Linux 用这个符号标识登录的用户权限等级:
      如果是超级用户(root 用户),提示符就是#;
      如果是普通用户,提示符就是$。
    

    第二层命令提示符

    • 有些命令不能在一行内输入完成,需要换行,这个时候就会看到第二层命令提示符。第二层命令提示符默认为>
    [mozhiyan@localhost ~]$ echo "Shell教程"
    Shell教程
    [mozhiyan@localhost ~]$ echo "
    > http://
    > c.biancheng.net
    > "
    
    http://
    c.biancheng.net
    
    • 第一个 echo 命令在一行内输入完成,不会出现第二层提示符。第二个 echo 命令需要多行才能输入完成,提示符>用来告诉用户命令还没输入完成,请继续输入。

    • echo 命令用来输出一个字符串。字符串是一组由" "包围起来的字符序列,echo 将第一个"作为字符串的开端,将第二个"作为字符串的结尾。对于第二个 echo 命令,我们将字符串分成多行,echo 遇到第一个"认为是不完整的字符串,所以会继续等待用户输入,直到遇见第二个"

    shell脚本的执行方式

    1、在终端直接调用,通过这种方式运行脚本,脚本文件第一行的#!/bin/bash一定要写对,好让系统查找到正确的解释器

    2、直接在终端运行 Bash 解释器,将脚本文件的名字作为参数传递给 Bash

    [mozhiyan@localhost demo]$ /bin/bash test.sh  #使用Bash的绝对路径
    

    通过这种方式运行脚本,不需要在脚本文件的第一行指定解释器信息,写了也没用。

    3、在当前进程中运行 Shell 脚本
    这里需要引入一个新的命令——source 命令。source 是 Shell 内置命令的一种,它会读取脚本文件中的代码,并依次执行所有语句。你也可以理解为,source 命令会强制执行脚本文件中的全部命令,而忽略脚本文件的权限。
    source 命令的用法为:

    source filename
    

    也可以简写为:

    . filename
    

    两种写法的效果相同。对于第二种写法,注意点号.和文件名中间有一个空格。

    shell四种运行方式(启动方式)

    http://c.biancheng.net/view/3045.html
    交互式的登录 Shell;
    交互式的非登录 Shell;
    非交互式的登录 Shell;
    非交互式的非登录 Shell

    shell常用配置文件

    与 Bash Shell 有关的配置文件主要有

    /etc/profile
    ~/.bash_profile
    ~/.bash_login
    ~/.profile
    ~/.bashrc
    /etc/bashrc
    /etc/profile.d/*.sh
    

    不同的启动方式会加载不同的配置文件。
    ~表示用户主目录。是通配符,/etc/profile.d/.sh 表示 /etc/profile.d/ 目录下所有的脚本文件(以.sh结尾的文件)。

    登录式的shell

    • Bash 官方文档说:如果是登录式的 Shell,首先会读取和执行 /etc/profiles,这是所有用户的全局配置文件,接着会到用户主目录中寻找 /.bash_profile、/.bash_login 或者 ~/.profile,它们都是用户个人的配置文件。

    • 不同的 Linux 发行版附带的个人配置文件也不同,有的可能只有其中一个,有的可能三者都有,笔者使用的是 CentOS 7,该发行版只有 ~/.bash_profile,其它两个都没有。

    • 如果三个文件同时存在的话,到底应该加载哪一个呢?它们的优先级顺序是 ~/.bash_profile > ~/.bash_login > ~/.profile。

    • 如果 ~/.bash_profile 存在,那么一切以该文件为准,并且到此结束,不再加载其它的配置文件。

    • 如果 ~/.bash_profile 不存在,那么尝试加载 /.bash_login。/.bash_login 存在的话就到此结束,不存在的话就加载 ~/.profile。

    • 注意,/etc/profiles 文件还会嵌套加载 /etc/profile.d/*.sh

    • 同样,~/.bash_profile 也使用类似的方式加载 ~/.bashrc

    非登录的 Shell

    • 如果以非登录的方式启动 Shell,那么就不会读取以上所说的配置文件,而是直接读取 ~/.bashrc。

    • ~/.bashrc 文件还会嵌套加载 /etc/bashrc

    http://c.biancheng.net/shell/

    相关文章

      网友评论

          本文标题:shell介绍

          本文链接:https://www.haomeiwen.com/subject/ndldqctx.html