I wrote that script but if I run it I get this output:

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

The script is:


while [ $key = "q"]; do

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

        read -t 1 -n 1 key


2 Answers 2

while [ $key = "q"]; do

should be

while [ $key = "q" ]; do


if[$dateTex !eq $datePdf]

should be

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

not to mention

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

and the other similar line are broken and should be something like:

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

Core Problem

Unlike many other programming languages, Bourne shell syntax has certain peculiarities, especially regarding whitespace in certain places. And that's what's messing you up here:

It's important to remember that [ is a command, not a syntax feature (footnote 1), and that ] is the final argument to that command.

In Bourne (and similar/derivative) shell, whitespace (footnote 2) separates commands and their arguments, and while/if/until keywords actually just invoke whatever command comes after those keywords, and act based on the return value.

Another unusual nature of Bourne shell that looks like a problem in your code is variable assignments, which are also specific about whitespace: There must be no whitespace between the variable name, the = sign, and the thing being assigned.

Also, the output of commands does not automatically go into variables like in most languages: in order to do that, you need to use "command substitution". The new, nestable, and more widely recommended syntax for that is $(command to run) (footnote 3).

Examples In Your Code

So applying the above to your code:

while [ $key = "q"]; do

..the problem is the "q"]. The shell sees that as one argument to the test/[ command: q] (the quotes also do nothing here: the shell doesn't have variable types: everything is a string, except in a few contexts, so q is already a string - quotes are for escaping special characters). I would fix it like this:

while [ "$key" = q ]; do

..I also quoted $key in order to make sure it's always interpreted correctly (footnote 4). The same problem (and one other) happens here:

if[$dateTex !eq $datePdf]

..remember that the shell relies on whitespace to tell where one thing ends and another begins (again, footnote 2): if you say if[$some_var, it will first replace $some_var with some_text, getting if[some_text, and then it will try to execute a command named if[some_text. So first, you want if separated by a space from the [.

This is why your code prints the error that it does: bash expects a then only after it sees an if, but it never sees an if, it sees an if[$dateTex, which gets parsed as an entirely different command/token.

Same principle is why you want to separate [ from $dateTex, and $datePdf from ], with a space.

Finally, !eq is not a valid argument/test that the [ command recognizes: -eq interprets the arguments around it as integers (which are still strings as far as the shell is concerned going into the [ command, whether or not you quote them), and compares them for equality. You had the right idea with ! as the negation operator, but as with so many other things in the shell, it must be a separate argument to [, before the others. So you'd want:

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

..or you can use the shell's negation operator, instead of the [ command's (again, watch the spacing - it has to be a separate token/field when the shell syntax splits on whitespace):

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

Also, per the other answer: = just checks for the two argument strings being the same (as opposed to interpreting the argument strings as integers before comparing them, like -eq does), and it also supports a special-case negated != operator to check for two strings being different. If a string comparison is sufficient in your usecase, then you can use one of these instead:

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

Now onto these lines with variable assignments:

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

First, the way the shell will parse dateTex = grep $1.tex is to try to run the command dateTex with the arguments =, grep and one or more arguments depending on what $1 expands to (again, footnote 4). So, second, you should quote the $1. And for the cut command, the -b option takes the list as one argument, so you want to combine 43-54 into one (no whitespace). Anyway, it would appear you actually wanted to use command substitution, so you want something like this:

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

..and do the same for the other line. Finally, grep filename seems wrong: grep's first argument (besides options) is supposed to be the pattern to search/match for. So as you have it written in your example code, it will read from and search stdin for whatever pattern "$1".tex expands to. You likely want to search the file "$1".tex for a pattern, so you want grep pattern "$1".tex instead.

Having gone over all of this, I suspect that either your actual code is different than what you've posted, or it's printing more error messages than just that one.


[1] The command is test, and [ is just another name the test command can be called with, the only difference being is that it expects ] to be the last argument when it's called as ]. Technically, in most implementations it will be a shell builtin, but the syntax rules are the same.

[2] Technically it's whatever is in the IFS (Internal Field Separator) variable, but that's usually spaces, tabs, and newlines (with newlines being slightly special for other reasons).

[3] The old, but not nestable syntax is `command to run` - you'd use this if you wanted your script to run on very old or somewhat not-standard shells, like Solaris 10's /bin/sh. Personally, I think this syntax is perfectly fine and I don't think nesting (the only real advantage of the new syntax) is ever necessary in scripts - though I can see how it makes interactive command-line use easier.

[4] When a variable is not quoted, whitespace (or whatever is in $IFS, per footnote 1) in a variable is evaluated after the variable is substituted, and "field splittling" is performed on the entire line again. So if $key is unset, blank, or just only whitespace characters, it will basically "disappear" from the command if it's not quoted. Add if there's a mix, e.g. $key contains abc def, it'll become two arguments abc and def. The same principle applies to the entire line with a substitution, so if $key contains abc def and the command line is foo$keybar, the shell splits the string into fooabc defbar (run command fooabc with one argument defbar). None of this happens if the variable is quoted: "$key".

Not the answer you're looking for? Browse other questions tagged .