1

I have written a script for the purpose of calling it from other scripts and am having issues doing so.

The script I wish to call from other scripts works correctly:

#!/bin/bash

####################################################################################################
# This script will compare two directories given as args.
#
# exit status: 0 --> if directories are the same
#              1 --> if directories are not the same
#              2 --> incorrect number of args
#              3 --> one or more of the directories does not have read permissions
#              4 --> one or more of the directories does not exists
#              5 --> one or more of the directories is not a directory
####################################################################################################


[ $# -ne 2 ] && exit 2

[ ! -r "$1" ] || [ ! -r "$2" ] && exit 3
[ ! -e "$1" ] || [ ! -e "$2" ] && exit 4
[ ! -d "$1" ] || [ ! -d "$2" ] && exit 5

dir_1="$1"
dir_2="$2"
sha1_dir_1=$(find "$dir_1" -type f \( -exec sha1sum {} \; \) | awk '{print $1}' | sort | sha1sum)
sha1_dir_2=$(find "$dir_2" -type f \( -exec sha1sum {} \; \) | awk '{print $1}' | sort | sha1sum)

[ "$sha1_dir_1" = "$sha1_dir_2" ] && exit 0
[ "$sha1_dir_1" != "$sha1_dir_2" ] && exit 1

I have tested it by calling it from the command line. Below is a test script which I cannot seem to call the above script from successfully:

#!/bin/bash

dir_1="~/test"
dir_2="~/test1"
directories_are_same="$(~/bin/compare_directories "$dir_1" "$dir_2")"

{
    if [ $directories_are_same -eq 3 ]; then
        echo "One of the directories $dir_1 and $dir_2 does not have read permissions!"
        exit 1
    elif [ $directories_are_same -eq 4 ]; then 
        echo "One of the directories $dir_1 and $dir_2 does not exist!"
        exit 1
    elif [ $directories_are_same -eq 5 ]; then
        echo "One of the directories $dir_1 and $dir_2 is not a directory!"
        exit 1
    fi
} >&2

if [ $directories_are_same -eq 0 ]; then
    echo "The directories $dir_1 and $dir_2 contain identical content"
    exit 0
elif [ $directories_are_same -eq 1 ]; then
    echo "The directories $dir_1 and $dir_2 do not contain identical content"
    exit 0
else
    echo "Something went wrong" >&2
    exit 1
fi

The output from the test script I am getting is:

/home/astral/bin/updates_dir_if: line 8: [: -eq: unary operator expected
/home/astral/bin/updates_dir_if: line 11: [: -eq: unary operator expected
/home/astral/bin/updates_dir_if: line 14: [: -eq: unary operator expected
/home/astral/bin/updates_dir_if: line 20: [: -eq: unary operator expected
/home/astral/bin/updates_dir_if: line 23: [: -eq: unary operator expected
Something went wrong

I have tried using the full path to the script, the relative path from the test script which would be ./, and the way that it currently is ~/bin/scriptname. All give the same results.

I have read some posts which seem to imply that what I am doing should work, such as:

StackOverflow post

Quote your args in Testscript 1:

echo "TestScript1 Arguments:"
echo "$1"
echo "$2"
echo "$#"
./testscript2 "$1" "$2"

What am I doing incorrectly?

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
  • 1
    Run your calling script with `bash -x yourscript` – Bodo Feb 24 '21 at 15:41
  • Thank you both! I was introduced to shellcheck.net yesterday when I asked a poorly formatted question, which I decided to delete, about a poorly written script, which I am rewriting, and have found it very useful. I actually used `bash -xv myscript` to solve this issue and have posted the solution. – Astral Axiom Feb 24 '21 at 15:47
  • 1
    Related question regarding function output vs. exit status: [Returning value from called function in a shell script](https://stackoverflow.com/q/8742783/3266847) – Benjamin W. Feb 24 '21 at 15:56
  • Am I correct that another issue with the version of the test script in my question is that the quotes in the call to the other script are not used correctly? "$(command "arg" "arg")" will be interpreted as "$(command " ... arg ... "arg" ... )". Why did this not cause an error like `no such command: "arg"`? does the command substitution provide a new environment so that this is not an issue? – Astral Axiom Feb 24 '21 at 16:07
  • 1
    @AstralAxiom : That a script runs fine from the command line, does not mean it runs well from another context. Scripts can be sensitive to environment, working directory and so on. In addition, a possible bug is that you did not quote `$directories_are_same`. While it may work if everything goes well, it really depends on the correct output of `compare_directories` in your case, and quoting it would mean one thing less which you have to worry. – user1934428 Feb 25 '21 at 08:41
  • @user1934428 that is a good point. In the solution, the variable holds the value of `"$?"` which should always be some numeric value. I generally quote almost everything, even when I do not have to. In the case of `$directories_are_same` here, `bash -xv` says it held no value at all. In the solution, should I quote the variable even though it holds `"$?"`, which is always numeric ..I think? – Astral Axiom Feb 25 '21 at 11:03
  • My current actual use of the script which compares the two directories sha1 sums is to place a copy of it in `/usr/local/bin/` and change the shebang to `#!/bin/sh` and call it from a script in `/usr/local/bin/` which uses `wget` to retrieve my professor's code examples and update my semester directories. The script is run by SystemD at boot and only does the bulk of its work if the sha1 sum of the newly retrieved directory is different from the one in my home directory, which is a renamed version of it. – Astral Axiom Feb 25 '21 at 11:13
  • It runs a minute after boot to be sure everything is up and network connectivity has been achieved first, is what I meant ...via `/etc/systemd/system/update_examples.service` and `/etc/systemd/system/update_examples.timer` – Astral Axiom Feb 25 '21 at 11:29
  • The script also does not run the `wget` at all and simply exits if the directory's last modification date is today ...meaning it has already been updated today and I have just booted more than once. – Astral Axiom Feb 25 '21 at 11:50
  • I have edited the solution to quote the variable `directories_are_same` in the test script. Thanks for pointing that out @user1934428 : ) – Astral Axiom Feb 25 '21 at 20:29
  • 1
    I don't understand why you are catching the stdout of `compare_directories`. Actually, the result of the comparision is communicated to the caller via the exit code, not stdout. – user1934428 Feb 26 '21 at 08:02
  • 1
    @AstralAxiom : I now see that you reflected this in your own answer already. In this case, the quotes around _$?_ and _$directories_are_same_ are of course unnecessary, but they don't harm. – user1934428 Feb 26 '21 at 08:11
  • @user1934428 I could explain what the issue was a little more clearly in my solution. Thanks, I will update the answer. – Astral Axiom Feb 26 '21 at 18:41

1 Answers1

1

I figured it out. The exit status is what I need, so I need to call it and set the variable to the exit status stored in $?. The issue is that I was catching the stdout of the other script and storing it in the variable directories_are_same as if it were a return value that I was expecting, when what I needed was its exit status. I could echo something from the other script and then treat the stdout as a returned string, but that is not how this script was designed.

Here is the working test script:

#!/bin/bash

dir_1=~/test
dir_2=~/test1
~/bin/compare_directories "$dir_1" "$dir_2"
directories_are_same="$?"

{
    if [ "$directories_are_same" -eq 3 ]; then
        echo "One of the directories $dir_1 and $dir_2 does not have read permissions!"
        exit 1
    elif [ "$directories_are_same" -eq 4 ]; then 
        echo "One of the directories $dir_1 and $dir_2 does not exist!"
        exit 1
    elif [ "$directories_are_same" -eq 5 ]; then
        echo "One of the directories $dir_1 and $dir_2 is not a directory!"
        exit 1
    fi
} >&2

if [ "$directories_are_same" -eq 0 ]; then
    echo "The directories $dir_1 and $dir_2 contain identical content"
    exit 0
elif [ "$directories_are_same" -eq 1 ]; then
    echo "The directories $dir_1 and $dir_2 do not contain identical content"
    exit 0
else
    echo "Something went wrong" >&2
    exit 1
fi

Now when the directories are different I get:

The directories /home/astral/test and /home/astral/test1 do not contain identical content

and when they are the same I get:

The directories /home/astral/test and /home/astral/test1 contain identical content
  • 2
    A full path path is probably not required. `"~/test"` does not work because you quoted it and bash interprets the quoted `~` literally (meaning, this isn't your home directory but a directory named literally `~`). `dir_1=~/test` should work too (note the missing quotes). – Socowi Feb 24 '21 at 15:44
  • 1
    Instead of updating your answer by appending new information, you should re-write it as if you knew everything already – the history is preserved in the edit history. – Benjamin W. Feb 24 '21 at 15:57
  • OK, thanks! I did not want to take the credit when I was not the one who noticed that : ) – Astral Axiom Feb 24 '21 at 16:09
  • 1
    @AstralAxiom : _Bash is not a programming language, it is a scripting language._ A scripting language **is** a programming language. At least bash is [**Turing complete**](https://en.wikipedia.org/wiki/Turing_completeness) – user1934428 Feb 27 '21 at 18:04