编写快速安全Bash脚本的建议

昨天我和一些朋友聊起Bash,我意识到:即使我已经使用Bash十多年了,现在还有一些基础的杂项,我理解的并不是很清晰。 像往常一样,我认为我应该写一个博文。

我们会包含:

  • 一些bash基础知识(“你怎么写一个for循环”)
  • 杂项事宜(“总是引用你的bash变量”)
  • bash脚本安全提示(“总是使用set -u”)

如果你编写shell脚本,并且你没有阅读这篇文章中其他任何内容,你应该知道有一个shell脚本校验工具(linter),叫做 shellcheck 。使用它来使您的shell脚本更好!

我们会像讨论编程语言一样讨论bash,因为,怎么说呢,它就是。
这篇文章的目标不是bash编程详解。我不会在bash中做复杂的编程,也真的不计划学习如何去做。
但是,经过今天的思考之后,我认为明确整理下bash编程语言的一些基础知识是有用的。bash编程语言与我使用过的其他编程语言有着很大的不同。

我真的曾认为我已经知道这些东西了,但是通过写这篇文章我依旧学到了一些东西,也许你也会有所收获。

变量赋值

在bash中变量赋值按照下面的方式:

VARIABLE=2

并且你可以使用$VARIABLE(变量名)来引用变量。需要注意的是不要在=运算符的两边放置空格符,比如VARIABLE=
2、VARIABLE = 2、或者VARIABLE
=2,这并不是语法错误,但是将会做完全不需要的事情(比如试图运行一个名字为2的程序,并将环境变量VARIABLE设置为空字符串)。

Bash变量并不要求全部大写,但是通常是大写的。

大多数你所使用的bash变量都是字符串。在bash中也有一些数组变量,但我并不是完全理解它们。

使用${}引用变量

有时某些变量,内容为file.txt,并且我想这样使用它:

mv $MYVAR $MYVAR__bak # wrong!

这段代码是无法工作的!它会去查找 MYVAR__bak变量,但这并不是一个真实存在的变量。

为了避免类似问题,你需要知道的仅仅是 ${MYVAR}和$MYVAR是一回事。所以你可以这样运行指令:

mv $MYVAR ${MYVAR}__bak # right!

全局变量,局部变量和环境变量

Bash有3种变量。我一般先想到(可能也是最常用)的是 环境变量 。

Linux上的每个进程实际上都有环境变量(您可以运行env查看当前设置的变量),但在Bash中,它们更易于访问。要查看名为MYVAR的环境变量,可以运行

echo "$MYVAR"

要设置环境变量,您需要使用export关键字:

export MYVAR=2

设置环境变量时,所有子进程将看到该环境变量。所以如果你运行export MYVAR=2; python test.py,python程序将MYVAR设置为2。

第二种变量是 全局变量 。同样像上面那样赋值。

MYVAR=2

在其他编程语言中他们表现得像全局变量。

还有 局部变量 ,它们的作用域只能存在于bash函数中。 我基本上从来没有使用过这样的函数(不像我写的其他编程语言),我从来没有使用过局部变量。

for循环

以下是我在bash中编写循环的方法。 此循环将从1打印到10。

for i in `seq 1 10` # you can use {1..10} instead of `seq 1 10`
do
 echo "$i"
done

如果你想用一行代码写这个循环,可以这样写:

for i in `seq 1 10`; do echo $i; done

我觉得这是不可能记住的(你要怎么记住在 seq 1 10 之后有一个分号,但是在 do 之后却没有了),所以我不会去记它。

你也可以写while循环,但我从来没有这样写过。

有个很酷的事情是你可以遍历另一个命令的输出。seq 1 10 将数字从1到10(每行一个)打印,这个for循环只是提取该输出并遍历它。我就经常用这种方法。

您也可以使用反引号或$()来插入命令的输出。

OUTPUT=`command`
# or
OUTPUT=$(command)

if 语句

