Using groovy, how do you pipe multiple shell commands? - bash

Using Groovy and it's java.lang.Process support, how do I pipe multiple shell commands together?
Consider this bash command (and assume your username is foo):
ps aux | grep ' foo' | awk '{print $1}'
This will print out usernames - one line for some processes related to your user account.
Using Groovy, the ProcessGroovyMethods documentation and code says I should be able to do this to achieve the same result:
def p = "ps aux".execute() | "grep ' foo'".execute() | "awk '{print $1}'".execute()
p.waitFor()
println p.text
However, I can't get any text output for anything other than this:
def p = "ps aux".execute()
p.waitFor()
println p.text
As soon as I start piping, the println does not print out any anything.
Thoughts?

This works for me :
def p = 'ps aux'.execute() | 'grep foo'.execute() | ['awk', '{ print $1 }'].execute()
p.waitFor()
println p.text
for an unknown reason, the parameters of awk can't be send with only one string (i don't know why! maybe bash is quoting something differently). If you dump with your command the error stream, you'll see error relative to the compilation of the awk script.
Edit : In fact,
"-string-".execute() delegate to Runtime.getRuntime().exec(-string-)
It's bash job to handle arguments containing spaces with ' or ". Runtime.exec or the OS are not aware of the quotes
Executing "grep ' foo'".execute() execute the command grep, with ' as the first parameters, and foo' as the second one : it's not valid. the same for awk

You can do this to just let the shell sort it out:
// slash string at the end so we don't need to escape ' or $
def p = ['/bin/bash', '-c', /ps aux | grep ' foo' | awk '{print $1}'/].execute()
p.waitFor()
println p.text

This has worked for me
def command = '''
ps aux | grep bash | awk '{print $1}'
'''
def proc = ['bash', '-c', command].execute()
proc.waitFor()
println proc.text
If you want to run multiple commands, you can add it in the command.
def command = '''
ls -ltr
cat secret
'''
def proc = ['bash', '-c', command].execute()
proc.waitFor()
println proc.text

If you want it async I recommend
proc.consumeProcessOutputStream(new LineOrientedOutputStream() {
#Override
protected void processLine(String line) throws IOException {
println line
}
}
);

Related

How can I provide the $INPUT_RECORD_SEPARATOR to ruby -n -e?

I'd like to use Ruby's $INPUT_RECORD_SEPARATOR aka $/ to operate on a tab-separated file.
The input file looks like this (grossly simplified):
a b c
(the values are separated by tabs).
I want to get the following output:
a---
b---
c---
I can easily achieve this by using ruby -e and setting the $INPUT_RECORD_SEPARATOR alias $/:
cat bla.txt | ruby -e '$/ = "\t"; ARGF.each {|line| puts line.chop + "---" }'
This works, but what I'd really like is this:
cat bla.txt | ruby -n -e '$/ = "\t"; puts $_.chop + "---" '
However, this prints:
a b c---
Apparently, it doesn't use the provided separator - presumably because it has already read the first line before the separator was set. I tried to provide it as an environment variable:
cat bla.txt | $/="\n" ruby -n -e 'puts $_.chop + "---" '
but this confuses the shell - it tries to interpret $/ as a command (I also tried escaping the $ with one, two, three or four backslashes, all to no avail).
So how can I combine $/ with ruby -n -e ?
Use the -0 option :
cat bla.txt | ruby -011 -n -e 'puts $_.chop + "---" '
a---
b---
c---
-0[ octal] Sets default record separator ($/) as an octal. Defaults to \0 if octal not specified.
tabs have an ascii code of 9, which in octal is 11. Hence the -011
Use a BEGIN block, which is processed before Ruby begins looping over the lines:
$ echo "foo\tbar\tbaz" | \
> ruby -n -e 'BEGIN { $/ = "\t" }; puts $_.chop + "---"'
foo---
bar---
baz---
Or, more readably:
#!/usr/bin/env ruby -n
BEGIN {
$/ = "\t"
}
puts $_.chop + "---"
Then:
$ chmod u+x script.rb
$ echo "foo\tbar\tbaz" | ./script.rb
foo---
bar---
baz---
If this is more than a one-off script (i.e. other people might use it), it may be worthwhile to make it configurable with an argument or an environment variable, e.g. $/ = ENV['IFS'] || "\t".

How to execute AWK in TCL list with variables?

