Bash using cut to separate an IP and Port - bash

I am trying to pass an ip and port to my bash script from a list of devices but the script is reading it as multiple devices instead of port. So in the example from below it's trying to telnet to 4 devices as it's reading the ports as a device.
for device in `cat device-list.txt`;
do
hostname=$(echo $device | cut -d : -f 1)
port=$(echo $port | cut -d : -f 2)
./script.exp $device $username $password $port ;
done
I am trying to use cut to take the port and pass it through as a variable so my telnet should be e.g. abc.abc.com 30040 as one device and so on.
# Telnet
spawn telnet $hostname $port
This is my list of devices
abc.abc.com 30040
abc.abc.com 30041
I have tried searching this site already for answers.

I see two errors (lines 4 & 5). It should be
for device in `cat device-list.txt`;
do
hostname=$(echo $device | cut -d : -f 1)
port=$(echo $device | cut -d : -f 2)
./script.exp $hostname $username $password $port ;
done

You can use the Bash built-in read function to extract hostname and port from the lines in a loop:
while read -r hostname port || [[ -n $hostname ]] ; do
./script.exp "$hostname" "$username" "$password" "$port"
done <device-list.txt
See Read a file line by line assigning the value to a variable for information about reading files line by line in Bash.
I've added quotes to stop Shellcheck warnings, and make the code safer.
See How to loop over the lines of a file? for an explanation of why the code in the question doesn't work.

#pjh has the correct answer.
But here's some notes on your script:
you iterate over all the words of the file, rather than its lines.
using cut -d :, you specify the delimiter between fields as :.
However, in your file you don't use : as the delimiter, but space ()
you calculate the $hostname variable by parsing $device, but then you use $device when calling the script
you calculate the $port variable by parsing the $port variable, which doesn't make any sense.
Here's an example on how to parse each line with cut:
cat device-list.txt | while read device; do
hostname=$(echo $device | cut -d" " -f 1)
port=$(echo $device | cut -d" " -f 2)
./script.exp $hostname $username $password $port
done

Related

How can I get the return of a bash script [duplicate]

