3

I am new to bash scripting and i'm just trying out new things and getting to grips with it.

Basically I am writing a small script to store the content of a file in a variable and then use that variable in an if statement.

Through step by step i have figured out the ways to store variables and then store content of files as variables. I am now working on if statements.

My test if statement if very VERY basic. I just wanted to grasp the syntax before moving onto more complicated if statement for my program.

My if statement is:

if [ "test" = "test" ]
then
    echo "This is the same"
fi

Simple right? however when i run the script i am getting the error:

syntax error near unexpected token `fi'

I have tried a number of things from this site as well as others but i am still getting this error and I am unsure what is wrong. Could it be an issue on my computer stopping the script from running?

Edit for ful code. Note i also deleted all the commented out code and just used the if statement, still getting same error.

#!/bin/bash
#this stores a simple variable with the content of the file testy1.txt
#DATA=$(<testy1.txt)
#This echos out the stored variable
#echo $DATA
#simple if statement
if [ "test" = "test" ]
then
    echo "has value"
fi
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • 1
    I tried it by my own it is not getting any error. But it lokks like you have a backtik before the `fi` – Jens Dec 12 '16 at 12:03
  • Not getting errors either. Can you check you're not having hidden characters around the `fi`? Are you running it using `/bin/bash` ? – Avihoo Mamka Dec 12 '16 at 12:06
  • 1
    I just checked. I dont have any hidden characters around the fi. I am using the #!/bin/bash at the start of the script. I mean if its working for 2 people its reasonable to assume its an issue with the shell on my pc? – Mark Skeath Dec 12 '16 at 12:10
  • 1
    @MarkSkeath: Is the snippet you posted your *entire* script? There could be errors before that cause an error message at the wrong line number. Copy paste the script you are running into your question. Also check the output of `cat -A sctipt.sh` to see if there are any strange characters. – user000001 Dec 12 '16 at 12:34
  • @MarkSkeath: Comments don't allow newlines. Edit your question instead – user000001 Dec 12 '16 at 12:40
  • @MarkSkeath: How exactly are you executing the script? just `./script.sh` ? Did you check the script is exectuable? – Avihoo Mamka Dec 12 '16 at 12:47
  • @AvihooMamka Yea i am opening the script through the shell, just clicking and dragging the file. It was working when it was just storing the variable and echoing it out. Its not working since i introduced the if statement – Mark Skeath Dec 12 '16 at 12:52
  • Did you write the script yourself? Or did you copy that if part from somewhere? The reason for asking, is maybe you have some non-unix line-end characters? – Avihoo Mamka Dec 12 '16 at 12:58
  • 1
    @AvihooMamka I did lookup how to write if statements and did use some statements as examples however i wrote the whole code myself and did not copy or paste anything. I am starting to believe that its perhaps not an issue with my code but something to do with my laptop. I was going to test the code when i get home and see – Mark Skeath Dec 12 '16 at 13:02
  • what happens when you run the script with `bash ./script.sh` -- the same? – chw21 Dec 12 '16 at 13:07
  • @chw21 Yea same thing – Mark Skeath Dec 12 '16 at 13:08

4 Answers4

4

If a script looks ok (you've balanced all your quotes and parentheses, as well as backticks), but issues an error message, this may be caused by funny characters, i.e. characters that are not displayed, such as carriage returns, vertical tabs and others. To diagnose these, examine your script with

od -c script.sh

and look for \r, \v or other unexpected characters. You can get rid of \r for example with the dos2unix script.sh command.

BTW, what operating system and editor are you using?

Jens
  • 69,818
  • 15
  • 125
  • 179
  • Thanks for the info, that's useful command to know! I am on windows 7 using cygwin. Would this be causing the issue? Sorry thats somthing I should of mentioned in my main post – Mark Skeath Dec 12 '16 at 14:34
  • 1
    @MarkSkeath If you use a Windows Editor yes, it likely uses Carriage Return plus Linefeed (\r\n) line endings. Many Unix/Cygwin tools will choke on the \r since they expect \n line endings. (I've added the Cygwin and Win7 tags). – Jens Dec 12 '16 at 14:41
  • Ah right ok. So i tried to run the dos2unix on my script but it says the command is not found. Is there any other way to remove the \r characters? As i do see on each new line its giving me a \r \n – Mark Skeath Dec 12 '16 at 14:47
  • Just wanted to say thanks @Jens. Due to your answer and with mklement0 expanding on it I was able to get the issue solved. – Mark Skeath Dec 12 '16 at 16:11
  • 1
    @MarkSkeath dos2unix is part of the cygwin package of the same name. You can install it. See http://superuser.com/questions/612435/cygwin-dos2unix-command-not-found – Jens Dec 12 '16 at 17:23
1

syntax error near unexpected token `fi' means that if statement not opened and closed correctly, you need to check from the beginning that every if, for or while statement was opened and closed correctly. Don't forget to add in the beginning of script :

