2

I'm not understanding why the diff command is not working correctly in my script. Can anybody clarify it for me?

Here is the bash script:

#!/bin/bash

SRC="src-folder"
DST="dst-folder"
FILES=( a/ b/ c/ )

for f in ${FILES[@]}; do
  cmd="diff -q  $SRC/$f $DST/$f"
  echo $cmd  # row-9
  $cmd       # row-10
done

Script output seems like this:

diff -q src-folder/a/ dst-folder/a/
diff -q src-folder/a/ dst-folder/a/
diff -q src-folder/a/ dst-folder/a/
diff -q src-folder/a/ dst-folder/a/
diff -q src-folder/a/ dst-folder/a/
diff -q src-folder/a/ dst-folder/a/
diff -q src-folder/a/ dst-folder/a/
(until buffer overflow)

When I disable/remove/comment row-10 I can see correct output (when I run these commands in an interactive shell rather than in the script I get correct results):

diff -q src-folder/a/ dst-folder/a/
diff -q src-folder/b/ dst-folder/b/
diff -q src-folder/c/ dst-folder/c/

When I change diff to rsync, everything is working correctly (call from script).

So how can I use diff in scripts? What's the reason that diff called by my script is not working as expected?

I tried also a very simple script:

#!/bin/bash

diff -q  src-folder/a dst-folder/a
diff -q  src-folder/b dst-folder/b

But it doesn't work. When I change diff command to any other (e.g. rsync), everything is working.

10
  • This site might and most likely will help you. geeksforgeeks.org/diff-command-linux-examples
    – David
    Oct 25, 2022 at 8:25
  • I understand diff command, but I don't understand why it is not working when I call it from bash script.
    – martin
    Oct 25, 2022 at 8:32
  • Try a fully qualified path
    – David
    Oct 25, 2022 at 8:37
  • I tried it - the same result. The first iteration ( subfolder "a" again and again)
    – martin
    Oct 25, 2022 at 8:53
  • 1
    @Dan, might be, but why then is it working fine in an interactive shell?
    – pLumo
    Oct 26, 2022 at 11:43

2 Answers 2

1

tl;dr: Works for me.

Long answer: When I run the script from your question on one of my test machine, it behaves exactly as expected:

a-schmidt@s-laugz2repo01:~$ ./show-diffs
diff -q src-folder/a/ dst-folder/a/
diff: src-folder/a/: No such file or directory
diff: dst-folder/a/: No such file or directory
diff -q src-folder/b/ dst-folder/b/
diff: src-folder/b/: No such file or directory
diff: dst-folder/b/: No such file or directory
diff -q src-folder/c/ dst-folder/c/
diff: src-folder/c/: No such file or directory
diff: dst-folder/c/: No such file or directory
a-schmidt@s-laugz2repo01:~$

So apparently the diff command on your system is not the same as on mine. There are several possibilities how this could happen, for example through shell aliases or local installation of a program of that name. In order to find out what it is, enter the shell command:

type diff

On an unmodified Ubuntu system this will output:

diff is /usr/bin/diff

If it outputs something else, such as "diff is aliased to ..." or "diff is a function ..." then you need to look into your shell profile to find out where that change is made, and possibly correct it.

1

I think the problem is that within your command cmd="diff -q $SRC/$f $DST/$f" there are options and after the expansion at row-10 the string contained in $cmd is not evaluated properly as a command by Bash.

When you are using the exec command the string contained in $cmd is evaluated properly. But what exec does is that it replaces the current shell with new one where the command is executed, so your loop no longer exists after the first iteration.

I've highlighted evaluated few times because in Bash have eval command which does exactly that - evaluates a string as a command. So I think the proper solution is to modify the script like this:

eval $cmd       # row-10

As a final note, if it is the entire script, why you need to assign the command to a variable which is evaluated immediately (at the next line)? I mean in this particular case you can simplify the loop in a way as this:

for f in ${FILES[@]}; do
  diff -q  "$SRC/$f" "$DST/$f"
done
2
  • Yes, I think the error lies in cmd="diff -q $SRC/$f $DST/$f"; echo $cmd that outputs diff -q src-folder/a/ dst-folder/a/, but $cmd is just a string at that point. Two things that may help, shellcheck and running the script with bash -x -v myscript.sh (and removing the quiet flag, -q, from diff). Easiest fix I think is either cmd=$("diff -q $SRC/$f $DST/$f"); echo $cmd where $cmd now holds the output of executing the diff (due to using -q, nothing if it matches) and deleting line 10; OR replace line 10 with "$($cmd)" to execute the string you placed in the $cmd variable.
    – pbhj
    Oct 26, 2022 at 16:44
  • Also, proper escaping is eg "${FILES[@]}" and "$SRC"/"$f" if you want to handle spaces in filenames and such. shellcheck is great for helping to identify improper escaping (you can use it online, or install from sudo apt install shellcheck.
    – pbhj
    Oct 26, 2022 at 16:49

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.