LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   variable is gone at the end of while loop (https://www.linuxquestions.org/questions/programming-9/variable-is-gone-at-the-end-of-while-loop-870978/)

ted_chou12 03-25-2011 01:10 PM

variable is gone at the end of while loop
 
Hi, I have a directory file capturing script, the variable is fine with in the loop but gone after the loop is done:
Code:

DIR="/usb/sdb1/media/music/"
i=1
echo "$(ls -lL "${DIR}")" | while read LINE
do
FILENAME=${LINE:56}
ISDIR=${LINE:0:1}
if [ "$ISDIR" = "d" ] ; then
if [ $i == $DIR0 ] ; then
DIR="$DIR$FILENAME/"
fi
i=$(($i+1))
fi
echo $DIR#the variable is new here eg /usb/sdb1/media/music/NEWDIR/
done
echo $DIR#the variable is old here eg /usb/sdb1/media/music/

Why is this happening? is this a bug?
Thanks

szboardstretcher 03-25-2011 01:15 PM

This was addressed in another question:

http://www.linuxquestions.org/questi...e-loop-500481/

Basically, its because you are using a "|" pipe.

ted_chou12 03-25-2011 01:42 PM

Hi, i had a read at the post:
Quote:

Originally Posted by aluser (Post 2500656)
A workaround is to put the entire "stuff | while blah; do halb; done" construct inside backquotes and echo $conflict at the end. i.e.
Code:

conflict=`stuff | ( while blah; do whatever; done; echo $conflict )`
Be sure to echo any errors found inside the subshell to stderr like
Code:

echo >&2 "Oh no Mr. Bill!"
so that they aren't captured by the backquotes

this seems to be what I desired, but how do i go ahead in incooperate it into my own code? since i have extra stuff in front of the while loop?
Thanks
Ted

PTrenholme 03-25-2011 04:57 PM

You could try this (untested) modification:
Code:

DIR="/usb/sdb1/media/music/"
tmpfile=$(mktemp "/tmp/tempXXXX")
ls -lL "${DIR}" > $tmpfile
while read LINE
do
  FILENAME=${LINE:56}
  ISDIR=${LINE:0:1}
  if [ "$ISDIR" = "d" ]
  then
    i=$((++i))
    if [ ${i} == ${DIR0} ]
    then
      DIR="${DIR}${FILENAME}/"
    fi
  fi
  echo ${DIR}  #the variable is new here eg /usb/sdb1/media/music/NEWDIR/
done < ${tmpfile}
rm -f ${tmpfile}
echo ${DIR}    #the variable is old here eg /usb/sdb1/media/music/


grail 03-26-2011 04:25 AM

So I am curious about 2 things:

1. Why not use find to show only directories?

2. You reference a variable called 'DIR0', but after re-reading this several times I cannot see anywhere it being set so your internal 'if' should never work??

Lastly, as opposed to PTrenholme's suggestion of a temp file, which of course works, I would generally go with process substitution in the redirection:
Code:

while read -r LINE
do
    <your stuff>
done< <(find $DIR -type d)


colucix 03-26-2011 05:14 AM

Moved: This thread is more suitable in Programming and has been moved accordingly to help your thread/question get the exposure it deserves.

PTrenholme 03-26-2011 12:19 PM

grail, I don't know which version of bash you're using, but that code failed on my Fedora 14 system. To get it to work, I needed to do this:
Code:

#!/bin/bash
while read x
do
    linenum=$((++linenum))
    echo "$linenum: $x"
done <<< "$(echo -en "hello\nworld\n")"
echo "total number of lines = $linenum"

(Note the quotes around the $(command) to prevent word parsing of the result of the command, and the triple redirection.)

That being said, yours is a simpler solution then the temporary file one, although, in terms of execution cycles, I suspect that they are equivalent.

Oh, FYI:
Code:

$ bash --version
GNU bash, version 4.1.7(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

P.S.: I agree that find might be a better way for the OP to proceed, and, in fact, by using the -exec option he might be able to avoid the need of a bash script altogether. But, as he stated, what he posted was only a fragment from his code. I had assumed that the DIR0 variable was set elsewhere, and that its value was irrelevant to the question he posed. As you can see, I was using the code posted in the thread referenced in the post by szboardstretcher, above for testing. That's why I noted that my modification of his code was "untested."

grail 03-26-2011 01:11 PM

Hey PT ... not sure why it didn't work for you?? You did put a space between the 2 < signs?

Sample of it working for me:
Code:

#!/bin/bash

DIR=$1

while read -r LINE
do
    echo $LINE
done< <(find $DIR -type d)

Output <snipped>:
Code:

$ ./test.sh Documents
Documents/
Documents/My Videos
Documents/My Music
Documents/programming
Documents/programming/bash
Documents/programming/bash/books
Documents/programming/bash/books/with a space
Documents/programming/bash/books/ch
Documents/programming/bash/grail

Bash version:
Code:

$ bash --version
GNU bash, version 4.1.5(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

So it appears the version is lower than yours??

PTrenholme 03-27-2011 11:55 AM

Oops! My bad! :redface:

I (from ingrained habit) used done< <$(cmd) instead of done < <(cmd) since I almost always use the $(cmd) form in scripts. (And I would have used gawk instead of bash for the problem posed by the OP, but that a different topic.)

ted_chou12 03-28-2011 04:57 AM

Thanks


All times are GMT -5. The time now is 06:45 AM.