复杂shell脚本
IT路边社
前言
与其它的编码规范一样,这里所讨论的不仅仅是编码格式美不美观的问题, 同时也讨论一些约定及编码标准。这份文档主要侧重于我们所普遍遵循的规则,对于那些不是明确强制要求的,我们尽量避免提供意见。
编码规范对于程序员而言尤为重要,有以下几个原因:
本文档中的准则致力于最大限度达到以下原则:
尽管本文档涵盖了许多基础知识,但应注意的是,没有编码规范可以为我们回答所有问题,开发人员始终需要再编写完代码后,对上述原则做出正确的判断。
注 :未明确指明的则默认为必须(Mandatory)
主要参考如下文档:
仅建议Shell用作相对简单的实用工具或者包装脚本。因此单个shell脚本内容不宜太过复杂。
在选择何时使用shell脚本时时应遵循以下原则:
可执行文件不建议有扩展名,库文件必须使用 .sh 作为扩展名,且应是不可执行的。
执行一个程序时,无需知道其编写语言,且shell脚本并不要求具有扩展名,所以更倾向可执行文件没有扩展名。
而库文件知道其编写语言十分重要,使用 .sh 作为特定语言后缀的扩展名,可以和其他语言编写的库文件加以区分。
文件名要求全部小写, 可以包含下划线 _ 或连字符 - , 建议可执行文件使用连字符,库文件使用下划线。
正例:
反例:
源文件编码格式为UTF-8。避免不同操作系统对文件换行处理的方式不同,一律使用 LF 。
每行最多不超过120个字符。每行代码最大长度限制的根本原因是过长的行会导致阅读障碍,使得缩进失效。
除了以下两种情况例外:
如出现长度必须超过120个字符的字符串,应尽量使用here document或者嵌入的换行符等合适的方法使其变短。
示例:
除了在行结束使用换行符,空格是源文件中唯一允许出现的空白字符。
对从来没有用到的或者被注释的方法、变量等要坚决从代码中清理出去,避免过多垃圾造成干扰。
Bash 是唯一被允许使用的可执行脚本shell。
可执行文件必须以 #!/bin/bash 开始。请使用 set 来设置shell的选项,使得用 bash echo "Process $: Done making $$$."
# 示例7:命令参数及路径不需要引号 grep -li Hugo /dev/ "$1"
# 示例8:常规变量用双引号,ccs可能为空的特殊情况可不用引号 git send-email --to "${reviewers}" ${ccs:+"--cc" "${ccs}"}
# 示例9:正则用单引号,$1可能为空的特殊情况可不用引号 grep -cP '([Ss]pecial||?characters*) ${1:+"$1"}
# 示例10:位置参数传递推荐带引号的"$@",所有参数作为单字符串传递用带引号的"$*" # content of t.sh func_t { echo num: $# echo args: 1:$1 2:$2 3:$3 }
func_t "$@" func_t "$*" # 当执行 ./t.sh a b c 时输出如下: num: 3 args: 1:a 2:b 3:c num: 1 args: 1:a b c 2: 3:
使用 $(command) 而不是反引号。
因反引号如果要嵌套则要求用反斜杠转义内部的反引号。而 $(command) 形式的嵌套无需转义,且可读性更高。
正例:
反例:
条件测试
使用 [[ ... ]] ,而不是 [ , test , 和 /usr/bin/[ 。
因为在 [[ 和 ]] 之间不会出现路径扩展或单词切分,所以使用 [[ ... ]] 能够减少犯错。且 [[ ... ]] 支持正则表达式匹配,而 [ ... ] 不支持。参考以下示例:
尽可能使用变量引用,而非字符串过滤。
Bash可以很好的处理空字符串测试,请使用空/非空字符串测试方法,而不是过滤字符,让代码具有更高的可读性。正例:
反例:
正例:
反例:
正例:
反例:
文件名扩展
当进行文件名的通配符扩展时,请指定明确的路径。
当目录中有特殊文件名如以 - 开头的文件时,使用带路径的扩展通配符 ./* 比不带路径的 * 要安全很多。
应该避免使用eval。
Eval在用于分配变量时会修改输入内容,但设置变量的同时并不能检查这些变量是什么。反例:
请使用进程替换或者for循环,而不是通过管道连接while循环。
这是因为在管道之后的while循环中,命令是在一个子shell中运行的,因此对变量的修改是不能传递给父shell的。
这种管道连接while循环中的隐式子shell使得bug定位非常困难。反例:
如果你确定输入中不包含空格或者其他特殊符号(通常不是来自用户输入),则可以用for循环代替。例如:
使用进程替换可实现重定向输出,但是请将命令放入显式子 shell,而非 while 循环创建的隐式子 shell。例如:
总是检查返回值,且提供有用的返回值。
对于非管道命令,使用 $? 或直接通过 if 语句来检查以保持其简洁。
例如:
当内建命令可以完成相同的任务时,在shell内建命令和调用外部命令之间,应尽量选择内建命令。
因内建命令相比外部命令而言会产生更少的依赖,且多数情况调用内建命令比调用外部命令可以获得更好的性能(通常外部命令会产生额外的进程开销)。
正例:
反例:
加载外部库文件不建议用使用.,建议使用source,已提升可阅读性。正例:
反例:
除非必要情况,尽量使用单个命令及其参数组合来完成一项任务,而非多个命令加上管道的不必要组合。常见的不建议的用法例如:cat和grep连用过滤字符串; cat和wc连用统计行数; grep和wc连用统计行数等。
正例:
除特殊情况外,几乎所有函数都不应该使用exit直接退出脚本,而应该使用return进行返回,以便后续逻辑中可以对错误进行处理。正例:
反例:
推荐以下工具帮助我们进行代码的规范:
原文链接:http://itxx00.github.io/blog/2020/01/03/shell-standards/
获取更多的面试题、脚本等运维资料点击: 运维知识社区 获取
脚本之---短信轰炸机
脚本之---QQ微信轰炸机
ansible---一键搭建redis5.0.5集群
elk7.9真集群docker部署文档
全球最全loki部署及配置文档
最强安全加固脚本2.0
一键设置iptbales脚本
⑵ 第七章 SHELL脚本
在linux里可以直接使用shell脚本进行管理,shell就是命令解释器,把用户的命令翻译成机器语言,在把机器语言翻译成人类语言返回给用户。
shell有Bshell和Cshell区分,经常在linux里面用的shell都是Bshell,Bshell和Cshell完全不兼容。我在Centos里面看见支持的几种shell解释器有sh,bash,csh。然后用的最多的就是base(标准shell)。
shell脚本可以添加执行权限,然后在base命令界面直接输入脚本路径即可执行;所有的脚本开头要声明脚本shell,常见的声明是#!/bin/bash,其他的还有#!/bin/sh,#!/bin/csh
写第一个shell脚本吧
可以使用alias命令查看系统当前的别名,也可以用alias 别名=‘真实命令’ 来执行;
重定向输出是将输出到标准输出设备的数据重新定向到另一个地方;
将标准(键盘)输入重定向到其他一个设备上;
格式:命令1 | 命令2
表示把命令1的执行结果当成命令2的执行条件
跟正则表达式不一样啊,刚才的grep -P就是使用正则,但是这里通配符比较简单啊。
用引号表示,其中单引号标识不解析字符串中的特殊字符,双引号则解析特殊字符。
如果字符串中有存在$(ls)这种,那么这个表示ls命令的执行结果啊
当然这也可以用\进行转义
⑶ Linux如何编写shell脚本
一般以#!/bin/sh开头(不是必须要写,但一定要单独一行),指定执行这个脚本的shell程序(也可以用#!/bin/zsh或其他),然后就是堆命令了。
Linux的shell脚本支持很多功能,加上Linux高度模块化的命令,完全可以用shell脚本写出复杂的程序。
以上只是简单介绍如何开始写shell脚本,如果要写复杂的脚本,还需要深入学习相关知识(如if——fi、case——esac等结构)。
当然,还需要给脚本加上可执行权限(chmod +x ./file.sh),否则可以用sh ./file.sh方式执行脚本(这里的sh是执行脚本所需shell,命令也可以是zsh ./file.sh或其他)。
整个shell脚本,其实就相当于你在终端输入的一系列命令,如果想在shell里做什么,就先想想在终端可以做什么吧,字符的的连接,就是直接用 "" 双引号,输出,变量定义无 $ 符号,但是使用时一定要加上 $ 符号。
"=" 赋值符号,两边一定不能有空格,这和其他语言有区别,尤其是你还有自己代码美观风格时特别注意,否则会报语法错误!
for 中的数组内容是以 " " 空格分隔,而非 "," 逗号分格。
条件判断 [ true ] 中括号 后面需要有一个空格,但是两个中括号之间不能有空格如 [[ true ]]。
while 条件判断可以用 () 括号,也可以用 [[ ]] 中括号。
如果用windows写shell,一定要注意换行符格式 而非 , 需要借助一些编辑器(如notepad++)更改换行符格式!
⑷ 学生信的那些事儿之七 - Linux基础之Shell脚本编程
沿着前面的轨迹,接下来是Linux中shell脚本的学习。这对于生信工程师后续处理大量 (海量更合适些) 数据是非常非常重要的,但是同样的,作为一个有点古板的人,对于"脚本"是什么意思我都死磕了好久。主要觉得有些抽象,尤其是跟生信的同事讨论项目分析部分的问题时,他们经常会说道这个词,在他们意识里这是个不言自明的术语,殊不知对外行人而言 (比如我),那简直就是无情的"知识的诅咒"。经常是我假装听懂了,然后继续讨论下面的问题,形成一个模糊的印象。
网络上的解释是:脚本(Script)是一种批处理文件的延伸,是一种纯文本保存的程序,一般来说的计算机脚本程序是确定的一系列控制计算机进行运算操作动作的组合,在其中可以实现一定的逻辑分支等。不知道你能不能看懂,反正我开始的时候真是一知半解。
鸟哥私房菜的解释是:shell script是利用 shell 的功能所写的一个"程序",这个程序是使用纯文本文件,将一些shell的语法与命令(含外部命令)写在里面,搭配正则表达式、管道命令与数据流重定向等功能,以达到我们所想要的处理的目的。不明觉厉,好像更看不懂了···
Jude 的简单粗暴大白话解释是:脚本就是Linux中很多命令按照一定规则的组合,以实现某个特定的功能。Linux中有很多简单的命令,往往只是进行了简单的对话,比如 cd 就是进入到某个目录,简单直接。但是如果我想进入某个目录A,然后在目录A中创建目录B,再在目录B中创建文本C呢?当然可以一步一步操作,如果想要一步到位呢,那就可以用脚本,把三个命令写在一起,一起执行。好像有点啰嗦···
或者从英语的角度去理解,脚本的对应英文是Script,而这个单词的中文释义中还有剧本的意思。剧本就好理解了啊,剧本就是导演(生信工程师)基于某个主旨(要实现的目标)按照一定的手法(规则)所写的一个故事。不管是哪个演员,都得按照剧本演。所以,学好英语对于生信也是有帮助的~
按照脚本的复杂程度可以分为:
这个无需多说,其实就是若干个简单命令的顺序排列,执行脚本后会按照命令的前后关系从前往后一一执行。
相对于简单的基本脚本,结构化的命令脚本可以施加逻辑流程控制,从而改变程序(命令)执行的顺序。基本脚本中的命令就是从上往下执行,但是结构化的命令脚本可以根据逻辑判断重复或者跳过某些命令。
常用的结构化命令(语句)有:
后面还有什么嵌套循环啊啥的,不过我觉得上面的7中命令学到家了,应该可以应付大部分在生信分析里面的应用了。
记得高中的时候,物理老师(也是班主任)在给我们讲解习题时有个有意思的套路:不管什么难题现在下面写个"答:",以示自己解决问题的决心,也是一种正向的心理暗示。脚本编写也是有套路的,不过总的来说还是比较简单。
对于简单的脚本(超级简单的那种),直接几个命令连在一起即可,中间用";"隔开。
对于更长更复杂的脚本,一般需要创建一个文本,并在里面编辑。这就涉及到了文本编辑器,比较常用和简单的一般有nano和vim,实在很简单,规则也容易理解,教程随手可得,不多说。
比如用vim创建了一个脚本之后,具体的语法(套路):
ok,脚本写完了,怎么让脚本开始工作呢?这有涉及到之前讲过的环境变量和相对路径、绝对路径了。方法有三:
就这么多吧,应该有点感觉到了,剩下的就是狂练狂练了~
⑸ Shell脚本
因为-c的意思是command,所以bash -c后面应该跟一个command。
shell运行脚本的两种方法:
1、bash xx.sh
2、bash -c "cmd string"
使用bash -c需要注意两点:
1、bash -c "cmd string"接的是shell命令字符串,用双引号括起来
2、bash -c "/path/to/file"接的是文件绝对路径,用双引号括起来,并且文件需要可执行权限
参考链接:
1.1、 linux shell -c,【bash】关于shell中 bash -c 执行命令或者可执行文件
1.2、 【bash】关于shell中 bash -c 执行命令或者可执行文件
示例:/usr/libexec/PlistBuddy -c "cmd string"
参考链接: XCode添加自定义Run Script Phase
由于PlistBuddy并不在Mac默认的Path里,所以我们得通过绝对路径来引用这个工具:
参考链接: PlistBuddy简单使用
⑹ 在Ubuntu下编写shell脚本读取文件
需求功能:
创建测试用例文件 text :
创建shell脚本:
运行结果:
利用 awk 'END{print NR}' [文件名] 来输出行数:
在shell脚本中添加该功能:
运行结果:
上面的shell文件只是把文件名写死在脚本中,并没有实际使用意义,所以需要编写一个指令来指定需要读取的文件,就像 vim [文件名] 一样
“$1” 代表第1个参数,也就是在 read 之后紧跟的参数,将其作为文件名传入:
将脚本移动至环境变量对应的目录中:(可以自己更改 /bin/xxx 来决定运行命令的名称,但注意不能重复)
sudo mv read /bin/read
然后添加运行权限:
sudo chmod 755 /bin/read
打开终端的目录可以是任何位置,通过以下命令即可运行shell脚本:
sudo read [文件路径]
同理,我们还可以自己为复杂指令编写shell脚本,来提高命令的使用效率,例如开关ss服务,打开锐捷客户端认证等等。