当前位置: 首页 > 科技观察

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

时间:2023-03-19 22:56:02 科技观察

昨天和一些朋友聊起Bash,我意识到尽管我已经使用Bash十多年了,但仍然有一些基本的杂项我不明白很清楚。像往常一样,我想我应该写一篇博文。我们将涵盖:一些bash基础知识(“你如何编写for循环”)杂项(“始终引用你的bash变量”)bash脚本安全提示(“始终使用set-u”),如果你编写shell脚本,以及如果您没有阅读本文的其他任何内容,您应该知道有一个名为shellcheck的shell脚本linter。用它来让你的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#错了!此代码无效!它查找MYVAR__bak变量,但它不是真正的变量。为避免此类问题,您只需要知道${MYVAR}和$MYVAR是同一事物即可。所以你可以像这样运行命令:mv$MYVAR${MYVAR}__bak#对!全局变量、局部变量和环境变量Bash有3种变量。我想到的第一件事(可能也是最常见的)是环境变量。Linux上的每个进程实际上都有环境变量(您可以运行env来查看当前设置了哪些变量),但在Bash中,它们更容易访问。要查看名为MYVAR的环境变量,可以运行echo"$MYVAR"要设置环境变量,需要使用export关键字:exportMYVAR=2设置环境变量时,所有子进程都会看到该环境变量。所以如果你运行exportMYVAR=2;pythontest.py,python程序设置MYVAR为2。第二种变量是全局变量。也如上分配。MYVAR=2它们的行为类似于其他编程语言中的全局变量。还有一些局部变量,其作用域只能存在于bash函数中。我基本上从不使用这样的函数(不像我写的其他编程语言),我从不使用局部变量。for循环以下是我如何在bash中编写循环。这个循环将从1打印到10。foriin`seq110`#你可以使用{1..10}而不是`seq110`doecho"$i"done如果你想把这个循环写成一个行,你可以这样写:foriin`seq110`;做echo$i;done实在是记不住(seq110后面有分号,do后面没有分号怎么记),就不去记了。你也可以写while循环,但我从来没有那样写过。一件很酷的事情是您可以迭代另一个命令的输出。seq110打印从1到10的数字(每行一个),这个for循环只是提取输出并遍历它。我经常使用这种方法。您还可以使用反引号或$()来插入命令的输出。OUTPUT=`command`#或OUTPUT=$(command)if语句bash中的If语句非常难记。这些方括号你得放,而且方括号之间必须有空格,否则不行。[[和[方括号(双/单)都有效。在这里我们真的进入了bash的奇怪领域:[是一个程序(/usr/bin/[)但[[是bash语法。[[更好的。如果[[“秃鹰”=“熊猫”]];thenechoexpressionevaluatedastrueelseechoexpressionevaluatedasfalse此外,您可以检查“此文件存在”、“此目录存在”等内容。例如,您可以检查文件/tmp/awesome.txt是否存在,如这个:如果[[-e/tmp/awesome.txt]];thenecho"awesome"fi这通常是有用的,但我每次都必须查找语法。如果您想使用命令行进行试验,可以使用测试命令,例如test-e/tmp/awesome.txt。成功返回0,否则返回错误。***有一件事是为什么[[比[好:如果你使用[[,那么你可以使用<进行比较,它不会变成文件重定向。$[3<4]&&echo"true"bash:4:Nosuchfileordirectory$[[3<4]]&&echo"true"true关于if还有一个额外的***一件事:我今天学到了您不需要通过[[或[来使用if语句:任何有效的命令都可以使用。所以你可以做ifgreppeanutsfood-list.txtthenecho"allergyallert!"fi函数并不那么难在bash中定义和调用函数(尤其是没有参数的函数)非常容易。my_function(){echo"这是一个函数";}my_function#Callingfunctionsalwaysquoteyourvariables另一个bash技巧:永远不要使用没有被引用的变量。看看这个看似合理的shell脚本:X="iamawesome"Y="iareawesome"if[$X=$Y];然后echoawesomefi如果你尝试运行这个脚本,你会得到非理性的错误Messagescript.sh:line3:[:toomanyarguments.什么?Bash将此if语句解释为if[iamawesome==iareawesome],这是一个对6个字符串(i,am,awesome,i,are,awesome)毫无意义的if语句。正确的写法是X="iamawesome"Y="iareawesome"if["$X"="$Y"];然后#I放引号是因为我知道bash会背叛我,如果我不放它echoawesomefi在某些情况下,只需使用$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。这在科技界非常巧妙。后台进程我不会在这里过多谈论作业控制,但是:你可以像这样启动一个后台进程去做这个。如果进程不止一个,可以使用jobs查看所有后台进程。出于某种原因,fg需要一个“作业ID”(即作业打印出来的内容)而不是PID。谁知道为什么Bash会这样。此外,如果后台运行的进程太多,内置的wait命令将等待它们全部返回。说到遗憾——如果你不小心在错误的终端启动了一个进程,NelsonElhage有一个很棒的项目reptyr可以保存你的进程并将其移动到屏幕会话或其他东西中。