在 bash 中的 If 语句是相当让人讨厌去记它。你必须放在这些方括号中,而在方括号之间必须有空格,否则它不起作用。[[ 和
[ 方括号(双/单) 都工作。 这里我们真正进入 bash 的奇怪领域:[ 是一个程序(/usr/bin/[)但 [[ 是 bash 语法。[[
更好。

if [[ "vulture" = "panda" ]]; then
 echo expression evaluated as true
else
 echo expression evaluated as false
fi

此外,您可以检查“此文件存在”,“此目录存在”等内容。例如,您可以检查文件 /tmp/awesome.txt 是否存在,如下:

If [[ -e /tmp/awesome.txt ]]; then
  echo "awesome"
fi

这通常是有用的,但我必须每次查找语法。

如果您想尝试用命令行,可以使用 test 命令,例如 test -e /tmp/awesome.txt 。 它成功会返回0,否则返回错误。

最后一件事是为什么[[比[好:如果你使用[[,那么你可以使用<做比较,它不会变成文件重定向。

$ [ 3 < 4 ] && echo "true"
bash: 4: No such file or directory
$ [[ 3 < 4 ]] && echo "true"
true

还有一个额外的最后一件关于 if 的事:我今天学到是不需要通过[[或者[去使用 if 语句:任何有效的命令都会工作。 所以你可以这样做

if grep peanuts food-list.txt
then
echo "allergy allert!"
fi

函数不是那么难

在 bash 中定义和调用函数(特别是没有参数)是非常容易的。

my_function () {
 echo "This is a function";
}
my_function #调用函数

总是引用你的变量

另一个 bash 技巧:绝不使用一个没有引用的变量。 看看这个看似合理的 shell 脚本:

X="i am awesome"
Y="i are awesome"
if [ $X = $Y ]; then
 echo awesome
fi

如果你尝试运行这个脚本,你会得到不可理喻的错误消息 script.sh: line 3: [: too many arguments. 什么?

Bash 解释这个 if 语句为 if [ i am awesome == i are awesome],这是6个字符串 (i, am, awesome, i, are, awesome) 无意义的 if 语句。 正确的写法是

X="i am awesome"
Y="i are awesome"
if [ "$X" = "$Y" ]; then #我放置引号因为我知道bash会背叛我,如果我不放的话
 echo awesome
fi

有些情况下,只要使用 $ X 而不是 “$ X” 就可以,但是您可以知道何时可以,何时不行吗? 我肯定不能。 总是引用你的 bash 变量,你会更快乐的。

返回代码, &&, 和 `||

每个 Unix 程序都有一个“返回代码”,它是一个从0到127的整数。0表示成功,其他都意味着失败。 这在 bash 中是有作用的,因为:有时我从命令行运行一个程序,并希望仅在第一个程序成功的情况下运行第二个程序。

你可以用 && 实现!

例如:create_user && make_home_directory。 这将运行 create_user ,检查返回代码,然后仅在返回代码为0时运行 make_home_directory。

这与 create_user; make_home_directory 不同,无论 create_user 的返回代码是什么,都将运行 make_home_directory。

你也可以使用create_user || make_home_directory,只有create_user运行失败才运行make_home_directory 。 这在技术领域中非常巧妙。

后台进程

我不会在此谈及太多关于 job 控制的内容,但是:你可以像下面这样启动后台进程

long_running_command &

如果你后来后悔将进程放到后台,并希望把它带调回前台,你可以用 fg 来做到这一点。 如果不止一个进程,您可以使用 jobs
查看所有后台进程。由于某种原因,fg 需要一个 “job ID”(这就是 jobs 打印输出的)而不是一个 PID。 谁知道 Bash
为什么这样子呢。

另外,如果你在后台运行太多的进程,内置等待命令将等到它们都返回。

说到后悔 - 如果你不小心在错误的终端启动一个进程,Nelson Elhage 有一个很棒的项目叫做 reptyr ,可以保存你的进程并将其移到屏幕会话或者某些其他东西中。

作者:Tocy

来源:51CTO

时间: 2024-08-12 22:58:32

编写快速安全Bash脚本的建议的相关文章

编写更好 Bash 脚本的 8 个建议

在我最开始管理Linux和Unix服务器时,经常遇到其他管理员编写的一大堆临时脚本.时常会因为其中某个脚本突然停止工作而进行故障排查.有时这些脚本编写得规范好理解,其他时候则是杂乱且令人困惑. 虽然排查编写糟糕的脚本很麻烦,但我从中吸取到了教训.即使你认为该脚本只会在今天使用,最好也抱着两年后还将有人去排查的态度编写脚本.因为总会有人查看,甚至很可能是你自己. 在本篇文章中,我想介绍一些优化脚本的建议,不是为了方便你编写脚本,而是方便想要弄清脚本为何不工作的人. 以释伴shebang行开头 Sh

如何编写健壮的Bash脚本(经验分享)_linux shell

shell脚本在运行异常时会受到非常大的影响. 本文介绍一些让bash脚本变得健壮的技术. 使用set -u 因为没有对变量初始化而使脚本崩溃过多少次?对于我来说,很多次.chroot=$1...rm -rf $chroot/usr/share/doc如果上面的代码没有给参数就运行,不会仅仅删除掉chroot中的文档,而是将系统的所有文档都删除.那应该做些什么呢?好在bash提供了set -u,当使用未初始化的变量时,让bash自动退出. 也可以使用可读性更强一点的set -o nounset.

如何用bash-support插件将Vim编辑器打造成编写Bash脚本的IDE

IDE(集成开发环境)就是这样一个软件,它为了最大化程序员生产效率,提供了很多编程所需的设施和组件. IDE 将所有开发工作集中到一个程序中,使得程序员可以编写.修改.编译.部署以及调试程序. 在这篇文章中,我们会介绍如何通过使用 bash-support vim 插件将 Vim 编辑器安装和配置 为一个编写 Bash 脚本的 IDE. 什么是 bash-support.vim 插件? bash-support 是一个高度定制化的 vim 插件,它允许你插入:文件头.补全语句.注释.函数.以及代

如何用 bash-support 插件将 Vim 编辑器打造成编写 Bash 脚本的 IDE

IDE(集成开发环境)就是这样一个软件,它为了最大化程序员生产效率,提供了很多编程所需的设施和组件. IDE 将所有开发工作集中到一个程序中,使得程序员可以编写.修改.编译.部署以及调试程序. 在这篇文章中,我们会介绍如何通过使用 bash-support vim 插件将 Vim 编辑器安装和配置 为一个编写 Bash 脚本的 IDE. 什么是 bash-support.vim 插件? bash-support 是一个高度定制化的 vim 插件,它允许你插入:文件头.补全语句.注释.函数.以及代

java-linux bash脚本编写问题

问题描述 linux bash脚本编写问题 我想要实现linux后台运行jar 于是编写了脚本: exec java -Xms128m -Xmx2048m -jar /var/www/JavaWork/BidCheck.jar 5 >pid.log 这里是保存进程到文件pid.log 让进程id保存在文件,但是运行我这个jar需要参数 /workspace 完整的java执行命令如下:java -Xms128m -Xmx2048m -jar /var/www/JavaWork/BidCheck.

Linux中高效编写Bash脚本的10个技巧

Shell 脚本编程 是你在 Linux 下学习或练习编程的最简单的方式.尤其对 系统管理员要处理着自动化任务,且要开发新的简单的实用程序或工具等(这里只是仅举几例)更是必备技能. 本文中,我们将分享 10 个写出高效可靠的 bash 脚本的实用技巧,它们包括: 1. 脚本中多写注释 这是不仅可应用于 shell 脚本程序中,也可用在其他所有类型的编程中的一种推荐做法.在脚本中作注释能帮你或别人翻阅你的脚本时了解脚本的不同部分所做的工作. 对于刚入门的人来说,注释用 # 号来定义. # TecM

Bash脚本学习笔记快速入门篇

脚本安全 我的所有bash脚本都以下面几句为开场白:  代码如下 复制代码 #!/bin/bash set -o nounset set -o errexit 这样做会避免两种常见的问题:     引用未定义的变量(缺省值为"")     执行失败的命令被忽略 需要注意的是,有些Linux命令的某些参数可以强制忽略发生的错误,例如"mkdir -p" 和 "rm -f". 还要注意的是,在"errexit"模式下,虽然能有效的

《UNIX/Linux 系统管理技术手册(第四版)》——2.2 bash脚本编程

2.2 bash脚本编程 UNIX/Linux 系统管理技术手册(第四版) bash特别适合编写简单的脚本,用来自动执行那些以往在命令行输入的操作.在命令行用的技巧也能用在bash的脚本里,反之亦然,这让用户在bash上投入的学习时间获得了最大的回报.不过,一旦bash脚本超过了100行,或者需要的特性bash没有,那么就要换到Perl或者Python上了. bash脚本的注释以一个井号(#)开头,并且注释一直延续到行尾.和命令行中一样,可以把逻辑上的一行分成多个物理上的多行来写,每行末尾用反斜

处理Apache日志的Bash脚本

去年一年,我写了将近100篇网络日志. 现在这一年结束了,我要统计"访问量排名",看看哪些文章最受欢迎.(隆重预告:本文结尾处将揭晓前5名.) 以往,我用的是AWStats日志分析软件.它可以生成很详细的报表,但是不太容易定制,得不到某些想要的信息.所以,我就决定自己写一个Bash脚本,统计服务器的日志,顺便温习一下脚本知识. 事实证明,这件事比我预想的难.虽然最终脚本只有20多行,但花了我整整一天,反复查看手册,确认用法和合适的参数.下面就是我的日志分析脚本,虽然它还不是通用的,但是