sagat has asked for the wisdom of the Perl Monks concerning the following question:

Hey Gurus: Im having a little trouble with some Perl I wrote. Here is a sanitized snippet.
sub checkdb { foreach $_ (@_) { .. ... # snipped code where we munge around the db and get our ... # $variable if ($variable < $threshold) { $command = '/usr/bin/blah'; $UPDATE = `$command << EOF $variable is the value from DB. $variable is less that $threshold EOF`; } else { } } }
This sub goes thru a list, checks the elements of the list in a database for its value, then returns $variable, which is a integer. Then I run $command, which expects some string followed by a carriage return. We SHOULD end up with something like this in the $command log file.
"23 is the value from the DB. 23 is less than 25"
Instead I get
"23 is the value from the DB. 29 is less than 25"
Im stymied how the $variable got changed while inside of the ``. $variable IS a global variable, but that shouldnt be a problem, since it should get reassigned for ever iteration of foreach.... right? Or does the perl code continue to roll on, even before the ``'s get to return?
TIA!
-chris

Replies are listed 'Best First'.
Re: A problem with variables inside a system() call
by davido (Cardinal) on May 13, 2004 at 22:03 UTC
    You definately have a problem with your HERE doc syntax. Your quoting is screwed up, there's no terminating semicolon on the first line of the HERE, and you shouldn't have a close-quote after the ending token. Furthermore, the closing token should be alone on its line (the semicolon screws it up). I'd be surprised if this code compiles. Are these just typos in typing your question? Also, it's not clear to me exactly, but it looks like you're trying to create the following system command:

    /usr/bin/blah $variable is the value from DB. $variable is less than $threshold

    So you've got embeded newlines in your invocation of the command, and unquoted barewords. What exactly do you want your command to look like when it gets passed to the operating system?


    Dave

      The here document is parsed by the shell, not Perl. It's interesting; I haven't seen this before, and I'm surprised that it works this way. For example, if I put my code from 353223 into /tmp/t13 and run strace -e execve -f perl /tmp/t13 I get:
      ... [pid 5261] execve("/bin/sh", ["sh", "-c", "/bin/cat << EOF\n24 is the + value "...], [/* 37 vars */]) = 0 ...
Re: A problem with variables inside a system() call
by jarich (Curate) on May 14, 2004 at 01:51 UTC
    Do you get the same kind of error if you rewrite your statement?
    if($variable < $threshold) { my $command = '/usr/bin/blah'; my $updatestring = << "EOF"; $variable is the value from DB. $variable is less that $threshold EOF my $UPDATE = `$command $updatestring`; } else { ... }
    Using this extra variable corrects some issues with your here document and should make the code much more readable.

    Does it also fix the error?

    I would be intersted in knowing how it's going wrong in the first place... do you have a code example that reproduces the error reliably?

    One final thing. In some responses above you suggest you will use system instead. I'm sure you know the difference between using backticks and system, but I thought I should point it out anyway.

    system returns $? which you can use to determine success or failure (basically if it's true your command failed). If the command called via system prints to STDOUT then that content will go to the STDOUT of your program. Backticks capture the STDOUT from the command and return that. If you call backticks in a list context each new line of data is returned in each list element. If you call it in scalar context you get a string with embedded newlines.

    If you want to give up on backticks but still capture the output of the command then perhaps you might want to use:

    open ( COMMAND, " $command $updatestring | ") or die $!; while(<COMMAND>) { .... }

    I hope this helps

    jarich

Re: A problem with variables inside a system() call
by sgifford (Prior) on May 13, 2004 at 22:08 UTC

    No, perl should wait for the command to finish before it returns, and even if it didn't should build the entire shell command before executing it. Is this a multithreaded program? Can you give a snippet that actually reproduces the problem? I tried something simple like this:

    $threshold=25; foreach $variable (23..30) { if ($variable < $threshold) { $command = '/bin/cat'; $UPDATE = `$command << EOF $variable is the value from DB. $variable is less than $threshold EOF`; print "Update: '$UPDATE'\n"; } }
    and it worked as expected.
      Hi:

      Ok, Im beginning to think this might be a problem with the command itself. I just added a print " "; around the $UPDATE section to the EOF piece, and the output looks exactly as it should.

      The program should be singlethreaded, although the perl itself was compiled as multithreaded.

      BTW, the $command was actually a propriatary sql interface to some strange DB.

      BTW, I was looking at the thread of answers behind my question and Davido was refering to a "HERE doc." I've never heard of that before....
      -chris

        << introduces a here document. The syntax and name come from the shell; < filename means get the input from a filename, and << means get the input from right here.

        You can find more in the perldata(1) manpage, or the manual for your shell.

Re: A problem with variables inside a system() call
by zude (Scribe) on May 13, 2004 at 23:27 UTC
    If program blah really wants input on STDIN then it may be instructive for you to change your $command to "/bin/cat".

    OTOH, if blah actually wants those strings on the command line you need to make a change:

    $UPDATE = `$command << EOF`; $variable is the value from DB. $variable is less that $threshold EOF
    In this case you would test by changing $command to "/bin/echo".