bash: filling user read input automatically with param list - bash

I'm trying to use Wireguard road-warrior script from here https://github.com/Nyr/wireguard-install
But i cant make it run in noninteractive mode with predefined parameters. I've read some other similar topics here about how to provide answers to bash "read", but suggestions from there doesn't work.
I've tried this:
# bash wireguard-install.sh < params
# printf '%s\n' 51822 clientcustom 2 y | bash wireguard-install.sh
# echo "51822 clientcustom 2 y" | bash wireguard-install.sh
in every case installer just uses default values. What am i doing wrong?

https://github.com/Nyr/wireguard-install/blob/master/wireguard-install.sh#L15
Indeed, this is a bit problematic. Typically, not caring, you would just:
( sleep 1; printf '%s\n' 51822 clientcustom 2 y ) | ...
A real robust solution, you would parse the output of the process to know when to write response, either with expect or Bash or something better.
coproc bash wireguard-install.sh
while IFS= read -r -u "${COPROC[0]}" line; do
case "$line" in
"IPv4 address [1]:"*) echo something >&"${COPROC[1]}"; ;;
"other prompt"*) echo other stuff >&"${COPROC[1]}"; ;;
"etc...") echo ... ;;
esac
done

Some alternatives:
1. you could simply drop problematic line while running.
bash <(sed /read\ -N\ 999999/d wireguard-install.sh) < <(
printf %s\\n 51822 clientcustom 2 y)
This will drop line used to
# Discard stdin. Needed when running from an one-liner which includes a newline
read -N 999999 -t 0.001
2. Instead of passing variables, you could edit script:
$ sed 's/^[ \o11]*read -p/ /p;d' wireguard-install.sh | uniq
"DNS server [1]: " dns
"IPv4 address [1]: " ip_number
"Public IPv4 address / hostname [$get_public_ip]: " public_ip
"Public IPv4 address / hostname: " public_ip
"IPv6 address [1]: " ip6_number
"Port [51820]: " port
"Name [client]: " unsanitized_client
"Should automatic updates be enabled for it? [Y/n]: " boringtun_updates
"Option: " option
"Name: " unsanitized_client
"Client: " client_number
"Confirm $client removal? [y/N]: " remove
"Confirm WireGuard removal? [y/N]: " remove
So you could prepare a sed string:
printf -v sedscr 's/read -p.* \(%s\) *$/\\1="%s"/;' port 51822 \
unsanitized_client clientcustom client_number 2 remove y
Then ensure all's ok:
sed -e "/read -N 99999/d;$sedscr" <wireguard-install.sh |
diff -u wireguard-install.sh -
you will see some lines like
# Discard stdin. Needed when running from an one-liner which includes a newline
-read -N 999999 -t 0.001
# Detect OpenVZ 6
if [[ $(uname -r | cut -d "." -f 1) -eq 2 ]]; then
## -235,15 +234,15 ##
fi
echo
echo "What port should WireGuard listen to?"
- read -p "Port [51820]: " port
+ port="51822"
until [[ -z "$port" || "$port" =~ ^[0-9]+$ && "$port" -le 65535 ]]; do
And finally
bash <(
printf -v sedscr 's/read -p.* \(%s\) *$/\\1="%s"/;' port 51822 \
unsanitized_client clientcustom client_number 2 remove y
sed -e "/read -N 99999/d;$sedscr" <wireguard-install.sh
)

Related

how to stop if statement from executing a command bash?

