核心问题

核心问题

我编写了该脚本,但如果运行它,我会得到以下输出:

./autotex: line 7: syntax error near unexpected token `then'
./autotex: line 7: `            then'

脚本是:

#!/bin/bash

while [ $key = "q"]; do

        dateTex = grep $1.tex| cut -b 43 - 54
        datePdf = grep $1.pdf| cut -b 43 - 54
        if[$dateTex !eq $datePdf]
        then
                pdflatex $1.tex
        fi

        read -t 1 -n 1 key

done

答案1

while [ $key = "q"]; do

应该

while [ $key = "q" ]; do

if[$dateTex !eq $datePdf]

应该

if [ "$dateTex" != "$datePdf" ]

更何况

dateTex = grep $1.tex| cut -b 43 - 54

另一条类似的线已损坏,应该类似于:

dateTex=$(grep something $1.tex | cut -b 43-54)

答案2

核心问题

与许多其他编程语言不同,Bourne shell 语法具有某些特殊性,特别是在某些地方的空格方面。这就是让你困惑的地方:

重要的是要记住这[是一个命令,不是语法特征(脚注1),这]是最终的争论到该命令。

在 Bourne(和类似/派生)shell 中,空格(脚注2 )分隔命令及其参数,而while//关键字实际上只是调用这些关键字后面的任何命令,并根据返回值进行操作。ifuntil

Bourne shell 的另一个不寻常的性质(看起来像是代码中的问题)是变量赋值,它也特定于空格:必须有变量名、符号=和所分配的内容之间的空格。

此外,命令的输出不会像大多数语言那样自动进入变量:为此,您需要使用“命令替换”。新的、可嵌套的、更广泛推荐的语法是$(command to run)(脚注 3)。

代码中的示例

因此,将上述内容应用到您的代码中:

while [ $key = "q"]; do

..问题是"q"]. shell 将其视为test/[命令的一个参数:(q]引号在这里也不执行任何操作:shell 没有变量类型:一切都是字符串,除了在少数上下文中,因此q已经是字符串 - 引号用于转义特殊字符)。我会这样修复它:

while [ "$key" = q ]; do

..我还引用了它,$key以确保它始终被正确解释(脚注 4)。同样的问题(以及另一个问题)发生在这里:

if[$dateTex !eq $datePdf]

..请记住,shell 依靠空格来告诉一件事在哪里结束,另一件事从哪里开始(同样,脚注 2):如果您说if[$some_var,它会首先替换$some_varsome_text, getting if[some_text,然后它将尝试执行名为 的命令if[some_text。因此,首先,您需要if用空格与[.

这就是为什么您的代码会打印它所执行的错误:bashthen仅在看到 an 后才期望 a if,但它从未看到 an if,它看到 an if[$dateTex,它被解析为完全不同的命令/令牌。

同样的原则就是为什么要用空格[分隔$dateTex, 和$datePdf, 。]

最后,!eq不是命令[识别的有效参数/测试:-eq将其周围的参数解释为整数(就进入命令的 shell 而言,它们仍然是字符串[,无论您是否引用它们),并将它们进行比较平等。作为否定运算符,您的想法是正确的!,但与 shell 中的许多其他内容一样,它必须是[, 在其他参数之前的单独参数。所以你想要:

if [ ! "$dateTex" -eq "$datePdf" ]

..或者您可以使用 shell 的否定运算符,而不是命令[的(再次注意间距 - 当 shell 语法在空格上分割时,它必须是一个单独的标记/字段):

if ! [ "$dateTex" -eq "$datePdf" ]

另外,根据另一个答案:=只需检查两个参数字符串是否相同(而不是像在比较之前将参数字符串解释为整数-eq),并且它还支持特殊情况的否定!=运算符来检查两个字符串与众不同。如果字符串比较在您的用例中就足够了,那么您可以使用其中之一:

if [ "$dateTex" != "$datePdf" ]  # != operator
if [ ! "$dateTex" = "$datePdf" ] # test ! operator with test = operator
if ! [ "$dateTex" = "$datePdf" ] # shell ! operator with test = operator

现在到这些带有变量赋值的行:

dateTex = grep $1.tex| cut -b 43 - 54
datePdf = grep $1.pdf| cut -b 43 - 54

首先,shell 解析的方式dateTex = grep $1.tex是尝试使用dateTex参数运行命令=grep以及一个或多个参数,具体取决于$1扩展的内容(同样,脚注 4)。所以,第二,你应该引用$1.对于该cut命令,该-b选项将列表作为一个参数,因此您希望合并43-54为一个(无空格)。不管怎样,看起来你确实想使用命令替换,所以你想要这样的东西:

dateTex=$(grep "$1".tex | cut -b 43-54)

..并对另一条线执行相同的操作。最后,grep filename似乎是错误的:grep的第一个参数(除了选项之外)应该是要搜索/匹配的模式。因此,当您在示例代码中编写它时,它将从 stdin 中读取并搜索任何模式"$1".tex扩展为的内容。您可能想要在文件中搜索"$1".tex某种模式,因此您需要这样做grep pattern "$1".tex

完成所有这些后,我怀疑您的实际代码与您发布的代码不同,或者它打印的错误消息比该错误消息多。

脚注

[1] 该命令是test,并且只是可以调用[该命令的另一个名称,唯一的区别是,当它被称为 时,它期望是最后一个参数。从技术上讲,在大多数实现中它将是一个内置的 shell,但语法规则是相同的。test]]

[2] 从技术上讲,它是IFS(内部字段分隔符)变量中的任何内容,但这通常是空格、制表符和换行符(换行符由于其他原因稍微特殊)。

[3] 旧的但不可嵌套的语法是`command to run`- 如果您希望脚本在非常旧的或有些不标准的 shell(例如 Solaris 10 的/bin/sh.就我个人而言,我认为这种语法非常好,并且我不认为脚本中需要嵌套(新语法的唯一真正优势)——尽管我可以看到它如何使交互式命令行使用更容易。

[4] 当变量未加引号时,$IFS变量中的空格(或 中的任何内容,每个脚注 1)在变量被替换后将被求值,并且再次在整行上执行“字段拆分”。所以如果$key未设置、空白或只是仅有的空白字符,如果不加引号,它基本上会从命令中“消失”。如果有混合,例如$keycontains ,则添加abc def,它将成为两个参数abcdef。同样的原则也适用于带有替换的整行,因此如果$keycontainsabc def并且命令行为foo$keybar,则 shell 会将字符串拆分为fooabc defbar(run command fooabcwith one argument defbar)。如果变量被引用:则不会发生这种情况"$key"

相关内容