Creating Variable in Expect Script using Sysname - expect

I'm trying to create an expect script that can automatically back up the config files of our Extreme switches. I need each config file to have a unique name so I can tell them apart and I'd like to use the sysname for this. On Extreme switches the sysname shows on each line in the following format:
ITSwitch.1 #
ITSwitch.2 #
All I need to know is how to capture all characters before the '.' so I can set it as a variable and use that to create the new config files before I upload them to our tftp server. I'm only recently getting into expect scripting but this seems like something easy that I'm just missing. Any help would be much appreciated.

Not as easy as you'd hope. On the remote system, just hit Enter to show a new prompt, and regex-match the prompt to extract the sysname:
send "\r"
expect -re {\r\n([^.]+).* # ?$}
set sysname $expect_out(1,string)
Plan B: assuming there is no whitespace in the sysname:
send "show switch\r"
expect -re {
{Sysname: (\S+)} { set sysname $expect_out(1,string) }
{# ?$}
}

Related

How to deal with shell commands that never stops

Here is the case;
There is this app called "termux" on android which allows me to use a terminal on android, and one of the addons are androids API's like sensors, tts engines, etc.
I wanted to make a script in ruby using this app, specifically this api, but there is a catch:
The script:
require('json')
JSON.parse(%x'termux-sensor -s "BMI160 Gyro" -n 1')
-s = Name or partially the name of the sensor
-n = Count of times the command will run
returns me:
{
"BMI160 Gyroscope" => {
"values" => [
-0.03...,
0.00...,
1.54...
]
}
}
I didn't copied and pasted the values, but that's not the point, the point is that this command takes almost a full second the load, but there is a way to "make it faster"
If I use the argument "-d" and not use "-n", I can specify the time in milliseconds to delay between data being sent in STDOUT, it also takes a full second to load, but when it loads, the delay works like charm
And since I didn't specify a 'n' number of times, it never stops, and there is the problem
How can I retrieve the data continuously in ruby??
I thought about using another thread so it won't stop my program, but how can I tell ruby to return the last X lines of the STDOUT from a command that hasn't and will not ever stop since "%x'command'" in ruby waits for a return?
If I understood you need to connect to stdout from a long running process.
see if this works for your scenario using IO.popen:
# by running this program
# and open another terminal
# and start writing some data into data.txt
# you will see it appearing in this program output
# $ date >> data.txt
io_obj = IO.popen('tail -f ./data.txt')
while !io_obj.eof?
puts io_obj.readline
end
I found out a built in module that saved me called PTY and the spawn#method plus thread management helped me to keep a variable updated with the command values each time the command outputted new bytes

Terraform GCP Instance Metadata Startup Script Issue

I've been working with Terraform, v0.15.4, for a few weeks now, and have gotten to grips with most of the lingo. I'm currently trying to create a cluster of RHEL 7 instances dynamically on GCP, and have, for the most part, got it to run okay.
I'm at the point of deploying an instance with certain metadata passed along to it for use in scripts built into the machine image for configuration thereafter. This metadata is typically just passed via an echo into a text file, which the scripts then pickup as required.
It's... very simple. Echo "STUFF" > file... Alas, I am hitting the same issue OVER AND OVER and it's driving me INSANE. I've Google'd around for ages, but all I can find is examples of the exact thing that I'm doing, the only difference is that theirs works, mine doesn't... So hopefully I can get some help here.
My 'makes it half-way' code is as follows:
resource "google_compute_instance" "GP_Master_Node" {
...
metadata_startup_script = <<-EOF
echo "hello
you" > /test.txt
echo "help
me" > /test2.txt
EOF
Now the instance with this does create successfully, although when I look onto the instance, I get one file called ' /test.txt? ' (or if I 'ls' the file, it shows as ' /test.txt^M ') and no second file.. I can run any command instead of echo, and whilst the first finishes, the second+ does not. Why?? What on earth is causing that??
The following code I found also, but it doesn't work for me at all, with the error, 'Blocks of type "metadata" are not expected here.'
resource "google_compute_instance" "GP_Master_Node" {
...
metadata {
startup-script = "echo test > /test.txt"
}
Okaaaaay! Simple answer for a, in hindsight, silly question (sort of). The file was somehow formmated in DOS, meaning the script required a line continuation character to run correctly (specifically \ at the end of each individual command). Code as follows:
resource "google_compute_instance" "GP_Master_Node" {
...
metadata_startup_script = <<-EOF
echo "hello
you" > /test.txt \
echo "help
me" > /test2.txt \
echo "example1" > /test3.txt \
echo "and so on..." > /final.txt
EOF
However, what also fixed my issue was just 'refreshing' the file (probably a word for this, I don't know). I created a brand new file using touch, 'more'd the original file contents to screen, and then copy pasted them into the new one. On save, it is no longer DOS, as expected, and then when I run terraform the code runs as expected without requiring the line continuation characters at the end of commands.
Thank you to commentors for the help :)

Expect->Telnet->Store and read a variable / Check if directory exists

Summary:
I am writing a script to check if a directory exists on a remote machine. I need a solution which can allow me to check for that directory and return the result in a usable way. This whole process is automated within a much larger script so I need a functional way to tell the parent script the directory exists or not.
Restrictions:
The tools that I have are limited. $REMOTE_2 can only be accessed through $REMOTE_1. Also, $REMOTE_2 can only be connected via telnet (no ssh available).
Current goal:
I am trying to set a local variable to be then read back to chose a return code. I am open to other options, but this is the closest I've come to a working solution so far.
I realize that $found will take from the parent process, but this is not my desired result and am not sure what syntax I need to return true or false when trying to echo the $found variable.
/usr/bin/expect<<EOF
spawn ssh $USER#$REMOTE_1
expect "*$USER*"
send -- "telnet $REMOTE_2\r"
expect "*login:*"
send -- "root\r"
expect "*$*"
# Everything prior to this can't be changed. Everything after it can be.
send "if \[ -d $DIRECTORY_LOCATION \] ; then found=true; else found=false ; fi\r"
send -- "echo **$found**\r"
expect {
"*true*" {
exit 0
}
"*false*" {
exit 1
}
}
EOF
I believe this type of solution can work, but I am not sure how to use the remote variable that I store within the if statement, later on to allow me to choose which return code to use.

reading a bash command result

I used to use the "execute_command" found in the former awesome wiki. This command uses io.popen and the lines method to return the command's result.
Now, the doc's advice is to avoid io.popen.
My rc.lua uses io.popen to assign hostname's computer to a variable ordinateur (I'm trying to maintain a unique rc.lua for two quite different computers).
I used to have this line :
ordinateur=execute_command( "hostname" )
I replace it with :
awful.spawn.easy_async_with_shell( "hostname" ,
function(stdout,stderr,reason,exit_code)
ordinateur = stdout
end)
Further in the script, I have tests like
if ordinateur == "asus" then ....
But it fails. Actually ordinateur is nil
I think the rc.lua is read before ordinateur gets its assignment, right ?
So, what can I do ? I'm thinking replace the command with the reading of the /etc/hostname file, is that better ? How am I going to do this with awful.spawn.* commands ?
Thank you
David
If at all possible, use LuaSocket.
> socket = require "socket"
> print(socket.dns.gethostname())
myhost
Another option is to run hostname from the script that launches the window manager, and store the result in an environment variable. Who knows, if you're lucky, it's already in there?!
> print(os.getenv("HOSTNAME") or os.getenv("HOST"))
myhost
It fails later in the script because the command is asynchronous. This means it Awesome keeps going during the command execution and the result will be available later.
This is the whole point of not using io.popen. io.popen will stop everything [related to X11, including all applications] on your computer while it is being executed.
You need to modify your code so all things that access ordinateur do so after the callback has been called. The easiest way to do so is adding that code in the callback.

Can I get continuous output from system calls in Ruby?

When you use a system call in a Ruby script, you can get the output of that command like this:
output = `ls`
puts output
That's what this question was about.
But is there a way to show the continuous output of a system call? For example, if you run this secure copy command, to get a file from a server over SSH:
scp user#someserver:remoteFile /some/local/folder/
... it shows continuous output with the progress of the download. But this:
output = `scp user#someserver:remoteFile /some/local/folder/`
puts output
... doesn't capture that output.
How can I show the ongoing progress of the download from inside my Ruby script?
Try:
IO.popen("scp -v user#server:remoteFile /local/folder/").each do |fd|
puts(fd.readline)
end
I think you would have better luck using the ruby standard library to handle SCP (as opposed to forking a shell process). The Net::SCP library (as well as the entire Net::* libraries) are full featured and used with Capistrano to handle remote commands.
Checkout http://net-ssh.rubyforge.org/ for a rundown of what is available.
Tokland answered the question as I asked it, but Adam's approach was what I ended up using. Here was my completed script, which does show a running count of bytes downloaded, and also a percentage complete.
require 'rubygems'
require 'net/scp'
puts "Fetching file"
# Establish the SSH session
ssh = Net::SSH.start("IP Address", "username on server", :password => "user's password on server", :port => 12345)
# Use that session to generate an SCP object
scp = ssh.scp
# Download the file and run the code block each time a new chuck of data is received
scp.download!("path/to/file/on/server/fileName", "/Users/me/Desktop/") do |ch, name, received, total|
# Calculate percentage complete and format as a two-digit percentage
percentage = format('%.2f', received.to_f / total.to_f * 100) + '%'
# Print on top of (replace) the same line in the terminal
# - Pad with spaces to make sure nothing remains from the previous output
# - Add a carriage return without a line feed so the line doesn't move down
print "Saving to #{name}: Received #{received} of #{total} bytes" + " (#{percentage}) \r"
# Print the output immediately - don't wait until the buffer fills up
STDOUT.flush
end
puts "Fetch complete!"
have you tried with IO.popen ?
you should be able to read the output while the process is still running and parse it accordingly.
Redirecting stderr to stdout may work for you:
output = `scp user#someserver:remoteFile /some/local/folder/ 2>&1`
puts output
That should capture both stderr and stdout. You can capture stderr only by throwing away stdout:
output = `scp user#someserver:remoteFile /some/local/folder/ 2>&1 >/dev/null`
puts output
You can then use IO.popen.

Resources