I am trying to check if a particular string contains a substring. It happens that the substring is also a command, which I do not want to execute but every time the if statement is hit, it does try to execute. How to stop it?
This is the part I am talking about:
if [[ "$installed" == *"$package"* ]]
then
"$package" >> $filename
fi
And this is the rest of the script:
read -p "enter first ID from yum History to search " startId
read -p "enter the last ID from yum history to search " endId
#getting undone operations so we can skip on them
undone=$(sudo yum history info $startId..$endId | grep Command | grep undo | awk {print$6}'|column)
echo "those operations were undone so we won\'t take them under consideration "$undone
#getting a list of installed packages
installed=$(sudo yum list installed | column)
read -r -p "enter an absolute path and a file name to save your result " filename
$(sudo touch "$filename")
#setting up a counter for a while loop
counter=1
while [ $counter -le $endId ]
do
if [[ "$undone" != *"$counter"* ]]
then
printer=1
package="something"
while [[ ! -z $package ]]
do
#get info about a particular history record, get the command, split by ":",
#print what's on the other side of ":", remove "re" and "install", and -y,
#separate by spaces and print 1st word and then second.
package=`sudo yum history info $counter | grep Command | awk -F":" '{print$2}'| grep install | sed s/'re'// | sed s/'install'// | sed s/'-y'//| awk -F' ' '{print$'$printer'}'`
((printer++))
HERE>>>>> if [[ "$installed" == *"$package"* ]]
then
"$package" >> $filename
fi
done
((counter++))
else
echo "this operation was undone".$counter
((counter++))
fi
done
$(sudo sort -u $filename -o $filename)
Output when running with -x option:
+ [[ Loaded plugins: langpacks, product-id, search-disabled-repos, subscription-
: manager
Installed Packages
.
.
.
zlib-devel.x86_64 1.2.7-18.el7 #rhel-server-local == *mcelog.x86_64* ]]
+ mcelog.x86_64
history: line 43: mcelog.x86_64: command not found
+ [[ ! -z mcelog.x86_64 ]]

Ping Script with filter

I have a text file with host names and IP addresses like so (one IP and one host name per row). The IP addresses and host names can be separated by a spaces and/or a pipe, and the host name may be before or after the IP address
10.10.10.10 HW-DL11_C023
11.11.11.11 HW-DL11_C024
10.10.10.13 | HW-DL12_C023
11.11.11.12 | HW-DL12_C024
HW-DL13_C023 11.10.10.10
HW-DL13_C024 21.11.11.11
HW-DL14_C023 | 11.10.10.10
HW-DL14_C024 | 21.11.11.11
The script below should be able to ping hosts with a common denominator e.g. DL13 (there are two devices and it will ping only those two). What am I doing wrong, as I simply can`t make it work?
The script is in the same directory as the data; I don`t get errors, and everything is formatted. The server is Linux.
pingme () {
hostfile="/home/rex/u128789/hostfile.txt"
IFS= mapfile -t hosts < <(cat $hostfile)
for host in "${hosts[#]}"; do
match=$(echo "$host" | grep -o "\-$1_" | sed 's/-//' | sed 's/_//')
if [[ "$match" = "$1" ]]; then
hostname=$(echo "$host" | awk '{print $2}')
ping -c1 -W1 $(echo "$host" | awk '{print $1}') > /dev/null
if [[ $? = 0 ]]; then
echo "$hostname is alive"
elif [[ $? = 1 ]]; then
echo "$hostname is dead"
fi
fi
done
}
Try adding these two lines to your code:
pingme () {
hostfile="/home/rex/u128789/hostfile.txt"
IFS= mapfile -t hosts < <(cat $hostfile)
for host in "${hosts[#]}"; do
echo "Hostname: $host" # <-------- ADD THIS LINE -------
match=$(echo "$host" | grep -o "\-$1_" | sed 's/-//' | sed 's/_//')
echo "...matched with $match" # <-------- ADD THIS LINE -------
if [[ "$match" = "$1" ]]; then
hostname=$(echo "$host" | awk '{print $2}')
ping -c1 -W1 $(echo "$host" | awk '{print $1}') > /dev/null
if [[ $? = 0 ]]; then
echo "$hostname is alive"
elif [[ $? = 1 ]]; then
echo "$hostname is dead"
fi
fi
done
}
Then when you run it, you should see a list of your hosts, at least.
If you don't then you're not reading your file successfully.
If you do, there's a problem in your per-host logic.
Congratulations! You've divided your problem into two smaller problems. Once you know which half has the problem, keep dividing the problem in half until the smallest possible problem is staring you in the face. You'll probably know the solution at that point. If not, add your findings to the question and we'll help out from there.
The original code doesn't handle the pipe separator or the possibly reversed hostname and IP address in the input file. It also makes a lot of unnecessary use of external programs (grep, sed, ...).
Try this:
# Enable extended glob patterns - e.g. +(pattern-list)
shopt -s extglob
function pingme
{
local -r host_denom=$1
local -r hostfile=$HOME/u128789/hostfile.txt
local ipaddr host tmp
# (Add '|' to the usual characters in IFS)
while IFS=$'| \t\n' read -r ipaddr host ; do
# Swap host and IP address if necessary
if [[ $host == +([0-9]).+([0-9]).+([0-9]).+([0-9]) ]] ; then
tmp=$host
host=$ipaddr
ipaddr=$tmp
fi
# Ping the host if its name contains the "denominator"
if [[ $host == *-"$host_denom"_* ]] ; then
if ping -c1 -W1 -- "$ipaddr" >/dev/null ; then
printf '%s is alive\n' "$host"
else
printf '%s is dead\n' "$host"
fi
fi
done < "$hostfile"
return 0
}
pingme DL13
The final line (call the pingme function) is just an example, but it's essential to make the code do something.
REX, you need to be more specific about your what IP's you are trying to get from this example. You also don't ping enough times IMO and your script is case sensitive checking the string (not major). Anyway,
First, check that your input and output is working correctly, in this example I'm just reading and printing, if this doesn't work fix permissions etc :
file="/tmp/hostfile.txt"
while IFS= read -r line ;do
echo $line
done < "${file}"
Next, instead of a function first try to make it work as a script, in this example I manually set "match" to DL13, then I read each line (like before) and (1) match on $match, if found I remove the '|', and then read the line into an array of 2. if the first array item is an a IP (contains periods) set it as the IP the other as hostname, else set the opposite. Then run the ping test.
# BASH4+ Example:
file="/tmp/hostfile.txt"
match="dl13"
while IFS= read -r line ;do
# -- check for matching string (e.g. dl13 --
[[ "${line,,}" =~ "${match,,}" ]] || continue
# -- We found a match, split out host/ip into vars --
line=$(echo ${line//|})
IFS=' ' read -r -a items <<< "$line"
if [[ "${items[0]}" =~ '.' ]] ;then
host="${items[1]}" ; ip="${items[0]}"
else
host="${items[0]}" ; ip="${items[1]}"
fi
# -- Ping test --
ping -q -c3 "${ip}" > /dev/null
if [ $? -eq 0 ] ;then
echo "$host is alive!"
else
echo "$host is toast!"
fi
done < "${file}"

CentOS, Convert ipV6 short to long form

I have just started with bash scripting and there is a small problem which I have to solve to go on:
I'm getting an IPv6 address in this format:
1080::8:800:200C:417A
Now I want to convert the short into the long IPv6 form like: 1080:0:0:0:8:800:200C:417A
Is there a regex expression or something similar to convert that?
I am working on a docker container which runs on CentOS.
It isn't a regex, but it is 'something similar' and it does the job: (tested with python3.5.1)
>>> import ipaddress
>>> x = '1080::8:800:200C:417A'
>>> y = ipaddress.ip_address(x)
>>> y.exploded
'1080:0000:0000:0000:0008:0800:200c:417a'
>>>
Reference:
https://docs.python.org/3/library/ipaddress.html
#!/bin/bash
echo "enter the ip address:"
read s
if [[ $s =~ ^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$ ]]; then
echo -e '\E[47;31m'"\033[1mIPv6 Format\033[0m"
echo -n "The IPv6 Address Expanded Form:"
EXPANDED=`sipcalc $s | fgrep Expand | cut -d '-' -f 2`
echo -e "\033[32m $EXPANDED\033[0m"
echo -n "IPv6 address Compress Form:"
Compress=`sipcalc $s | fgrep Comp | cut -d '-' -f 2`
echo -e "\033[32m$Compress\033[0m"
echo -n "Address Type of IPv6:"
type=`sipcalc $s | fgrep type | cut -d '-' -f 2,3,4`
comment=`sipcalc $s | fgrep Comment | cut -d '-' -f 2`
echo -e "\033[32m $type$comment\033[0m"
else
echo -e '\E[37;44m'"\033[1mNOT VALID IPv6 address\033[0m"
fi
This is my code for ipv6 validation.U will get the expanded form of ip using "sipcalc".but you should do some grep and cut commands
Here is how I decompress IPv6 address.
#!/bin/bash
decompress_ipv6_address() {
# How many hextets are there
num_hextets=${addr//[^:]/}
num_hextets=${#num_hextets}
# Fix up beginning and end
[[ $addr =~ ^:: ]] && addr='0'$addr
[[ $addr =~ ::$ ]] && addr=$addr'0'
# Create additional hextets
additional_hextets=':'
for (( i=$num_hextets; $i<8; i++ ))
do
additional_hextets=$additional_hextets'0:'
done
# Insert additional hextets (replace ::)
addr=${addr/::/$additional_hextets}
}

replacing some commands in an existing BASH script

I have the following script which sends the results of an iwlist scan via OSC:
#!/bin/bash
NUM_BANKS=20
while [[ "$input" != "\e" ]] ; do
networks=$(iwlist wlan0 scanning | awk 'BEGIN{ FS="[:=]"; OFS = " " }
/ESSID/{
#gsub(/ /,"\\ ",$2)
#gsub(/\"/,"",$2)
essid[c++]=$2
}
/Address/{
gsub(/.*Address: /,"")
address[a++]=$0
}
/Encryption key/{ encryption[d++]=$2 }
/Quality/{
gsub(/ dBm /,"")
signal[b++]=$3
}
END {
for( c in essid ) { print "/wlan_scan ",essid[c],signal[c],encryption[c] }
}'
)
read -t 0.1 input
echo "$networks" | while read network; do
set $network
hash=` echo "$2" | md5sum | awk '{ print $1 }'| tr '[:lower:]' '[:upper:]'`
bank=`echo "ibase=16;obase=A; $hash%$NUM_BANKS " | bc`
echo "$1$bank $2 $3 $4"
echo "$1$bank $2 $3 $4" | sendOSC -h localhost 9997
done
#echo "$networks" | sendOSC -h localhost 9997
done
An example of the output from this is '/wlan_scan13 BTHomehub757 -85 On', which is then sent via the sendOSC program.
I basically need to replace the iwlist scan data with the results of this tshark scan:
sudo tshark -I -i en1 -T fields -e wlan.sa_resolved -e wlan_mgt.ssid -e radiotap.dbm_antsignal type mgt subtype probe
which similarly outputs two strings and an int, outputting a result like:
'Hewlett-_91:fa:xx EE-BrightBox-mjmxxx -78'.
So eventually I want the script to give me an output in this instance of
'/wlan13 Hewlett-_91:fa:xx EE-BrightBox-mjmxxx -78'.
Both scans constantly generate results in this format at about the same rate, updating as new wifi routers are detected, and these are sent out as soon as they arrive over the sendOSC program.
This is probably a pretty simple edit for an experienced coder, but I've been trying to work this out for days and I figured I should ask for help!
If someone could clarify what needs to stay and what needs to go here I'd really appreciate it.
Many thanks.
Do you really want to replace commands? The sane approach would seem to be to add an option to the script to specify which piece of code to run, and include them both.
# TODO: replace with proper option parsing
case $1 in
--tshark) command=tshark_networks; shift;;
*) command=iwlist_networks;;
esac
tshark_networks () {
sudo tshark -I -i en1 -T fields \
-e wlan.sa_resolved \
-e wlan_mgt.ssid \
-e radiotap.dbm_antsignal type mgt subtype probe
}
iwlist_networks () {
iwlist wlan0 scanning | awk .... long Awk script here ....
}
while [[ "$input" != "\e" ]] ; do
networks=$($command)
read -t 0.1 input
echo "$networks" | while read network; do
: the rest as before, except fix your indentation
This also has the nice side effect that the hideous iwlist command is encapsulated in its own function, outside of the main loop.
... Well, in fact, I might refactor the main loop to
while true; do
$command |
while read a b c d; do
hash=$(echo "$b" | md5sum | awk '{ print toupper($1) }')
bank=$(echo "ibase=16;obase=A; $hash%$NUM_BANKS " | bc)
echo "$a$bank $b $c $d"
echo "$a$bank $b $c $d" | sendOSC -h localhost 9997
done
read -t 0.1 input
case $input in '\e') break;; esac
done

ping an output within if loop

Using the script below I want to be able to get the IP address that is outputted at the end of it and then ping it.
#!/usr/local/bin/bash
echo -n "Enter server number:"
read userinput
lookupip="d $userinput"
if [[ $userinput -lt 0 || $userinput -gt 9999 ]] #checks that the input is within the desired range
then
echo "Input outside acceptable range."
else
#grep gets just the IP address
$lookupip | grep -E -o '(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)' | sed '1 ! d'
I can't figure out how to do this with the output:
> or >> filename | xargs ping
as using " or ` around the grep command (or putting it in a variable like so:
ipgrep=$(grepcommand)
ipgrep=`grepcommand`
or variables doesn't seem to work.
Derp, I only had to add:
| xargs ping -c 1
after:
sed '1 ! d'

Resources