#!/bin/bash

medismail
  • 29
  • 4
  • 1
    The shebang is only necessary if you plan on executing the script by itself, not by running a shell explicitly and passing the script name as an argument. Its absence is not, in and of itself, a source of syntax errors. – chepner Dec 12 '16 at 12:39
1

To complement Jens's helpful answer, which explains the symptoms well and offers a utility-based solution (dos2unix). Sometimes installing a third-party utility is undesired, so here's a solution based on standard utility tr:

tr -d '\r' < script > script.tmp && mv script.tmp script

This removes all \r (CR) characters from the input, saves the output to a temporary file, and then replaces the original file.

  • While this blindly removes \r instances even if they're not part of \r\n (CRLF) pairs, it's usually safe to assume that \r instances indeed only occur as part of such pairs.
  • Solutions with other standard utilities (awk, sed) are possible too - see this answer of mine.
    If your sed implementation offers the -i option for in-place updating, it may be the simpler choice.

To diagnose the problem I suggest using cat -v script, as its output is easy to parse visually: if you see ^M (which represents \r) at the end of the output lines, you know you're dealing with a file with Window line endings.


Why Your Script Failed So Obscurely

Normally, a shell script that mistakenly has Windows-style CRLF line endings, \r\n, (rather than the required Unix-style LF-only endings, \n) and starts with shebang line #!/bin/bash fails in a manner that does indicate the cause of the problem:

/bin/bash^M: bad interpreter

