How do I get the output of an external command and extract values from it?
I have something like this:
stdin, stdout, stderr, wait_thr = Open3.popen3("#{path}/foobar", configfile)
if /exit 0/ =~ wait_thr.value.to_s
runlog.puts("Foobar exited normally.\n")
puts "Test completed."
someoutputvalue = stdout.read("TX.*\s+(\d+)\s+")
puts "Output value: " + someoutputvalue
end
I'm not using the right method on stdout since Ruby tells me it can't convert String into Integer.
So for instance, if the output is
"TX So and so: 28"
I would like to get only "28". I validated that the regex above matches what I need to match, I'm only wondering how to store that extracted value in a variable.
What is the right way of doing this? I can't find anywhere in the documentation the methods available for stdout. I'm using stout.read from Ruby 1.9.3.
All the information needed is in the Popen3 documentation, but you have to read it all and look at the examples pretty carefully. You can also glean useful information from the Process docs too.
Maybe this will 'splain it better:
require 'open3'
captured_stdout = ''
captured_stderr = ''
exit_status = Open3.popen3(ENV, 'date') {|stdin, stdout, stderr, wait_thr|
pid = wait_thr.pid # pid of the started process.
stdin.close
captured_stdout = stdout.read
captured_stderr = stderr.read
wait_thr.value # Process::Status object returned.
}
puts "STDOUT: " + captured_stdout
puts "STDERR: " + captured_stderr
puts "EXIT STATUS: " + (exit_status.success? ? 'succeeded' : 'failed')
Running that outputs:
STDOUT: Wed Jun 12 07:07:12 MST 2013
STDERR:
EXIT STATUS: succeeded
Things to note:
You often have to close the stdin stream. If the called application expects input on STDIN it will hang until it sees the stream close, then will continue its processing.
stdin, stdout, stderr are IO handles, so you have to read the IO class documentation to find out what methods are available.
You have to output to stdin using puts, print or write, and read or gets from stdout and stderr.
exit_status isn't a string, it's an instance of the Process::Status class. You can mess with trying to parse from its to_s version, but don't. Instead use the accessors to see what it returned.
I passed in the ENV hash, so the child program had access to the entire environment the parent saw. It's not necessary to do that; Instead you can create a reduced environment for the child if you don't want it to have access to everything, or you can mess with its view of the environment by changing values.
The code stdout.read("TX.*\s+(\d+)\s+") posted in the question is, um... nonsense. I have no idea where you got that as nothing like that is documented in Ruby's IO class for IO#read or IO.read.
It's easier to use capture3 if you don't need to write to STDIN of the called code:
require 'open3'
stdout, stderr, exit_status = Open3.capture3('date')
puts "STDOUT: " + stdout
puts "STDERR: " + stderr
puts "EXIT STATUS: " + (exit_status.success? ? 'succeeded' : 'failed')
Which outputs:
STDOUT: Wed Jun 12 07:23:23 MST 2013
STDERR:
EXIT STATUS: succeeded
Extracting a value from a string using a regular expression is trivial, and well covered by the Regexp documentation. Starting from the last code example:
stdout[/^\w+ (\w+ \d+) .+ (\d+)$/]
puts "Today is: " + [$1, $2].join(' ')
Which outputs:
Today is: Jun 12 2013
That's using the String.[] method which is extremely flexible.
An alternate is using "named captures":
/^\w+ (?<mon_day>\w+ \d+) .+ (?<year>\d+)$/ =~ stdout
puts "Today is: #{ mon_day } #{ year }"
which outputs the same thing. The downside to named captures is they're slower for what I consider a minor bit of convenience.
"TX So and so: 28"[/\d+$/]
=> "28"
Related
I made this function who change the title of a terminal window by using ainsi escape sequence but after the call of this function a line is jumped in console, how avoid this ?
void setConsoleTitle(std::string const& title)
{
m_title = title;
std::string cmd1 = "echo \"\033]0;";
cmd1 += title;
cmd1 += "\007\"";
system(cmd1.c_str());
}
Thanks.
ReallY DON'T use system here (it starts a new process, in which a shell is run, and then runs echo in that shell, and tears down that new process - which is a lot of work to output a handful of characters to the screen, that can just as well be output with cout or similar - system is acceptable to use if you, say, need to run a compiler or unpack a zip file - something that isn't easy to do in your own program). But if you insist on using system, then use echo -n ..., where man echo explains it as:
-n do not output the trailing newline
However, just using cout will do fine here:
cout << "\033]0;" << title << "\a";
(\a is "alarm", the same as \007 but portable in case your system doesn't use character number 7 for "ring the bell")
Why you want to print "noise" is beyond me, but the above does the same thing as your "echo" command.
I'm building a tool were a command with a password needs to be entered.
I want when I enter this command with the password, the command line replaces each character with "*" or " ", so the command and the password will not be observable !
is there such a command that tells the TCL interpreter "from this point, show each character entered as *", and then switch back to regular mode ?
any other suggestion will be valuable too.
In your case, you shall take "full control" over your terminal and disable its default echoing behavior (In UNIX the likes the terminal should be entered into the so-called raw mode)
Then, you can read the characters one-by-one (till max password size or till Enter is pressed) and echo '*' per each pressed character.
You got working code examples both on UNIX and Windows how doing so here
You may want reading also this link echo-free password entry TCL wiki
proc enableRaw {{channel stdin}} {
exec /bin/stty raw -echo <#$channel
}
proc disableRaw {{channel stdin}} {
exec /bin/stty -raw echo <#$channel
}
enableRaw
set c [read stdin 1]
puts -nonewline $c
disableRaw
package require twapi
proc enableRaw {{channel stdin}} {
set console_handle [twapi::GetStdHandle -10]
set oldmode [twapi::GetConsoleMode $console_handle]
set newmode [expr {$oldmode & ~6}] ;# Turn off the echo and line-editing bits
twapi::SetConsoleMode $console_handle $newmode
}
proc disableRaw {{channel stdin}} {
set console_handle [twapi::GetStdHandle -10]
set oldmode [twapi::GetConsoleMode $console_handle]
set newmode [expr {$oldmode | 6}] ;# Turn on the echo and line-editing bits
twapi::SetConsoleMode $console_handle $newmode
}
enableRaw
set c [read stdin 1]
puts -nonewline $c
disableRaw
(Assuming Linux.) By far the easiest way to handle passwords in a terminal is to turn off echoing of input but leave the terminal otherwise in cooked mode. It won't show a * for each entered character, but it does mean that you don't have to handle things like backspace (when a user realises they typed the last couple of characters wrong before hitting Return), etc.
exec /bin/stty -echo <#stdin
set password [gets stdin]
puts ""
exec /bin/stty echo <#stdin
If you've got Tcl 8.6, you can easily make this more robust with this procedure:
proc getPassword {{prompt "Password: "}} {
exec /bin/stty -echo <#stdin
try {
puts -nonewline $prompt
flush stdout
return [gets stdin]
} finally {
puts ""
flush stdout
exec /bin/stty echo <#stdin
}
}
(It's possible to use catch and some scripting to emulate try…finally but it's really annoying.)
If you have a GUI and prefer that, you make a password entry box by setting the -show option to something non-empty (e.g., * to show an asterisk).
Friends , im trying to automate a routing using expect , basically its a debug plugin in a special equipment that i need to log some data , to access this debug plugin my company needs to give me a responsekey based on a challengekey , its a lot of hosts and i need to deliver this by friday , what i've done so far.
#!/usr/bin/expect -f
match_max 10000
set f [open "cimc.txt"]
set hosts [split [read $f] "\n"]
close $f
foreach host $hosts {
spawn ssh ucs-local\\marcos#10.2.8.2
expect "Password: "
send "Temp1234\r"
expect "# "
send "connect cimc $host\r"
expect "# "
send "load debug plugin\r"
expect "ResponseKey#>"
sleep 2
set buffer $expect_out(buffer)
set fid [open output.txt w]
puts $fid $buffer
close $fid
sleep 10
spawn ./find-chag.sh
sleep 2
set b [open "key.txt"]
set challenge [read $b]
close $b
spawn ./find-rep.sh $challenge
sleep 3
set c [open "rep.txt"]
set response [read $c]
close $c
puts Response-IS
send "\r"
expect "ResponseKey#> "
send "$response"
}
$ cat find-chag.sh
cat output.txt | awk 'match($0,"ChallengeKey"){print substr($0,RSTART+15,38)}' > key.txt
$ cat find-rep.sh
curl bla-blabla.com/CIMC-key/generate?key=$1 | grep ResponseAuth | awk 'match($0,"</td><td>"){print substr($0,RSTART+9,35)}' > rep.txt
i dont know how to work with regexp on expect so i put the buffer output to a file and used bash script , the problem is that after i run the scripts with spawn looks like my ssh session is lost , does anyone have any tips? should i use something else instead of spawn to invoke my scripts?
expect -re "my tcl compatible regular expression goes here"
Should allow you to use regular expressions.
What I'm trying to do is write an expect script that will allow me to search for file A, and if file A is found then send a series of commands. If files B, or C are found then continue on through the script. I keep getting stuck on the regular expression. I run the expression through www.myregextester.com alone with the data I'm searching for and it matches up just fine. If anyone has any experience with this I would really appreciate the help.
send "term length 0\r"
expect "*#"
send "wr mem\r"
expect "*#"
send "dir flash:\r"
expect
if [[ \bc3750-ipservicesk9-mz.122-55.SE7.bin\b ]]; then {
send "conf t\r"
expect "*(config)#"
send "boot system flash:c3750-ipservicesk9-mz.122-55.SE7.bin\r"
expect "*(config)#"
send "exit\r"
expect "*#"
send "reload at 02:00 8 June\r"
expect "System configuration has been modified. Save? [yes/no]:"
send "yes\r"
expect "Proceed with reload? [confirm]"
send "\r"
expect "*#"
send "wr\r"
expect "*#"
send "exit\r
expect eof
elif [[ \bc3750-ipservicesk9-mz.122-55.SE5\b | b\c3750-ipservicesk9-mz.122-55.SE5.bin\b} ]]; then
fi
}
expect {
-re {\mc3750-ipservicesk9-mz\.122-55\.SE7\.bin\M} {
send "conf t\r"
#...
}
-re {\mc3750-ipservicesk9-mz\.122-55\.SE5(?:\.bin)?\M}
}
Expect is an extension of Tcl: you can read about Tcl regular expressions here.
Hey, my Qt C++ program has a part where it needs to send the first 128 characters or so of the output of a bash command to an email address. The output from the tty is captured in a text box in my gui called textEdit_displayOutput and put into my message I built using the Message Builder ( the object m_vmMessage ) Here is the relevant code snippet:
m_vmMessage.getTextPart()->setCharset( vmime::charsets::US_ASCII );
m_vmMessage.getTextPart()->setText( vmime::create < vmime::stringContentHandler > ( ui->textEdit_displayOutput->toPlainText().toStdString() ) );
vmime::ref < vmime::message > msg = m_vmMessage.construct();
vmime::utility::outputStreamAdapter out( std::cout );
msg->generate( out );
Giving bash 'ls /' and a newline makes vmime give terminal output like this:
ls /=0Abin etc=09 initrd.img.old mnt=09 sbin=09 tmp=09 vmlinuz.o=
ld=0Aboot farts=09 lib=09=09 opt=09 selinux usr=0Acdrom home=09 =
lost+found=09 proc srv=09 var=0Adev initrd.img media=09 root =
Whereas it should look more like this:
ls /
bin etc initrd.img.old mnt sbin tmp vmlinuz.old
boot farts lib opt selinux usr
cdrom home lost+found proc srv var
dev initrd.img media root sys vmlinuz
18:22>
Output seems to be truncated around 'root', nothing after it is displayed.
How do I encode and piece together the email properly? Does vmime just display it like that on purpose and the actual content of the email is complete and properly formatted?
Thanks!
=0A is a line feed (LF) character.
=09 is a horizontal tab (HT).
I think this is just MIME's way of encoding your non-printing (control) characters.