tags:

views:

94

answers:

4

I have a python script called mythicalPythonBindings.py. It outputs something like

Establishing database connection
chanid = 1952
starttime = 2010-09-29 08:12:00
endtime = 2010-09-29 08:30:00
basename = 1952_20100929081200.mpg
stars = 0.0

I need to read this information into variables of the same name in a bash script. My bash script looks like this

#! /bin/bash

/usr/local/bin/mythicalPythonBindings.py --filename='1952_20100929081200.mpg' --DBHostName='192.168.1.110' --DBName='mythconverg' --DBUserName='mythtv' --DBPassword='mythtv' --output=./ReturnFile.txt --display|
   while read line
     do 
     case "$line" in
         "basename = "* ) 
              echo $line
              basename=`echo "$line" |sed s/'basename = '//g`
              echo $basename
         ;;
     esac
   done
echo $basename
#doing more stuff 

The problem is that when setting variables inside the piped output from mythicalPythonBindings.py, it does not export the variables. The statement "echo $basename"(inside the piped output) works, but the seccond "echo $basename"(outside the piped output) does not work.

How can I set these variables for use in the rest of the script?

edit: I need this variable to be used in other commands without saving the data to a file which must be deleted later.

A: 
#!/bin/sh
./testpyscri.sh | (
   while read line
     do 
     case "$line" in
         "basename = "* ) 
    echo $line
              basename=`echo "$line" |sed s/'basename = '//g`
                echo $basename
         ;;
     esac
   done
   echo and also $basename
)

I made a couple of changes to easily reproduce and demo the result, but the only important change was the addition of the single group of parens.

so ross$ ./testpyscri.sh  | ./pyscri.sh 
basename = 1952_20100929081200.mpg
1952_20100929081200.mpg
and also 1952_20100929081200.mpg

Update:

It should be easy to understand the above design pattern which can in fact execute an arbitrarily complex script on either end of the pipeline. But perhaps you would like an different sort of design. Try something like this:

so ross$ expand < test.sh
#!/bin/sh
# here is an alternate design pattern
if [ x$1 != x-a ]; then
  echo abc | ./$0 -a
  exit 0
fi
read x
echo $x
so ross$ ./test.sh
abc
so ross$ 
DigitalRoss
That does not work. How can the variable be exported? I have alot of things to do with it. All the ( ) does is extend the current statement. I need the variable accessible in the rest of the bash script.
Adam Outler
By using the () you've only extended the same command.
Adam Outler
this is going to be a small portion of mythicalLibrarian http://code.google.com/p/mythicallibrarian/updates/list
Adam Outler
It's not likely that the variable needs to be exported. People do that unnecessarily all the time. That makes the value available to child processes - *not* parent processes. You need it outside the subshell that the pipe into while creates and outside an explicit subshell created by parentheses. The only way to do this is to not create a subshell.
Dennis Williamson
It does work. Both the original answer and the updated alternative solve the problem, work in any Posix shell, and they have both been tested.
DigitalRoss
Adam Outler
A: 

Two key changes:

#! /bin/bash
/usr/local/bin/mythicalPythonBindings.py --filename='1952_20100929081200.mpg' \
    --DBHostName='192.168.1.110' --DBName='mythconverg' --DBUserName='mythtv' \
    --DBPassword='mythtv' --output=./ReturnFile.txt --display |
{
while read var equals value
do
    if [ $equals = "=" ]
    then
        eval $var=\"$value\"
    fi
done

echo basename=$basename
echo starttime=$starttime
echo endtime=$endtime
echo stars=$stars
echo chanid=$chanid
# ...rest of script...
}
  1. Use the shell's built-in splitting facility, plus eval to set the variable without needing to know what the variable is (and without a separate assignment for each different variable). The test ignores the first line of the output.
  2. Use the I/O grouping facility of the { ... } to capture the rest of the script so that the variables set in the loop are available after the loop.

You could use parentheses instead of the braces; in this case, it amounts to the same thing. In general, the I/O grouping does not start a sub-shell, which can be useful to avoid repeated redirects while executing a block of code, while still allowing variables set in the block of code to be seen outside the block.

Jonathan Leffler
Hi JL, sorry about the bad comment formatting, I guess you just need to quote the definition part of the assignment, `+ read var equals value<br>+ eval 'starttime=2010-09-29 08:12:00'<br>++ starttime=2010-09-29<br>++ 08:12:00<br>py2.sh: line 6: 08:12:00: command not found<br>`
DigitalRoss
well, darn, i haven't been around much since i got a j-j-j-j-job last December but I do seem to recall that you just can't cram a newline into a comment...ahh, OK, you can make this work by just doing: `eval $var=\"$value\"`
DigitalRoss
Thanks, @DigitalRoss; I should have known better than to post untested code - `eval` is tricky but powerful shell magic.
Jonathan Leffler
+2  A: 

The while loop creates a subshell when something is piped into it. To avoid that you can redirect process substitution into the done:

pythonfunc () {
    /usr/local/bin/mythicalPythonBindings.py --filename='1952_20100929081200.mpg' --DBHostName='192.168.1.110' --DBName='mythconverg' --DBUserName='mythtv' --DBPassword='mythtv' --output=./ReturnFile.txt --display
}

while ...
do
    ...
done < <(pythonfunc)
echo $basename

I would use

read var equals value
# do some validation
declare $var=$value

Using declare in this way allows you to avoid using eval.

Dennis Williamson
WOW!!! Thank you! it worked very well! I've been wondering how to do this for a long time. This is the first time I needed it. Thank you, thank you. May 1 million virgins await you in heaven.
Adam Outler
I suppose the question *was* tagged bash but for the record this will not work in dash or any other Posix shell besides bash. If have noted that you are writing a large script: the posix shells run much faster than bash.
DigitalRoss
@DigitalRoss: One could use named pipes.
Dennis Williamson
@Jonathan Leffler: That redirection is not superfluous. `<` is the redirection, `<()` is Bash's process substitution.
Dennis Williamson
The script has been made POSIX compliant. It runs on Mac. It would be neat if I could figure out how to use sh instead of bash.
Adam Outler
@Adam: you can use named pipes. See [BashFAQ/024](http://mywiki.wooledge.org/BashFAQ/024).
Dennis Williamson
A: 

Here's another version using eval It assumes:

  1. Python outputs variables and values separated by =.
  2. Uses Awk with -F '=' to break down inputs into variable and value
  3. Removes leading and trailing white-space characters on variables and values using Sed.

Bash code:

#!/bin/bash

while read line
do
    var=$(echo $line | awk -F '=' '{print $1}' | sed 's/^[ \t]*//;s/[ \t]*$//')
    val=$(echo $line | awk -F '=' '{print $2}' | sed 's/^[ \t]*//;s/[ \t]*$//')

    eval $var="$val"
    echo $python_var
done

Examples usage:

>> echo "python_var = 1234" | ./script.sh 
>> 1234
Babil