tags:

views:

135

answers:

5

Hi all,

I am just new to programming in Unix and have a small issue that I am unsure of how to solve. The objective of this piece of my script is to offer the user various options as to the type of scan they would like to use. This scan detects duplicate files with specified variables depending on the option chosen. I am unable to get it working at all and am unsure why? Also could you please offer me advice on how I could better display the selection screen if possible. I have only pasted part of my code as I would like to figure out the rest of my objective myself. Please nothing too complicated as I'm just an amateur at this >< Many thanks for the help!

#!/bin/bash
same_name="1"
filesize="2"
md5sum="3"
different_name="4"
echo "The list of choices are, same_name=1, filesize=2, md5sum=3 and different name=4"
echo "Search for files with the:"
read choice 
if [$choice == "$same_name" ];then
find /home/user/OSN -type f -exec basename '{}' \; | sort > filelist.txt
find /home/user/OSN -type f -exec basename '{}' \; | sort | uniq -d > repeatlist.txt
else
ls -al /home/user/OSN  >2filelist.txt
fi
+5  A: 

It would help if you included the error messages you were receiving. When I tried this, I got an error of:

./foo: line 9: [1: command not found

This makes the problem fairly clear. The [ operator in the if statement is, in Unix's "never use something complicated when some simple hack will work" style, just another program. (See ls /bin/[ for proof!) As such, it needs to be treated like any other program with command-line options; you separate it from its options with whitespace. Otherwise, bash will think that "[$choice", concatenated, is the name of a program to execute and will try to execute it. Thus, that line needs to be:

if [ $choice == "$same_name" ];then

After I changed that, it worked.

Also, as a style suggestion, I'd note that the case construct is a much easier way to write this code than using if statements, when you've got more than one test. And, as noted in other answers, you should put " marks around $choice, to guard against the case where the user input is empty or contains spaces -- $choice, unquoted, will expand to a list of zero or more tokens separated by whitespace, whereas "$choice" always expands to a single token.

Brooks Moses
Yes that was the error I received, many thanks for the help! I will remember for next time to post the error too. Just my brain was dead to have remembered to do such a simple thing.
Joey jie
Except that Bash uses a built-in `[` instead of `/bin/[`. But it works basically the same.
Dennis Williamson
Dennis: Good point; thanks! Though, if I recall correctly, it's a built-in pretty much only for performance reasons and it otherwise acts identically to an executable.
Brooks Moses
+5  A: 

The shell command [ also known as test needs a space after it for the shell to parse correctly. For example:

if [ "x$choice" == x"$same_name" ] ; then

is equivalent to

if test "x$choice" == "x$same_name" ; then

prepending "x" to the variables is an idiom to prevent test from seeing too few arguments. Test would complain if called as test 5 == so if $choice and $same_name were empty the call to expr is syntactically correct.

You can also use the construct ${choice:-default} or ${choice:=default} to guard against unset or null shell variables.

msw
`[` is the same as `test`, not `expr`. Look at in the synopsis for test(1).
Mikael S
Also note that, in a modern bash shell, if you quote the variables, you don't need to prepend anything to make them nonempty -- `if [ "" == "" ]; then echo foo; fi` works fine. It's only when they're unquoted, and thus expand to nothing at all, that `[` has problems with them.
Brooks Moses
Agree Mikael, oops, fixed. Thanks, Brooks, I'm a child of Bourne's sh(1).
msw
+1  A: 

Bash's double square brackets are much more forgiving of quoting and null or unset variables.

if [[ $choice == "$same_name" ]]; then

You should take a look at Bash's select and case statements:

choices="same_name filesize md5sum different_name exit"

PS3="Make a selection: "    # this is the prompt that the select statement will display
select choice in $choices
do
    case $choice in
        same_name)
            find ...
            ;;
        filesize)
            do_something
            ;;
        .
        .
        .
        exit)
             break
             ;;
    esac
done
Dennis Williamson
+1  A: 

can't believe nobody's picked up this error: if you use [ (or test), the operator for string equality is = not ==.

glenn jackman
Bash's builtin `[` and `test` are happy with both `=` and `==`. But you're right in that `=` is standard.
ephemient
+1  A: 

you can do it like this.

while true
do
cat <<EOF
The list of choices are:
  1) same name
  2) filesize
  3) md5sum
  4) different name
  5) exit
EOF
read -r -p "Enter your choice: " choice
case "$choice" in
 1)
    find /home/user/OSN -type f -exec basename '{}' \; | sort > filelist.txt
    find /home/user/OSN -type f -exec basename '{}' \; | sort | uniq -d > repeatlist.txt
 5) exit;
 *) ls -al /home/user/OSN  >2filelist.txt
esac
done
ghostdog74