0

I have two files, say file1="/scratch/abc/osx.xrts" and file2="/scratch/unzip/abc/osx.xrts".

I am executing the diff command, ignoring the line endings. Command and code is as below:

diff --strip-trailing-cr file1 file2 > /dev/null
if [ $? -eq 0 ]; then
  echo "no diff"
elif if [ $? -eq 1 ]; then
  echo "there is diff"
else
  echo "something is wrong"
fi

Result Observed

When I execute this, if the file1 and file2 contents are the same, then I am getting result as "no diff" which is correct. But the problem comes when the file1 and file2 contents have some differences,.  In this case it is just executing the diff command (as mentioned above) and printing the differences and exiting. It is not going to the if-else section at all.

I am running this code in a for loop (not shown here) as there are many files which I need to compare.  But my script exits as soon as it finds the first pair of files with a diff by printing the difference and it is not going to the if-else section at all.  Not sure what is wrong with it.  Any help would be highly appreciated

Result I am Expecting

When there is a diff between the files then it should not print the difference. Instead it should go to the section elif if [ $? -eq 1 ]; then and print "there is diff", and then continue with the for loop by comparing the other files.

3

If file1 and file2 exist and are equal, your code snippet, as shown, generates a syntax error: unexpected end of file. This is because of an unterminated if statement where the shell reaches the end of the script before it encounters a matching fi. This is caused by having elif if instead of elif. Unless there are other errors in the rest of your code, this should resolve the problems you’ve reported.

Also, you can check the exit code of a command directly in one line instead of running the command and checking its exit status in two separate statements:

if diff --strip-trailing-cr file1 file2 > /dev/null; then
  echo "no diff"
elif [ $? -eq 1 ]; then
  echo "there is diff"
else
  echo "something is wrong"
fi

By the way, when writing shell scripts, it’s really useful to check them with ShellCheck – shell script analysis tool.

| improve this answer | |
  • thanks for the solution – Prasobir Mandal Jul 23 '19 at 9:48
  • While this works for three possible control flows (0, 1, and other), it is not extensible — try adding an elif [ $? -eq 2 ]; then branch — and it papers over a flaw in the OP’s code. – G-Man Says 'Reinstate Monica' Feb 29 at 2:02
  • @G-ManSays'ReinstateMonica' If I can trust my memory, I was purposely trying to keep my answer simple (in line with that the OP's level of knowledge and what they were looking for). Rather than edit my answer to make it more general, I'll just upvote yours! – Anthony Geoghegan Feb 29 at 17:52
3

Your code, with the trivial typographical errors corrected, is

(1)      diff --strip-trailing-cr "$file1" "$file2" > /dev/null
(2)      if [ $? -eq 0 ]; then
(3)        echo "no diff"
(4)      elif [ $? -eq 1 ]; then
(5)        echo "there is diff"
(6)      else
(7)        echo "something is wrong"
(8)      fi

(The line numbers in the exhibit above are, of course, for illustration only, and are not part of the script.)  This has a subtle error that nobody else has pointed out — no matter what the exit status of diff is, the echo "something is wrong" section will never be reached.  You can test this easily by replacing the diff command with (exit "$1") and then passing numbers like 0, 1, 2, 3, etc…, in as arguments.

The problem is that $? is a volatile variable.  At line 2 it is, of course, the exit status from the (diff) command on line 1.  But on line 4, $? is the exit status from the [ $? -eq 0 ] (test) command on line 2.  If the exit status from diff is anything other than 0, then the [ $? -eq 0 ] test will set $? to 1, and so you will always get the there is diff message.

There are a couple of ways to handle this.  One is to save the diff exit status in a stable variable:

diff --strip-trailing-cr "$file1" "$file2" > /dev/null
diff_exit_status=$?
if [ "$diff_exit_status" -eq 0 ]; then
  echo "no diff"
elif [ "$diff_exit_status" -eq 1 ]; then
  echo "there is diff"
else
  echo "something is wrong"
fi

Of course you are free to use a shorter variable name.

The second is to use a case statement:

diff --strip-trailing-cr "$file1" "$file2" > /dev/null
case "$?" in
  (0)
      echo "no diff"
      ;;
  (1)
      echo "there is diff"
      ;;
  (*)
      echo "something is wrong"
esac
| improve this answer | |
  • is there any reason to use fancy unicode like U+2474 = "parenthesized digit one" (which are not supported in all fonts and appear as hex boxes) instead of plain digits? – Uncle Billy Feb 29 at 8:56
  • @UncleBilly: Just to make it doubly clear that they aren’t part of the code, and shouldn’t be copied-and-pasted into a script — because some people don’t read text. But I’ll try to remember that “fancy Unicode characters like "parenthesized digit one" are not supported in all fonts and appear as hex boxes”, and maybe use them less often. – G-Man Says 'Reinstate Monica' Feb 29 at 23:14
  • 1
    That makes it less accessible than a plain (1), that's all. I can easily install any font I like on my Linux desktop, but not on my tablet or phone. The stackoverlow overlords could easily offer the option to have numbered lines in code boxes with some trivial css (which would allow the user to select the code without the line numbers), but they just don't care. – Uncle Billy Mar 1 at 7:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.