as a quick SO search can attest. The ^M indicates that the CR was considered part of the interpreter path, which obviously fails.
(If, by contrast, the script's shebang line is env-based, such as #!/usr/bin/env bash, the error message differs, but still points to the cause: env: bash\r: No such file or directory)

The reason you did not see this problem is that you're running in the Windows Unix-emulation environment Cygwin, which - unlike on Unix - allows a shebang line to end in CRLF (presumably to also support invoking other interpreters on Windows that do expect CRLF endings).

The CRLF problem therefore didn't surface until later in your script, and the fact that you had no empty lines after the shebang line further obfuscated the problem:

  • An empty CRLF-terminated line would cause Bash (4.x) to complain as follows: "bash: line <n>: $'\r': command not found, because Bash tries to execute the CR as a command (since it doesn't recognize it as part of the line ending).

  • The comment lines directly following the shebang lines are unproblematic, because a comment line ending in CR is still syntactically valid.

  • Finally, the if statement broke the command, in an obscure manner:

    • If your file were to end with a line break, as is usually the case, you would have gotten syntax error: unexpected end of file:

      • The line-ending then and if tokens are seen as then\r and if\r (i.e., the CR is appended) by Bash, and are therefore not recognized as keywords. Bash therefore never sees the end of the if compound command, and complains about encountering the end of the file before seeing the if statement completed.
    • Since your file did not, you got syntax error near unexpected token 'fi':

      • The final fi, due to not being followed by a CR, is recognized as a keyword by Bash, whereas the preceding then wasn't (as explained). In this case, Bash therefore saw keyword fi before ever seeing then, and complained about this out-of-place occurrence of fi.

Optional Background Information

Shell scripts that look OK but break due to characters that are either invisible or only look the same as the required characters are a common problem that usually has one of the following causes:

  • Problem A: The file has Windows-style CRLF (\r\n) line endings rather than Unix-style LF-only (\n) line endings - which is the case here.

    • Copying a file from a Windows machine or using an editor that saves files with CRLF sequences are among the possible causes.
  • Problem B: The file has non-ASCII Unicode whitespace and punctuation that looks like regular whitespace, but is technically distinct.

    • A common cause is source code copied from websites that use non-ASCII whitespace and punctuation for formatting code for display purposes;
      an example is use of the no-break space Unicode character (U+00A0; UTF-8 encoding 0xc2 0xa0), which is visually indistinguishable from a normal (ASCII) space (U+0020).

Diagnosing the Problem

The following cat command visualizes:

  • all normally invisible ASCII control characters, such as \r as ^M.
  • all non-ASCII characters (assuming the now prevalent UTF-8 encoding), such as the non-break space Unicode char. as M-BM- .

^M is an example of caret notation, which is not obvious, especially with multi-byte characters, but, beyond ^M, it's usually not necessary to know exactly what the notation stands for - you just need to note if the ^<letter> sequences are present at all (problem A), or are present in unexpected places (problem B).

The last point is important: non-ASCII characters can be a legitimate part of source code, such as in string literals and comments. They're only a problem if they're used in place of ASCII punctuation.

LC_ALL=C cat -v script

Note: If you're using GNU utilities, the LC_ALL=C prefix is optional.

Solutions to Problem A: translating line endings from CRLF to LF-only

  • For solutions based on standard or usually-available-by-default utilities (tr, awk, sed, perl), see this answer of mine.

  • A more robust and convenient option is the widely used dos2unix utility, if it is already installed (typically, it is not), or installing it is an option.
    How you install it depends on your platform; e.g.:

    • on Ubuntu: sudo apt-get install dos2unix
    • on macOs, with Homebrew installed, brew install dos2unix

dos2unix script would convert the line endings to LF and update file script in place.

Note that dos2unix also offers additional features, such as changing the character encoding of a file.

Solutions to Problem B: translating Unicode punctuation to ASCII punctuation

Note: By punctuation I mean both whitespace and characters such as -

The challenge in this case is that only Unicode punctuation should be targeted, whereas other non-ASCII characters should be left alone; thus, use of character-transcoding utilities such as iconv is not an option.

nws is a utility (that I wrote) that offers a Unicode-punctuation-to-ASCII-punctuation translation mode while leaving non-punctuation Unicode chars. alone; e.g.:

nws -i --ascii script  # translate Unicode punct. to ASCII, update file 'script' in place

Installation:

  • If you have Node.js installed, install it by simply running [sudo] npm install -g nws-cli, which will place nws in your path.

  • Otherwise: See the manual installation instructions.

nws has several other functions focused on whitespace handling, including CRLF-to-LF and vice-versa translations (--lf, --crlf).

Community
  • 1
  • 1
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • 1
    Thanks to you and jens answer I have managed to get the if statement running. I really appreciate all your and Jens help! – Mark Skeath Dec 12 '16 at 16:04
  • @MarkSkeath: Glad to hear it; because I couldn't quite make sense of your exact symptoms - I expected your script to fail differently - I've dug deeper: please see my update. – mklement0 Dec 12 '16 at 19:17
  • 1
    This is a extremely well written comprehensive answer that explains a lot to me. Being fairly new to writing scripts in shell (wrote a few in PowerShell nothing in bash) I am just trying new things. Again thank you so much for your help. – Mark Skeath Dec 13 '16 at 09:20
  • It also explains another question I had. I have programmed alot in java and c# so i am used to writing code in a manner at which i find it easy to read. When i did this in bash i had taken a new line after the /bin/bash which gave me the error you mentioned. Not knowing any different i thought that you could not leave blank lines in the code. Your answer went to explaining this question to me as well. So again thank you! – Mark Skeath Dec 13 '16 at 09:31
  • @MarkSkeath: My pleasure; glad to hear it was helpful; I learned a few things too. I took the liberty of adding more tags to your question and extending the title, so as to make it easier to discover by others. – mklement0 Dec 13 '16 at 14:23
-1

Here, "$test" should be a variable that stores the content of that file.

if [ "$test" = "test" ]; then
    echo "This is the same"
fi
Tanmay Vats
  • 409
  • 1
  • 5
  • 17
  • Ah no, I know how to store content of files in variables and call them. This is mainly an if statement issue. I wasn't planning on inputting the file variable in until i got a basic if statement working. Thanks though! – Mark Skeath Dec 12 '16 at 12:08