如何在不运行Bash脚本的情况下进行语法检查?

322

如何在不执行脚本的情况下检查bash脚本的语法是否正确?

使用Perl,可以运行 perl -c '脚本名称'命令。是否有类似于Bash脚本的等效命令?


1
相关:是否有像Lint或Perl :: Critic这样的静态分析工具适用于shell脚本? - blong
10个回答

457
bash -n scriptname

也许一个显而易见的警告:这个工具可以验证语法,但不检查您的bash脚本是否尝试执行不在您路径中的命令,例如ech hello而不是echo hello


9
在bash的man页中,在"SHELL BUILTIN COMMANDS / set"下文档记录了-n,并且如man页开头所述,bash解释了set所有单字符选项。 - ephemient
25
除了对我来说不明显的警告之外,它还不能捕捉由于缺少空格而导致的错误,例如 if ["$var" == "string" ] 而不是 if [ "$var" == "string" ]。请注意,翻译保持原义,通俗易懂。 - Brynjar
15
这是因为它只是语法检查。开放的括号不是语法,那是要运行的函数的名称。“type [说:“[ 是一个Shell内置”。它最终委托给'test'程序,但它也期望有一个闭合的括号。所以这就像if test"$var"`,这不是作者想表达的意思,但在语法上是有效的(假设$var的值为“a”,那么我们将看到“bash:testa: command not found”)。关键是从语法上讲,没有缺少空格。 - Joshua Cheek
3
在这里,Bash 仍在进行语法检查:它正在检查简单命令调用的语法:["$var" 在语法上是一个有效的命令名称表达式;同样,标记 =="$string" 是有效的命令参数。通常情况下,内置的 [ 会被解析为命令语法,而作为 shell 关键字的 [[ 则有不同的解析方式。Shell 内置的 [ 不会委托给“test 程序”(外部实用程序):bashdashkshzsh 都有 [test 的内置版本,并且它们不会调用其外部实用程序对应物。 - mklement0
2
底线是:仅使用Bash本身,-n是最好的选择,但这不会捕获初学者可能会犯的许多错误;为了捕获这些错误,请使用http://shellcheck.net或其CLI,如[dvd818的答案](https://dev59.com/y3VC5IYBdhLWcg3w1Exq#19573083)中所述。 - mklement0
显示剩余7条评论

160

时间改变一切。 这个网站 提供在线的 shell 脚本语法检查,我觉得它可以很好地检测常见错误。

图片描述

关于 ShellCheck

ShellCheck 是一个用于 sh/bash 脚本的静态分析和 lint 工具。它主要关注处理初学者和中级水平语法错误和陷阱,其中 shell 只会给出晦涩难懂的错误消息或奇怪的行为,但它还报告了一些更高级的问题,其中边角情况可能导致延迟故障。

ShellCheck 的 Haskell 源代码可在 GitHub 上找到!


7
好的提示:在 macOS 上,现在还可以通过 Homebrew 安装 shellcheck.net 命令行工具 shellcheckbrew install shellcheck - mklement0
5
在Debian及其衍生版本上,执行命令apt-get install shellcheck。该命令的作用是安装ShellCheck工具。 - that other guy
对于Ubuntu Trusty,此软件包必须从“trusty-backports”安装。 - Peterino
如上所述,需要安装trusty依赖项,可以在Ubuntu 14.04中按以下方式安装:sudo apt-get -f install,然后:sudo sudo apt-get install shellcheck。 - zhihong
1
这确实很有用,但它并没有使用Bash的解析器而是使用了自己的解析器。在大多数情况下,这已经足够好了,它可以识别出解析和其他问题,但至少有一种边缘情况(可能还有其他我没见过的情况)它无法完全解析相同的方式。 - Daniel H
如果你想只有在程序通过shellcheck检查之后才运行它:https://gitlab.com/es20490446e/shellproof - Alberto Salvia Novella

42

为了进行一些额外的检查,我也会在编写的每个bash脚本中启用'u'选项:

set -u 

这将报告未初始化变量的使用情况,就像在以下脚本“check_init.sh”中一样。

#!/bin/sh
set -u
message=hello
echo $mesage

运行脚本:

$ check_init.sh

将报告以下内容:

./check_init.sh[4]: mesage: 参数未设置。

非常有用,可以捕捉拼写错误。


5
我总是在我的Bash脚本中设置这些标志,如果它们通过了这些标志,就可以继续执行。"set -o errexit"、"set -o nounset"、"set -o pipefail"。 - μολὼν.λαβέ
3
+1 对于 set -u,虽然这并没有真正回答问题,因为您必须运行脚本才能获取错误消息。即使是 bash -n check_init.sh 也不会显示该警告。 - rubo77

26
sh  -n   script-name 

运行此脚本。如果脚本中存在语法错误,则返回相同的错误消息。 如果没有错误,则不会给出任何消息。您可以使用echo $?立即检查,它将返回0,证实成功且没有错误。

这对我很有效。我在Linux操作系统、Bash Shell上运行。


1
尽管与bash语法检查没有直接关系,但在调试整个脚本或脚本的某些部分时使用set -x和set +x非常有用。 - GuruM
谢谢,我不知道-n选项,但是我想要的是@GuruM > sh -x test.sh,因为它可以显示脚本生成的输出。 - zzapper
1
是的。我忘了提到你可以在以下情况下执行以下操作 在命令行中: 1)bash -x test.sh #这将以“调试模式”运行整个脚本 2)set +x; bash test.sh; set -x #在脚本运行之前/之后设置调试模式开/关 在脚本中: a)#!/bin/bash -x #在脚本顶部添加“调试模式” b)set +x; code; set -x #为脚本的任何部分添加“调试模式” - GuruM
sh -n 可能不会检查脚本是否是有效的 Bash 脚本。它可能会给出错误的负面结果。sh 是一些 Bourne shell 变体,通常不是 Bash。例如,在 Ubuntu Linux 中,realpath -e $(command -v sh) 给出 /bin/dash。 - jarno

6

我实际上使用 find 工具来检查当前目录中所有 Bash 脚本的语法错误,而不运行它们:

例如:

find . -name '*.sh' -print0 | xargs -0 -P"$(nproc)" -I{} bash -n "{}"

如果您只想用于单个文件,请将通配符更改为文件名。


4

在调试时,null命令[冒号]也非常有用,可以查看变量的值。

set -x
for i in {1..10}; do
    let i=i+1
    : i=$i
done
set - 

它使用参数扩展。 - mug896
2
这个可以工作,因为 set -x 会在每行执行之前显示它。 - rubo77

3

仅用于验证语法的工具:

shellcheck [程序路径]

如果语法通过,则仅运行该程序,以便调试语法和执行:

shellproof [程序路径]


3

Bash shell脚本将运行语法检查,如果您启用了语法检查,则会进行检查。

set -o noexec

如果你想关闭语法检查

set +o noexec

2

1
但是如果您的文件不以.sh或其他与Bash脚本相关的扩展名结尾,则它将无法正常工作,如果您使用某些模板工具(例如ERB)生成脚本,则会出现这种情况(然后它们以.erb结尾)。如果您希望修复此问题,请为https://youtrack.jetbrains.com/issue/IDEA-79574投票! - Greg Dubicki

1
如果您需要在一个变量中获取目录中所有文件的有效性(例如git pre-commit hook,build lint script),您可以将“sh -n”或“bash -n”命令的stderr输出捕获到一个变量中,并基于此进行“if / else”判断。
bashErrLines=$(find bin/ -type f -name '*.sh' -exec sh -n {} \;  2>&1 > /dev/null)
  if [ "$bashErrLines" != "" ]; then 
   # at least one sh file in the bin dir has a syntax error
   echo $bashErrLines; 
   exit; 
  fi

根据您的需要,使用“bash”替换“sh”


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接