Shell command with envvars - howto?

(Wybo Dekker) #1

I want to make a system calling method that returns output, success rate, and exit status of a system command, like the sys method in the example program below. My system call fails, however, when I put a definition of an environment variable in front of the system command.

It works only if I put &&'s between the envvar definition(s) and the command, as illustrated in the program. But that means that I'll have to interpret the argument of my method to insert those &&'s.

Is there a better way?

#!/usr/bin/env ruby

# return output, success rate, and exit status of a system command
def sys(command)
   [%x{#{command} 2>&1}, $?.success?, $?.exitstatus]
end

def pr(command,output,success,status)
   puts "command: #{command}"
   puts " output: #{output}"
   puts "success: #{success ? 'succeeded' : 'failed'}"
   puts " status: #{status}",''
end

command = "echo hello"; pr(command,*sys(command))
command = "xxxx hello"; pr(command,*sys(command))
command = "x=hello echo $x"; pr(command,*sys(command)) # FAILS!
command = "x=hello && echo $x"; pr(command,*sys(command))

Outputs:

command: echo hello
  output: hello
success: succeeded
  status: 0

command: xxxx hello
  output: sh: xxxx: command not found
success: failed
  status: 127

command: x=hello echo $x
  output:
success: succeeded
  status: 0

command: x=hello && echo $x
  output: hello
success: succeeded
  status: 0

···

--
Wybo

(Reyn Vlietstra) #2

Hi,
We havent implemented && or exit status yet(shouldnt be too hard using
open4), but have a look at ruSH.

You can pipe commands to each other.

http://reyn.co.za/rushtest2.png
http://rubyforge.org/projects/rush/

···

On 8/30/05, Wybo Dekker <wybo@servalys.nl> wrote:

I want to make a system calling method that returns output, success rate,
and exit status of a system command, like the sys method in the example
program below. My system call fails, however, when I put a definition of
an
environment variable in front of the system command.

It works only if I put &&'s between the envvar definition(s) and the
command, as illustrated in the program. But that means that I'll have to
interpret the argument of my method to insert those &&'s.

Is there a better way?

#!/usr/bin/env ruby

# return output, success rate, and exit status of a system command
def sys(command)
[%x{#{command} 2>&1}, $?.success?, $?.exitstatus]
end

def pr(command,output,success,status)
puts "command: #{command}"
puts " output: #{output}"
puts "success: #{success ? 'succeeded' : 'failed'}"
puts " status: #{status}",''
end

command = "echo hello"; pr(command,*sys(command))
command = "xxxx hello"; pr(command,*sys(command))
command = "x=hello echo $x"; pr(command,*sys(command)) # FAILS!
command = "x=hello && echo $x"; pr(command,*sys(command))

Outputs:

command: echo hello
output: hello
success: succeeded
status: 0

command: xxxx hello
output: sh: xxxx: command not found
success: failed
status: 127

command: x=hello echo $x
output:
success: succeeded
status: 0

command: x=hello && echo $x
output: hello
success: succeeded
status: 0

--
Wybo

--
Reyn Vlietstra

(Ara.T.Howard) #3

this doesn't even work from the shell:

     harp:~ > x=hello echo $x
     (newline)

this is because parameter expansion (of $x) occurs __before__ the x=hello is
set for the local environment. you can see this by doing:

     harp:~ > x=hello env|egrep ^x=
     x=hello

so here the 'x' is looked for __after__ local env vars are set. this is
detailed here:

     http://www.gnu.org/software/bash/manual/bashref.html#SEC48

alternatives:

   using session:

     harp:~ > cat a.rb
     require 'session'

     sh = Session::new

     sh.execute 'x=hello'
     stdout, stderr = sh.execute 'echo $x'

     p stdout
     p sh.status

     harp:~ > ruby a.rb
     "hello\n"
     0

   another way:

     def sys cmd, env = {}
       merge_env(env){ [ %x[{ #{ cmd } ;} 2>&1], $?.success?, $?.exitstatus ]}
     end
     def merge_env new_env, &block
       push_env(ENV.to_hash.merge(new_env), &block)
     end
     def push_env new_env, &block
       @env_stack ||= []
       cur_env = ENV.to_hash
       @env_stack << cur_env
       set_env new_env
       if block
         begin
           block[new_env]
         ensure
           pop_env
         end
       else
         cur_env
       end
     end
     def pop_env
       @env_stack ||= []
       old_env = @env_stack.pop
       if old_env
         set_env old_env
       else
         old_env
       end
     end
     def set_env env
       ENV.clear
       env.each{|k,v| ENV["#{ k }"] = "#{ v }"}
       ENV.to_hash
     end

use like

   sys 'foobar', 'PATH' => './bin', 'LD_LIBRARY_PATH' => './lib'

hth.

-a

···

On Tue, 30 Aug 2005, Wybo Dekker wrote:

command = "x=hello echo $x"; pr(command,*sys(command)) # FAILS!

--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
Your life dwells amoung the causes of death
Like a lamp standing in a strong breeze. --Nagarjuna

===============================================================================

(Patrick Gundlach) #4

Hello Wybo,

I am not sure if this might break anything, but try inserting a ';'
after your assignment:

command = "x=hello ; echo $x"; pr(command,*sys(command))

Patrick

(Wybo Dekker) #5

Ara.T.Howard wrote:

···

On Tue, 30 Aug 2005, Wybo Dekker wrote:

command = "x=hello echo $x"; pr(command,*sys(command)) # FAILS!

this doesn't even work from the shell:

     harp:~ > x=hello echo $x
     (newline)

You're quite right - I have been totally confused and made the wrong test!
Thanks for helping me out. Thanks also for pointing me to session.rb, which allows me to get output to stdout and stderr separately.

--
Wybo