I need to run a few commands from terminal via a TCL script. This TCL script works fine.
set cmd_list [list {mkdir testdir} {cd testdir} ]
set dataportInterface "eth0"
lappend cmd_list [list ifconfig -a | grep -m 1 $dataportInterface]
However, as soon as I add the awk command, it starts failing.
Error is "Failed : extra characters after close-brace".
lappend cmd_list [list ifconfig -a | grep -m 1 $dataportInterface | awk 'BEGIN {FS="[:]"} { print $1 } END {}' | xargs -t ethtool -S]
I have tried several possibilities, but none of them work. For example, if I try
lappend cmd_list { }
then, $dataportInterface's value eth0 is not taken.
I tried putting a \ in front of all special characters (: " ' [ { and that threw the error $1 not recognized. I put the awk command in {{ brackets and that didn't work either. (error: doesn't recognize {{awk)
What is the correct way to go about this, and why?
Single quotes have no significance to Tcl - try changing '...' to {...}

Shell doesn't respond first input character after executing my program which executes shell command

I had a C program which is trying to do remote command execution using (rsh) command. It is trying to pull IP information and executed command. After executing my program, I was landed in shell prompt. My C program successfully existed (Strace and ps -ef confirmed the same).
When a type any character in a shell now, shell doesn't respond. It works well for next key typing.
I am using bash shell
C program looks something like this.
char *cmdline = "/sbin/ip link show bond0 2>/dev/null && inf=bond0 || inf=eth0;"
"/sbin/ip -6 addr show $inf scope link | /bin/awk '/inet6/ { print \$2 }'";
char *getpeerip = "/usr/bin/rsh $(/sbin/ifconfig eth1 | /bin/awk '/inet/ { print \$2 }'"
"| /bin/awk -F: '{ print \$2 }'"
"| /bin/awk -F. '{ printf \"%%s.%%s.%%s.%d\", \$1, \$2, \$3 }') "
"\"/sbin/ip link show bond0 2>/dev/null && inf=bond0 || inf=eth0;"
"/sbin/ip -6 addr show $inf scope link | /bin/awk '/inet6/ { print \\$2 }'\"";
char lnkbuf[300];
cpid = p_getset->local_addr_ids[i].entity_instance;
if (cpid == getThisCPNum()) {
cmd_file = popen(cmdline, "r");
} else {
sprintf(lnkbuf, getpeerip, 1+getPeerCPSlot());
cmd_file = popen(lnkbuf, "r");
}
if (cmd_file != NULL) {
fgets(lnkbuf, 100, cmd_file);
fclose(cmd_file);
}
The output of "ip link show bond0" over rsh containing escape character causing console input problem.
I changed command like this. It worked.
char *cmdline = "/sbin/ip link show bond0 >/dev/null 2>/dev/null && inf=bond0 || inf=eth0;"
I did the same thing for variable getpeerip.

How do get the full command line in ruby?

How do I get the full command line in ruby?
$ rails c
> $0
=> "script/rails"
> ARGV
[]
> `ps -eo "%p|$|%a" | grep '^\\s*#{Process.pid}'`.strip.split("|$|")[1]
=> "/home/sam/.rvm/rubies/ruby-1.9.3-p194-perf/bin/ruby script/rails console"
Is there anything cleaner than ninja ps I can do to get the same results?
To clarify, in case there is confusion, I want the exact same output as:
`ps -eo "%p|$|%a" | grep '^\\s*#{Process.pid}'`.strip.split("|$|")[1]
ARGV is coming back blank.
$0 is missing the full path.
I'd use:
#!/usr/bin/env ruby
puts "Process ID: #{ $$ }"
puts `ps axw`.split("\n").select{ |ps| ps[ /\A#{ $$ }/ ] }
Running that inside a script outputs:
18222 s000 S+ 0:00.25 /Users/foo/.rbenv/versions/1.9.3-p385/bin/ruby /Users/foo/.rbenv/versions/1.9.3-p385/bin/rdebug /Users/foo/Desktop/test.rb

Ruby: execute bash command, capture output AND dump to screen at the same time

So my problem is that I need to have the output of running the command dumped to the screen and also capture it in a variable in a ruby script. I know that I can do the second part like this:
some_variable = `./some_kickbutt`
But my problem is that I need it to still print to the console as Hudson captures that output and records it for posterity's sake.
thanks in advance for any ideas...
Just tee the stdout stream to stderr like so:
ruby -e 'var = `ls | tee /dev/stderr`; puts "\nFROM RUBY\n\n"; puts var' | nl
ruby -e 'var = `ls | tee /dev/stderr`; puts "\nFROM RUBY\n\n"; puts var' 2>&1 | nl

Resources