-3
x=$?
if [ ${x} -eq '0' ]; then
echo "something missing"
exit 1
else
echo "all present"
fi

Why I am getting -eq: unary operator expected when executing above?

A more complete version of what I am doing:

for file in `cat ${2}` #$2 is file.txt 
do 
    if [ ! -f "${1}/${file}" ]; # $1 is a path 
    then 
        echo "$file is missing" 
        notok=$? 
    fi 
done 
if [ ${notok} -eq 0 ]; 
then 
    echo "need to check" 
    exit 1 
else 
    echo "All files present" 
fi

closed as unclear what you're asking by Mikel, peterh, DarkHeart, G-Man, EightBitTony Dec 19 '17 at 7:42

Please clarify your specific problem or add additional details to highlight exactly what you need. As it's currently written, it’s hard to tell exactly what you're asking. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • 5
    It seems that ${x} expands to nothing. Is that all your code? – nxnev Dec 13 '17 at 6:12
  • 1
    As @nxnev mentioned, -eq: unary operator expected is a direct result of x being unset or empty. – Travis Clarke Dec 13 '17 at 6:31
  • 2
    Possible duplicate of When is double-quoting necessary? – ilkkachu Dec 13 '17 at 10:28
  • From the script JdeBP edited in from the comment, it should be easy to see why notok can be unset in some cases, so IMO this just falls back to the lack of quoting – ilkkachu Dec 13 '17 at 10:29
  • 3
    notok is set to return code of echo command, I bet this is not what you want. – Archemar Dec 13 '17 at 11:01
4

It look like variable x is undefined.

try either

if [ ${x-1} -eq 0 ]

where ${x-1} will evaluated to $x's value if defined or 1 if not.

or better, I assume there is a command before x=$?, why not use

if cmd arg1 ... argn
then
    # true
else
    # false
