美文网首页
Shell 基础篇

Shell 基础篇

作者: 生产八哥 | 来源:发表于2022-02-17 00:18 被阅读0次

    一、需求背景

    在程序猿的日常工作中,经常会遇到一些需要人工手动去操作或者需要重复去做的工作,而这基本上都会占用我们大量的时间,怎么办呢?那我们就可以使用脚本去替代我们去做这些事情,把更多的精力和时间花在更有价值的事情上。

    我们常见的脚本语言有很多种,比如Perl、Python、Ruby、shell等等,这次要介绍的脚本就是以Bash为例的shell,让大家可以对shell有一个初步的认识。

    二、认识shell

    1、Hello,World

    首先我们看一段代码

    #!/bin/bash
    echo "Hello World!";echo "time:" $(date)
    echo "work directory: $(pwd)"
    

    将以下代码加入到 test.sh 文件 ,并赋予权限 『chmod 777 test.sh』,最后执行 『./test.sh』 或 『sh test.sh』

    显示如下:

    Hello World!
    time: 2021年 6月1日 星期二 15时00分56秒 CST
    work directory: /Users/tuya/Desktop/sh
    

    从上面的例子中,我们可以学到以下几个知识点:

    ● # 可以用来添加对代码的注释

    ● #! 用来说明shell的类型以及执行的路径,如果没有声明,则使用默认的shell类型

    ● echo 用来打印输出,默认添加换行符(也可以使用printf打印,不过需要自己加换行符)

    ● pwd 可以获取当前文件夹的路径

    ● $() 可以在内部执行指令

    ● 指令之间可以用分号或者换行符隔开

    2、什么是shell

    我们首先看一个图,了解一下计算机的运行状态:

    image.png

    可以发现,应用程序是在最外层,就跟鸡蛋的外壳一样,因为也被称呼为壳程序

    shell是一种特殊的交互式工具,本质上是解释器,核心是命令提示符,允许输入文本命令,解释命令,并在内核中执行命令,返回结果

    3、shell的版本历史

    shell的版本有很多,早年的Unix时代,根据发展者的不同就有很多的版本,例如熟知的就有Bourne Shell (sh)、在 Sun 里头默认的 C Shell、 商业上常用的 K Shell, 还有 TCSH 等等,每一种 Shell 都各有其特点。

    image.png

    想知道当前系统有几种shell,我们可以通过 /etc/shells 文件可以查看:

    $ cat /etc/shells
    /bin/bash
    /bin/csh
    /bin/dash
    /bin/ksh
    /bin/sh
    /bin/tcsh
    /bin/zsh
    

    那如何查看当前使用的 shell 类型呢?使用 $SHELL 变量就可以知道当前默认使用的 shell

    $ echo $SHELL
    /bin/zsh
    

    我们发现在 Mac 上执行这个命令会发现多了一个 zsh ,也就是说 OS X 系统预装了 zsh,其实从 macOS Catalina 版开始,其默认 shell 从 bash 改为 zsh,目前github上有一个开源的框架『oh my zsh』,专门用于管理 zsh 配置,可以帮助我们提高开发效率。

    三、Bash shell的优点

    既然 /bin/bash 是 Linux 默认的 shell,那我们肯定首先是要了解它的功能,才能知道我们可以用它来做什么以及怎么用,这样对于了解其他类型的shell也是有帮助的,Bash shell主要有以下几个优点:

    1、命令编修能力 - history

    按上下键就可以找到前/后输入过的指令,默认的指令记忆功能可达1000个,可在终端通过输入history查询最近使用过的指令

    2、命令与文件补全功能

    ●[Tab]接在第一串指令的第一个字的后面,则为命令补全

    ●[Tab]接在第一串指令的第二字的后面,则为文件补全

    ●X[Tab],可查询所有以X开头的指令

    ●若安装 bash-completion 软件,则使用[Tab]键后可进行选项参数的补齐功能

    3、命令别名设置 - alias

    可使用alias查询目前的命令别名有哪些,也可以直接输入 alias 命令来设置别名,例如:

    alias lm='ls -al'
    

    使用的时候我们直接使用 lm 就可以代替 ls -al 实现其具备的功能。

    4、程序化脚本 - shell scripts

    在Dos年代需将一堆命令写在一起的所谓的批处理文件,如今在Linux下使用shell scripts就可以实现

    5、通配符 - wildcard

    除了完整的字符串以外,bash还支持许多的通配符来帮助用户查询与命令下达。主要的通配符包含有, ?, [ ] 等等,例如想要知道 /usr/bin 底下有多少以 X 为开头的文件,就使用:『 ls -l /usr/bin/X 』查询

    四、Shell的变量功能

    简单来说,变量就是以一组文字或符号等,来取代一些配置或者是一串保留的数据!

    变量在设置时,需要符合一定的规则:

    ● 变量与变量内容以『 = 』来连结,『 = 』两边不能有空格

    正确:name=tuya
    错误:name = tuya
    

    ●变量名称只能是英文字母与数字,开头不能是数字

    正确:name=tuya
    错误:2name=tuya
    

    ●变量内容若有空格可使用双引号或者单引号将变量内容结合起来

    ○双引号的特殊字符 $ 等,可以保有原有本性

    ○单引号的特殊字符 $ 等,则为一般字符

    name=tuya
    双引号:var="my name is $name" <== 输出:my name is tuya
    单引号:var='my name is $name' <== 输出:my name is $name
    

    ●可用跳脱字符 『 \ 』将特殊字符(如$、'、\)等变成一般字符

    var=my\ name\ is\ tuya <== 输出:my name is tuya
    

    ●在一串命令中,还需要借由其他的命令提供的信息,可以使用反单引号『 命令 』或者『 $(命令) 』

    files=$(ls -al) <== 输出当前目录的所有文件夹及文件
    

    ●若该变量为扩增变量内容时,则可用"变量名称"或{变量}累加内容

    path=Desktop
    var=${path}:/home/bin <== 输出:Desktop:/home/bin
    

    ●若该变量需要在其他子程序运行,则需要 export 来使变量变成环境变量,使用方式『export var』

    name=tuya
    bash <== 进入到所谓的子程序
    echo $name <== 子程序:输出一下
    输出: <== 并没有内容
    exit <== 退出子程序
    export name <== 使变量变成环境变量
    bash <== 进入到所谓的子程序
    echo $name <== 子程序:再次输出一下
    输出:tuya <== 出现结果
    exit <== 退出子程序
    

    ●通常大写字符为系统默认变量,自行配置变量可以使用小写字符,方便判断

    ●取消变量的方法为使用 unset,例如『unset name』

    ●如果需要查看环境变量,可以使用命令 env 查看环境变量,set 还可以查看自定义变量

    子程序的概念:简单来说,就是在当前的这个shell下去激活另外一个shell,新的那个shell就是子程序,一般情况下,父程序的自定义变量是无法在子程序里面使用的,但是通过 export 将变量变成环境变量之后,就能够在子程序里面使用父程序的自定义变量了。

    五、数据流重导向

    我们在运行命令的时候,可能会从文件读取数据,经过处理之后,再将结果输出到屏幕上。但是输出的内容不管是正确的还是错误的,都一股脑儿的都显示在屏幕上,就会很难区分,这就需要用到数据流重导向的功能,这里就涉及到三个概念,分别是标准输入(stdin)、标准输出(stdout)以及标准错误输出(stderr)。

    名称 简称 代码 符号
    标准输入 stdin 0 < 或者 <<
    标准输出 stdout 1 > 或者 >>
    标准错误输出 stderr 2 2> 或者 2>>

    ●standard input

    符号 < 的作用是读取文件内容,符号 << 代表的是结束的输入字符,表示当输入指定的字符串后,就停止输入,例如:

    $ cat > catfile < ~/.bash_profile <== 将bash_profile的内容读取到catfile文件
    $ cat catfile <== catfile的内容和bash_profile的一模一样
    $ cat > catfile << 'end' <== 表示当输入 end 的时候就退出输入模式
    one
    two
    end <== 这里将直接退出输入模式,不需要再使用 ctrl + d 退出了
    

    ●standard output 和 standard error output

    符号 > 和 >> 的主要区别在于 > 会覆盖之前的内容,而 >> 会在之前的基础上进行累加

    ls -al >list_right <==list_right文件未创建会自动创建,并会覆盖之前的内容
    ls -al >>list_right <==list_right文件内容累加
    find / -name .bashrc >list_right 2>list_error <==指定不同的输出结果至不同的文件,屏幕无任何信息展示
    

    我们也可以同时将正确和错误的输出信息写入到同一个文件,但是需要用到特殊写法:

    find / -name .bashrc >list 2>list <==错误
    find / -name .bashrc >list 2>&1 <==正确
    find / -name .bashrc &>list <==正确
    

    ●/dev/null 垃圾桶黑洞装置

    如果需要将错误的输出结果忽略掉,既不想输出到屏幕上也不想要输出到文件上,这时候黑洞装置就派上用场了,它可以吃掉任何导向这个装置的信息,例如:

    find / -name test.sh >list_right 2>/dev/null
    

    综上所述,数据流重导向的优点还是有很多,最重要的是可以方便我们筛选信息,可以很快的找到需要的内容,这对于程序开发提供了极大的便利性。

    六、管线命令(pipe)

    管线命令就像是流水线上的工作,由一道道工序组成,下一道工序的来源产自上一道工序,而每一道工序只需完成其中的一部分工作内容,最终完成一个看起来很复杂的任务。

    先来一张图来直观感受下管线命令是如何运行的

    image.png

    可以看到,管线命令(符号是『 | 』)仅能处理由前面一个命令传来的正确信息,也就是 standard output 的信息,但是对于 stdandard error output 没有直接处理的能力。

    管道命令有很多,有撷取命令(cut,grep)、排序命令(sort, wc, uniq)、双向重导向(tee)、字符切换命令( tr, col, join, paste, expand)、分割命令(split)、参数代换(xargs)等等,具体使用可自行查阅相关资料,下面简单举一个例子如何使用撷取命令。

    简单来说,撷取命令(cut, grep)就是将一段信息进行处理后得到我们想要的数据。

    顾名思义,cut 的作用主要是通过裁剪信息来获取指定数据。

    $ echo $PATH
    /Library/Frameworks/Python.framework/Versions/3.9/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin:/Users/linbo/.rvm/bin
    $echo $PATH | cut -d ':' -f 1 <== $PATH作为数据源,然后使用『 : 』作为分隔符,获取第一个值
    /Library/Frameworks/Python.framework/Versions/3.9/bin
    

    再来看看 grep 命令,它的作用区别于 cut,cut 只是截取一部分信息内容,而 grep 是获取含有指定内容的那一条信息。

    $ last | grep 'root' <== 在显示的登录信息列表中,只要含有 root 就输出
    $ last | grep -v 'root' <== 相反,没有 root 就输出
    $ last | grep 'root' | cut -d ' ' -f1 <== 只要有 root 就输出,并且只取第一列
    

    七、shell在项目中的应用

    1、xcodebuild

    我们可以通过xcodebuild写一个自动打包的脚本,其中主要的流程包括打包和导出ipa,关于xcodebuild的相关命令,可以通过 man xcodebuild 的方式查看详细用法。

    1.1、构建APP

    ●构建Xcode项目,在项目目录下运行 xcodebuild 以下相关命令

    xcodebuild [-project name.xcodeproj]
    [[-target targetname] ... | -alltargets]
    [-configuration configurationname]
    [-sdk [sdkfullpath | sdkname]] [action ...]
    [buildsetting=value ...] [-userdefault=value ...]
    

    如果是单个的target和单个的schema,就可以不指定任何参数,直接运行 xcodebuild 即可,默认生成relaase版本真机模式的app

    ●如果要构建workspace,必须要指定 -workspace-scheme 参数

    xcodebuild -workspace name.xcworkspace -scheme schemename
    [[-destination destinationspecifier] ...]
    [-destination-timeout value]
    [-configuration configurationname]
    [-sdk [sdkfullpath | sdkname]] [action ...]
    [buildsetting=value ...] [-userdefault=value ...]
    

    1.2、导出ipa文件

    archive完需要导出ipa文件,使用的命令是 -exportArchive ,需要指定打包的路径,即刚才打包出来的 *.xcarchive 文件,则命令行可以这么写:

    xcodebuild -exportArchive -archivePath EXPORT_ARCHIVE_PATH
    -exportPath EXPORT_IPA_PATH -exportOptionsPlist ExportOptionsPlistPath
    -allowProvisioningUpdates
    

    了解完xcodebuild相关命令之后,我们就可以用shell去创建一个自动打包上传的脚本,从而可以节省大量的时间和重复的工作。

    2、Check Pods Manifest.lock

    我们在使用cocopods的时候,可能会遇到这么一个问题:

    error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.
    

    这个是由于我们的 Manifest.lock 与 Podfile.lock 产生了差异造成的。我们可以打开xcode项目,找到 Target -> Build Phase -> Check Pods Manifest.lock,发现这里其实就是一个 shell script,它自动帮我们做了文件差异的比较。

    diff "${PODS_PODFILE_DIR_PATH}/Podfile.lock" "${PODS_ROOT}/Manifest.lock" > /dev/null
    if [ $? != 0 ] ; then
    # print error to STDERR
    echo "error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation." >&2
    exit 1
    fi
    

    如果我们学习了上面的一些基础知识,就很容易看懂这段脚本内容,如果需要在后面添加一些自定义的需求,比如我可以不输出错误信息,自动执行 pod install 的操作等等都是可以的。

    除了上面介绍的几种应用shell的场景外,我们还可以用 shell 做 oclint、codesign以及项目中硬编码和资源检测等等,让我们的项目更加规范、更加稳定。

    八、小结

    shell 在我们的日常工作中其实是很常见的,如果能借助 shell 去做一些节省工作流程或者化繁为简的任务,是很有意义的一件事。对于每个程序员来说,基本都或多或少接触过 shell,可能目前只是简单的满足日常工作,如果能够深入的去了解 shell 这门语言,其实是可以帮助我们发掘更多值得去做的事情。另外关于此篇本章只是简单的介绍一些概念及基本操作,关于 shell 后面还有更多的功能值得探索和学习,用尽量少的代码,做尽量多的事情。

    参考资料

    鸟哥的linux私房菜

    https://github.com/ohmyzsh/ohmyzsh

    相关文章

      网友评论

          本文标题:Shell 基础篇

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