Sign in to follow this  
Gamer Gamester

Need help with BASH script Case Loop

Recommended Posts

I'm having a problem and I can't find my error. It's driving me nuts!! Please help me, the details are as follows: I have a file named: sourcelist.tmp Its contents are a list of files I want to do stuff to, separated by spaces (no newlines). Below is an example of this file (The list is automatically generated so the directory trees look a little weird, but they're fine):
../Sources/Font/Test/../Letter.cpp ../Sources/Font/Test/../../Fileable/Fileable.cpp
And then I have a BASH script that processes these files. However, I'm having trouble with a case loop in it. Below is an excerpt of the trouble section:
# This variable indicates the files that have been processed so far
SOURCES="../Sources/Font/Test/LetterTest.cpp"
# This variable will be 1 until all files in sourcelist.tmp have been processed
LOOP=1
# This loop repeats until LOOP is null (once all files have been processed)
while [ ${LOOP} ]; do   # Go through each file...
  for SOURCE in `cat sourcelist.tmp`; do
    # The following commented lines are not part of the script, but I use
    # them to test that the case loop is finding SOURCE when it is in SOURCES
    # and selecting that case, which it is not (my current problem):
      # SOURCES="$${SOURCES}|$${SOURCE}"
      # echo $${SOURCE};
      # echo $${SOURCES};
     case ${SOURCE} in
      # If the file is listed in SOURCES, then it has already been processed,
      # so LOOP is set to null and we check the next file...  or end the while
      # loop if this is the last file  (*** THIS NEVER HAPPENS!!!  WHY??? ***)
      (${SOURCES}) LOOP="";;
      # File is not in SOURCES so we add it to SOURCES...
      (*) SOURCES="$${SOURCES}|$${SOURCE}"
          # and process it...
          if [ -e $${SOURCE} ]; then
            # I do my stuff to file and sourcelist.tmp here
          fi
          # Make sure LOOP is 1 as we must continue while loop
          LOOP=1
          # Start new iteration of while loop (we want to start the for
          # loop over since sourelist.tmp may have changed)
          continue 2;;
    esac
  done
done
If you help me you will be my official hero of the week! Thanks, Beau

Share this post


Link to post
Share on other sites
Quote:
Original post by BeauMN
I'm having a problem and I can't find my error. It's driving me nuts!!


(1) Make sure there is a space before the ;; case terminator otherwise it may be missed by the parser.

(2) lose the left paren in your pattern matches otherwise it's part of the match.

case $blah in
$SOURCES)
echo "matched $SOURCES"
;;
(*)
echo "a line beginning with a left parent has matched!"
;;
*)
echo "default match"
;;
esac

(3) I'm not completely sure you can use variable substitution in a case statement like that.

Use set -x to turn on debugging so you can see what's going on.

--smw

Share this post


Link to post
Share on other sites
I tried your suggestions but am still having the same problem. What happens is the wildcard case is selected everytime, the first case is always ignored (Even though I KNOW they match, assuming the variable substitution is working properly).

The debugging info was not detailed enough to help, it told nothing of what was happening inside the case statement, only the results. I'm going to try and see if there's a way to make it more verbose.

Share this post


Link to post
Share on other sites
Nope, the PATTERN in a case statement is evaluated when the case statement is evaluated. That means you can't change the PATTERN in subsequent iterations of your loop. You can't even stick it in a function and call that. You could, if you're feeling creative, stick it in a here document and eval it.

To tell you the truth, you're just better off using the tried-and-true way of processing lists from shell scripts. For example,

for source in $(fold -s sourcelist.tmp | uniq); do
if [ -e "$source" ]; then
echo "processing $source..."
fi
done

Wouldn't that be easier?

Share this post


Link to post
Share on other sites
Thanks! I think I'm finally starting to understand now. Haven't done much shell scripting and I'm realizing I might be treating it a bit too much like other languages such as C.

Just to make sure I have a grasp on this, can somebody confirm my understanding?

So in BASH code such as this...


for SOURCE in `cat sourcelist.tmp`; do
case ${SOURCE} in
match1) #do this stuff ;;
match2) #do other stuff ;;
esac
done


${SOURCE} will never be defined in the case statement because the case statement is evaluated before the for loop is run? But on the other hand, if you have an if statement inside the for loop, the if statement will get reevaluated on each iteration of that for loop?

I've been searching through the BASH reference, but I haven't been able to find info on the order of evaluation in nested constructs such as these. How do we know precisely when something will be evaluated?

Perhaps I just need a better understanding of how the shell goes about processing its scripts?

Thanks again

Share this post


Link to post
Share on other sites
Quote:
Original post by BeauMN
${SOURCE} will never be defined in the case statement because the case statement is evaluated before the for loop is run? But on the other hand, if you have an if statement inside the for loop, the if statement will get reevaluated on each iteration of that for loop?


After further investigation I figured out what the problem is.

As the bash documentation describes, variable substitution takes place when the case statement is evaluated. In other words, both ${SOURCE} and ${SOURCES} are expanded as you expect.

Problem is, this is done at evaluation time, not at parse time. The parser will tokenize the match pattern into the various alternatives before the variable substitution takes place.

Consider a construct like this.

matchme="a | b"
case a in
$matchme)
echo "never gets here"
;;
*)
echo "always gets here"
;;
esac

There will be two patterns to match against: "a | b" and "*". Note the first pattern is not the same as "a" | "b" but instead consists of a 5-character token.

Unfortunately, try as I might, I seem unable to work around the dynamic construction of alternate cases. I stand by my previous assertion that you just need to come up with a simpler way of traversing your list.

--smw

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this