fi
  • after using if [ "$x" -eq 0 ] , I am getting error:- [: : integer expression expected – deepak Dec 13 '17 at 7:10
  • 2
    @deepak – this is because x does not contain a value. Is there a command before your snippet? $? Contains exit status for last command. Where is your last command? This is the second time someone asked you to no response. Help us help you!, – Travis Clarke Dec 13 '17 at 7:46
  • 1
    @deepak – also, questions typically have question marks (?). Maybe your question should be: Does anyone have time to write a script for me? If your question is Why I am getting -eq: unary operator?, then you got your answer over an hour ago (just scroll up)... best of luck! – Travis Clarke Dec 13 '17 at 7:50
  • for file in cat ${2}------$2 is file.txt do if [ ! -f "${1}/${file}" ];--------$1 is a path then echo "$file is missing" notok=$? fi done if [ ${notok} -eq 0 ]; then echo "need to check" exit 1 else echo "All files present" fi [Here when notok is not qual to zero , then it should give as all files present but it does so along with error -eq: unary operator expected ] – deepak Dec 13 '17 at 9:33
  • That sort of stuff should have been in the question from the start and should not be buried in a comment on an answer. – JdeBP Dec 13 '17 at 10:22
0

Ok, now that you've edited your question, I think I know what is really happening.

Let's assume I have a directory (~/dir) which contains some files and a text file (~/file.txt) which contains some filenames:

$ ls ~/dir
1.txt  2.txt  3.txt  4.txt  5.txt
$ cat ~/file.txt
1.txt
2.txt
3.txt
4.txt
5.txt

As far as I know, your script reads the content of ~/file.txt and checks if those files (1.txt, 2.txt, etc.) exists inside of ~/dir.

But when the script runs:

$ script.sh ~/dir ~/file.txt
/home/user/bin/script.sh: line 11: [: -eq: unary operator expected
All files present

So, what's happening? Look at this excerpt of code:

if [ ! -f "${1}/${file}" ]; # $1 is a path 
then 
    echo "$file is missing" 
    notok=$? 
fi

The main flaw here is that if the content of both ~/dir and ~/file.txt matches, the echo "$file is missing"; notok=$? never gets executed. Then, if the notok variable holds no value, [ ${notok} -eq 0 ] fails because ${notok} expands to nothing (as many of us suspected).

That flaw is not the only one, but it's the reason why you are getting the [: -eq: unary operator expected error.

How do you solve it?

If you don't want to rewrite your script, just initialize the notok variable with any number different from 0 before your main code. For example:

notok='1'

for file in `cat ${2}` #$2 is file.txt 
do 
    if [ ! -f "${1}/${file}" ]; # $1 is a path 
    then 
        echo "$file is missing" 
        notok=$? 
    fi 
done 
if [ ${notok} -eq 0 ]; 
then 
    echo "need to check" 
    exit 1 
else 
    echo "All files present" 
fi

It works:

# When the content of ~/dir and ~/file.txt matches
$ ls ~/dir
1.txt  2.txt  3.txt  4.txt  5.txt
$ cat ~/file.txt
1.txt
2.txt
3.txt
4.txt
5.txt
$ script.sh ~/dir ~/file.txt
All files present

# When the content of ~/dir and ~/file.txt does not match
$ ls ~/dir
1.txt  2.txt  3.txt  4.txt  5.txt
$ cat ~/file.txt
foo
1.txt
2.txt
3.txt
4.txt
5.txt
$ script.sh ~/dir ~/file.txt
foo is missing
need to check

But if I were to rewrite the script, I'd do something like this:

# Initialize variables
# It's possible to parse arguments like `-d DIR -f FILE` but let's keep simple
dir="${1}"
txt="${2}"
missing='0'

# Don't read lines with for!
while IFS='' read -r 'file'; do
  if [ ! -f "${dir}/${file}" ]; then
    echo "Missing file: ${file}"
    # Use a simple counter instead of catch the exit code of `echo`
    missing=$(( missing + 1 ))
  fi
done < "${txt}"

if [ "${missing}" -gt '0' ]; then
  echo "Total: ${missing}"
  exit '1'
else
  echo 'All files are present'
fi
0

I think what you really want here is rsync:

rsync --list-only --files-from="$2" "$1" "$1/.."

The last argument is a dummy argument but is necessary; it could be any directory so long as it's not the same directory as $1 itself.

Alternatively, if all you want is the exit status and less verbosity:

rsync --dry-run --files-from="$2" "$1" "$1/.."

(Optionally add 2>/dev/null on the end, if you really only want the exit status.)


Now, for a review of the broken script you posted:

for file in `cat ${2}` #$2 is file.txt 

Backticks are deprecated; see Have backticks (i.e. cmd) in *sh shells been deprecated?

You're assuming that the filename passed in $2 doesn't have any spaces or special characters in its name; see Why does my shell script choke on whitespace or other special characters?

You're also assuming that all files listed in $2 don't have any special characters or whitespace in their names.

And you're assuming that the list is short enough that it won't exceed your shell's maximum length for an argument list. If the list contains wildcards (e.g. /*/*/*/*/../../../../*/*/*/*) then you will have lots and lots of problems.

do 
    if [ ! -f "${1}/${file}" ]; # $1 is a path 

Do you really only want regular files to be allowed? And not special files of any kind, and not even directories? If so then you may need to revise the rsync command given earlier.

Also, stylistic point: putting the ! before the [ as in if ! [ -f "$1/$file" ] is less likely to lead you astray. Nothing wrong with it in this specific case, however. (If you combine [ ! ... ] with missing double quotes you are very likely to be led astray.)

    then 
        echo "$file is missing" 
        notok=$? 

Unless the echo command fails, you're setting notok to 0 here in every case.

This is even sillier when you realize that you've already checked the exit status. You don't need to do so again. If you wrote notok=1 here (and added notok=0 to the top of your script) you would get a simpler and better form.

    fi 
done 
if [ ${notok} -eq 0 ]; 

Missing double quote characters. Again, see Why does my shell script choke on whitespace or other special characters? (and note its subtitle "an introductory guide to robust filename handling and other string passing in shell scripts").

then 
    echo "need to check" 

This message makes absolutely no sense. Need to check what? Who needs to check what? Huh?

    exit 1 
else 
    echo "All files present" 
fi

The rest is fine. Ish.


Replacement script

if rsync --dry-run --files-from="$2" "$1" "$1/.." 2>/dev/null; then
  echo "All files present"
else
  echo "need to check"
  exit 1
fi

Replacement script that disallows directories in list (will throw an error on them)

if ! rsync --dry-run --files-from="$2" "$1" "$1/.." --no-dirs 2>&1 | grep -q .; then
  echo "All files present"
else
  echo "need to check"
  exit 1
fi
-2

I’m all for long-winded scripts, but

if [ X”$x” = X”0” ] ; then doThis ; else doThat ;fi

is adequate, if not entirely correct. Don’t give me no lip, I saw that in some scripts in FreeBSD base. OP’s real problem is not getting return code reliably, but that appears to have been fixed by someone else.

  • Haha, who gave me a downvote? It’s a valid method!!1 – user2497 Dec 19 '17 at 16:26

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