This question already has answers here:
How do I set a variable to the output of a command in Bash?
(15 answers)
Closed 4 years ago.
I have a script like that
genhash --use-ssl -s $IP -p 443 --url $URL | grep MD5 | grep -c $MD5
I want to get stream generated by genhash in a variable. How do I redirect it into a variable $hash to compare inside a conditional?
if [ $hash -ne 0 ]
then echo KO
exit 0
else echo -n OK
exit 0
fi
Use the $( ... ) construct:
hash=$(genhash --use-ssl -s $IP -p 443 --url $URL | grep MD5 | grep -c $MD5)
TL;DR
To store "abc" into $foo:
echo "abc" | read foo
But, because pipes create forks, you have to use $foo before the pipe ends, so...
echo "abc" | ( read foo; date +"I received $foo on %D"; )
Sure, all these other answers show ways to not do what the OP asked, but that really screws up the rest of us who searched for the OP's question.
The answer to the question is to use the read command.
Here's how you do it
# I would usually do this on one line, but for readability...
series | of | commands \
| \
(
read string;
mystic_command --opt "$string" /path/to/file
) \
| \
handle_mystified_file
Here is what it is doing and why it is important:
Let's pretend that the series | of | commands is a very complicated series of piped commands.
mystic_command can accept the content of a file as stdin in lieu of a file path, but not the --opt arg therefore it must come in as a variable. The command outputs the modified content and would commonly be redirected into a file or piped to another command. (E.g. sed, awk, perl, etc.)
read takes stdin and places it into the variable $string
Putting the read and the mystic_command into a "sub shell" via parenthesis is not necessary but makes it flow like a continuous pipe as if the 2 commands where in a separate script file.
There is always an alternative, and in this case the alternative is ugly and unreadable compared to my example above.
# my example above as a oneliner
series | of | commands | (read string; mystic_command --opt "$string" /path/to/file) | handle_mystified_file
# ugly and unreadable alternative
mystic_command --opt "$(series | of | commands)" /path/to/file | handle_mystified_file
My way is entirely chronological and logical. The alternative starts with the 4th command and shoves commands 1, 2, and 3 into command substitution.
I have a real world example of this in this script but I didn't use it as the example above because it has some other crazy/confusing/distracting bash magic going on also.
read hash < <(genhash --use-ssl -s $IP -p 443 --url $URL | grep MD5 | grep -c $MD5)
This technique uses Bash's "process substitution" not to be confused with "command substitution".
Here are a few good references:
http://www.linuxjournal.com/content/shell-process-redirection
http://tldp.org/LDP/abs/html/process-sub.html
http://tldp.org/LDP/abs/html/commandsub.html ☚ for comparison
I guess compatible way:
hash=`genhash --use-ssl -s $IP -p 443 --url $URL | grep MD5 | grep -c $MD5`
but I prefer
hash="$(genhash --use-ssl -s $IP -p 443 --url $URL | grep MD5 | grep -c $MD5)"
If a pipeline is too complicated to wrap in $(...), consider writing a function. Any local variables available at the time of definition will be accessible.
function getHash {
genhash --use-ssl -s $IP -p 443 --url $URL | grep MD5 | grep -c $MD5
}
hash=$(getHash)
http://www.gnu.org/software/bash/manual/bashref.html#Shell-Functions
You can do:
hash=$(genhash --use-ssl -s $IP -p 443 --url $URL)
or
hash=`genhash --use-ssl -s $IP -p 443 --url $URL`
If you want to result of the entire pipe to be assigned to the variable, you can use the entire pipeline in the above assignments.
I got error sometimes when using $(`code`) constructor.
Finally i got some approach to that here: https://stackoverflow.com/a/7902174/2480481
Basically, using Tee to read again the ouput and putting it into a variable.
Theres how you see the normal output then read it from the ouput.
is not? I guess your current task genhash will output just that, a single string hash so might work for you.
Im so neewbie and still looking for full output & save into 1 command.
Regards.
Create a function calling it as the command you want to invoke. In this case, I need to use the ruok command.
Then, call the function and assign its result into a variable. In this case, I am assigning the result to the variable health.
function ruok {
echo ruok | nc *ip* 2181
}
health=echo ruok *ip*

Bash + SSH + Grep not generating output

I'm trying to use the script below to extract values from the df command on remote servers, then record to a log file. SSH keys are in place and no password is needed (this is not the problem).
It's getting hung up, however, and not spitting back output.
#!/bin/bash
PATH=/bin:/usr/bin:/usr/sbin
export PATH
SERVERLIST=/opt/scripts/server-list.dat
while IFS='|' read -u 3 hostname; do
echo evaluating $hostname...
SIZE=$(ssh $hostname | df -Pkhl | grep '/Volumes/UserStorage$' | awk '{print $2}')
echo $SIZE
done 3< $SERVERLIST
exit 0
You need to run df on the remote system, not pipe the output of an interactive ssh to it:
SIZE=$(ssh -n $hostname df -Pkhl | grep '/Volumes/UserStorage$' | awk '{print $2}')
Also, use the -n option to ssh to keep it from trying to read from stdin, which would consume the rest of the lines from the server list file.

netcat inside a while read loop returning immediately

