我编写了该脚本,但如果运行它,我会得到以下输出:
./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
//关键字实际上只是调用这些关键字后面的任何命令,并根据返回值进行操作。if
until
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_var
为some_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
未设置、空白或只是仅有的空白字符,如果不加引号,它基本上会从命令中“消失”。如果有混合,例如$key
contains ,则添加abc def
,它将成为两个参数abc
和def
。同样的原则也适用于带有替换的整行,因此如果$key
containsabc def
并且命令行为foo$keybar
,则 shell 会将字符串拆分为fooabc defbar
(run command fooabc
with one argument defbar
)。如果变量被引用:则不会发生这种情况"$key"
。