Shell 基础篇

一、需求背景

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

我们常见的脚本语言有很多种,比如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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容