Bash shell脚本错误sh: [: missing `]'。

6

目标:

我想创建一个有用的快捷方式,用于在本地初始化git仓库,并同时在Bitbucket或Github上创建远程仓库origin。这个函数被添加到了我的主目录下的.bashrc文件中。

以下是Bash函数(会产生错误):

function initg() {
 # Defaults to bitbucket API
 local API="https://api.bitbucket.org/1.0/repositories/"
 local DATA="name=$3&description=$4&is_private=true&scm=git"
 local REMOTE="ssh://git@bitbucket.org/$2/$3"

 # Use github if specified
 if [ "$1" == "gh" ]; then
      API="https://api.github.com/user/repos"
      DATA='{"name":"$3", "description": "$4"}'
      REMOTE="git@github.com:$2/$3"
 fi

 if [ -z "$API" ] || [ -z "$DATA" ] || [ -z "$REMOTE" ] || [ -z "$2" ]
 then
      echo "A parameter is missing or incorrect"
 else
      curl -X POST -u $2 ${API} -d ${DATA}
      git init
      git add .
      git commit -m "Created repo"
      git remote add origin $REMOTE
      git push -u origin master
 fi
}

错误信息:

username@COMPUTER-NAME /path/to/local/repo
$ initg gh username bash_test desc
sh: [: missing `]'
sh: [: missing `]'
Enter host password for user 'username':

我的问题:

首先,我的错误在哪里?其次,我该如何改进此脚本的控制流程或结构,以实现所述目标?


3
尝试运行 bash -x initg gh username bash_test desc 命令来查找哪些行触发了错误,并使用 [[ ]] 语法而不是 [] (例如 我的一个脚本 中的用法)。 - VonC
4
它显示“sh:”,似乎意味着它正在运行在“sh”的一种正确变体,或者在POSIX兼容模式下的“bash”,这可能加剧了问题。此外,它是一个函数,因此虽然上面的代码看起来还可以,但您实际上可能正在调用错误的函数,因为它已经被资源化了。 - Anya Shenanigans
尝试将上述代码与触发函数的命令放入一个单独的文件中,比如test.sh,然后调用bash -x test.sh - devnull
$1 等于 "gh" 时,由于它们出现在单引号中,因此在设置 DATA 的值时不会扩展 $3$4。这可能会在稍后未使用引号的情况下调用 curl 时或可能(因为 [ 相关错误)与 -z 一起使用 $DATA 时导致问题。 - chepner
4个回答

40

我因为在 ] 前没有使用空格而遇到了这种错误,例如:

if [ "$#" -ne 2]; then

取而代之

if [ "$#" -ne 2 ]; then

PS: 我知道这是一个老问题,但它可能会对某些人有所帮助 :)


4

我曾多次遇到bash中的这种无意义错误(一个bug?),解决方法是将[ some-condition ]替换为test some-condition


3

更新/初步回答

严格来说,这不是一个(有知识的)答案,但我已经成功解决了这个问题。

调试

我在一个专门的脚本文件中运行了函数和命令,使用了原帖评论中devnull建议的bash -x test.sh命令。

这在shell中产生了相当多的反馈。以下是我认为最关键的反馈,在我终止它之前。

username@COMPUTERNAME /d/test
$ bash -x test.sh
+ initg gh username bash_test desc
+ local API=https://api.bitbucket.org/1.0/repositories/
+ local 'DATA=name=bash_test&description=desc&is_private=true&scm=git'
+ local REMOTE=ssh://git@bitbucket.org/username/bash_test
+ '[' gh == gh ']'
+ API=https://api.github.com/user/repos
+ DATA='{"name":"$3", "description": "$4"}'
+ REMOTE=git@github.com:username/bash_test
+ '[' -z https://api.github.com/user/repos ']'
+ '[' -z '{"name":"$3", "description": "$4"}' ']'
+ '[' -z git@github.com:username/bash_test ']'
+ '[' -z username ']'
+ curl -X POST -u username https://api.github.com/user/repos -d '{"name":"$3",' '"description":' '"$4"}'
+ Enter host password for user 'username':

问题:

问题似乎出在变量表达式(下面)中设置DATA的地方。首先,传递给脚本的参数,由$3$4引用,不能在单引号字符串内插值。

DATA='{"name":"$3", "description": "$4"}'

此外,逗号和第二个分号后的空格奇怪地包在单引号中,导致字符串被分成了几个部分,在失败的curl调用中可见。

解决方案

除了使用双方括号[[ ]]代替原来的单方括号之外,我只是用花括号包装了每个插值变量,例如${3},并修复了DATA变量赋值中引号和空格的使用(如下所示):

DATA="{\"name\":\"${3}\",\"description\":\"${4}\"}"

后记

如果有人能够进一步完善这个答案,那将会非常棒。我对于为什么在curl调用中使用的$DATA变量被解析为'{"name":"$3",' '"description":' '"$4"}'感到困惑。


正如你已经写过的那样,使用 ' 和 " 进行引用的方式是不同的。要获取确切的细节,请查看 `man bash' 中的引用部分。最重要的部分是 $ 替换在 ' 中不会发生。但是,在我看来,IMHO,DATA='{"name":"'$3'", "description":"'$4'"}' 看起来更好一些 :) - andi5

0

如果要给变量DATA提供数据,请尝试以下方法:

printf -vDATA '{"name":"%s", "description": "%s"}' $3 $4
echo "$DATA"

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