I am making a menu for myself, because sometimes I need to search (Or NMAP which port).
I want to do the same as running the command in the command line.
Here is a piece of my code:
nmap $1 | grep open | while read line; do
serviceport=$(echo $line | cut -d' ' -f1 | cut -d'/' -f1);
if [ $i -eq $choice ]; then
echo "Running command: netcat $1 $serviceport";
netcat $1 $serviceport;
fi;
i=$(($i+1));
done;
It is closing immediately after it scanned everything with nmap.
Don't use FD 0 (stdin) for both your read loop and netcat. If you don't distinguish these streams, netcat can consume content emitted by the nmap | grep pipeline rather than leaving that content to be read by read.
This has a few undesirable effects: Further parts of the while/read loop don't get executed, and netcat sees a closed stdin stream and exits when the pipeline's contents are consumed (so you don't get interactive control of netcat, if that's what you're trying to accomplish). An easy way to work around this issue is to feed the output of your nmap pipeline in on a non-default file descriptor; below, I'm using FD 3.
There's a lot wrong with this code beyond the scope of the question, so please don't consider the parts I've copied-and-pasted an endorsement, but:
while read -r -u 3 line; do
serviceport=${line%% *}; serviceport=${serviceport##/*}
if [ "$i" -eq "$choice" ]; then
echo "Running command: netcat $1 $serviceport"
netcat "$1" "$serviceport"
fi
done 3< <(nmap "$1" | grep open)

Reading from pasted input with line breaks in a Bash Script

I've been trying for a couple nights to get this Script to run with no luck. I'm trying to write a script using Bash that allows a user to paste a block of text, and the script will grep out the valid IP addresses from the text, and automatically ping them in order.
So far, after much modification, I'm stuck at this point:
#!/bin/sh
echo Paste Text with IP Addresses
read inputtext
echo "$inputtext">inputtext.txt
grep -E -o "([0-9]{1,3}[\.]){3}[0-9]{1,3}" inputtext.txt > address.txt
awk '{print $1}' < address.txt | while read ip; do
if ping -c1 $ip >/dev/null 2>&1; then
echo $ip IS UP
else
echo $ip IS DOWN
fi
done
rm inputtext.txt
rm address.txt
After running this script, the user is prompted as desired, and if an IP address was included in the first line of text, the ping check will succeed, but then all the text after that line will be spat out onto the following command prompt. So it seems that my issue lies in when I read from user input. The only part that is being read is the first line, and once a break is encountered, the script does not considered any lines past the first in its work.
As written, you just need an outer loop to actually read each line of user input.
#!/bin/sh
echo Paste Text with IP Addresses
while read -r inputtext
do
echo "$inputtext">inputtext.txt
grep -E -o "([0-9]{1,3}[\.]){3}[0-9]{1,3}" inputtext.txt > address.txt
awk '{print $1}' < address.txt | while read ip; do
if ping -c1 $ip >/dev/null 2>&1; then
echo $ip IS UP
else
echo $ip IS DOWN
fi
done
rm inputtext.txt
rm address.txt
done
However, you can actually simplify this much further and eliminate the temporary files.
#!/bin/sh
echo Paste Text with IP Addresses
while read -r inputtext
do
ip=$(echo "$inputtext" | grep -E -o "([0-9]{1,3}[\.]){3}[0-9]{1,3}" | awk '{print $1}')
if ping -c1 $ip >/dev/null 2>&1; then
echo $ip IS UP
else
echo $ip IS DOWN
fi
done

bash script to read/ping a list of ip addresses from a text file and then write the results to another file?

I have a text file with a lists of IP addresses called address.txt which contains the following
172.26.26.1 wlan01
172.26.27.65 wlan02
172.26.28.180 wlan03
I need to write a bash script that reads the only IP addresses, ping them and output to a another text file to something like this:
172.26.26.1 IS UP
172.26.27.65 IS DOWN
172.26.28.180 IS DOWN
I am fairly new to bash scripting so I am not sure where to start with this. Any help would be much appreciated.
In Linux this would work:
awk '{print $1}' < address.txt | while read ip; do ping -c1 $ip >/dev/null 2>&1 && echo $ip IS UP || echo $ip IS DOWN; done
I don't have a cygwin now to test, but it should work there too.
Explanation:
With awk we get the first column from the input file and pipe it into a loop
We send a single ping to $ip, and redirect standard output and standard error to /dev/null so it doesn't pollute our output
If ping is successful, the command after && is executed: echo $ip IS UP
If ping fails, the command after || is executed: echo $ip IS DOWN
Somewhat more readable, expanded format, to put in a script:
#!/bin/sh
awk '{print $1}' < address.txt | while read ip; do
if ping -c1 $ip >/dev/null 2>&1; then
echo $ip IS UP
else
echo $ip IS DOWN
fi
done

Resources