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
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
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
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
], with a space.
!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.
 The command is
[ 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.
 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).
 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.
 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.
abc def, it'll become two arguments
def. The same principle applies to the entire line with a substitution, so if
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: