This is my Server-Code for now.
I just want to get Data from a Client, analyze it and send my answer.
Is there a way to call my function managedata() in nc? My Code, obviously doesn't work.
server.sh:
managedata()
{
echo $1
#do something with data
return $1
}
listen()
{
echo "Server listening.."
nc -l -p 1234 -c '$(read i && managedata $i && echo $?)'
}
#MAIN
while true;
do
listen
done
Client.sh:
send()
{
echo $1 | nc $ip $port -o 0
}
#
register()
{
read -p "Login: " usrname
echo -n "Password: "
read -s password | shasum > password
#Schleife zum encrypten des Passworts
#Count = n -> Leslie Lamport Alogrithmus
n=5
count=$n
while [ $count -ge 2 ]
do
password="$(cat password | shasum)"
((count--))
done
password=`cat password`
echo
echo $password
data="reg-$usrname-$n-$password"
send $data
}
#
log()
{
echo "..."
}
#
menue()
{
echo "====== Lab: Shell Programming (BS) ======"
echo " r Register"
echo " l Login"
echo " q Quit"
echo
read -p "your choice: " check
case "$check" in
r) register;;
l) log;;
q) exit;;
esac
}
#MAIN
ip=$1
port=$2
while true
do
menue
done
bash -x server.sh:
+ true
+ listen
+ echo 'Server listening..'
Server listening..
+ nc -l -p 1234 -c '$(read i && managedata $i && echo $?)'
sh: 1: managedata: not found
+ true
+ listen
+ echo 'Server listening..'
Server listening..
+ nc -l -p 1234 -c '$(read i && managedata $i && echo $?)'
Yep you should "tell" bash to treat managedata as command or function that can be executed.
BTW, you need to use $() to execute whole expression not only managedata:
nc -l -p 1234 -c $(read i && managedata $i && echo $?)
Also bash can't return strings, only integer error codes, so return $1 won't work if it's not integer in range of 0 - 255.
Replace to return 0
Related
Latest Version
#!/bin/bash
set -e
shopt -s nocasematch
#vars
redbgbold='\e[1;97;41m'
resetcolor='\e[0m'
RegExFQDN='(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{1,63}(?<!-)\.)+[a-zA-Z]{2,63}$)'
#functions
ask() {
local input
until
read -rp "$1 > " input >&2 || return 1
grep -q -P "$2" <<< "$input"
do
printf "ERROR - "${redbgbold}"\"$input\""${resetcolor}" is not a valid " >&2; sed "s/.*the //" <<< "$1" >&2
done
printf '%s\n' "$input"
}
#code
while [ -z $fqdn ]; do
fqdn=$(ask "Enter the FQDN" $RegExFQDN)
echo "FQDN is $fqdn"
done
The Question
I have a read line, and I want to take what the user entered and see if it matches my regex, if it matches we leave the loop, if it fails it prints an error and we do the loop again until we get a match. It looks redundant to me, and I assume there should be a better way but not sure what that should be.
Original Code
#!/bin/bash
set -e
shopt -s nocasematch
function RegexValidation() {
if [ "$2" = "fqdn" ]; then
if [ `echo $1 | grep -c -P '(?=^.{1,254}$)(^(?>(?!\d+\.)[a-z0-9_\-]{1,63}\.?)+(?:[a-z]{2,})$)'` == "0" ]; then
echo "ERROR - $1 is not a valid FQDN"
unset $!{1}
fi
fi
}
while [ -z $fqdn ]; do
read -e -r -p "Enter the Fully Qualified Domain Name > " fqdn
RegexValidation $fqdn fqdn
done
shopt -u nocasematch
any help is appreciated.
Update #1 - fixed formatting issues.
Update #2 - using that other guy's suggestions with a few additional tweaks
I would do basically the same thing, but split it differently to make it easier to reuse:
#!/bin/bash
set -e
ask() {
local input
until
read -rp "$1 > " input >&2 || return 1
grep -q -P "$2" <<< "$input"
do
echo "Invalid answer. Try again" >&2
done
printf '%s\n' "$input"
}
ask_fqdn() {
ask "$1" '(?=^.{1,254}$)(^(?>(?!\d+\.)[a-z0-9_\-]{1,63}\.?)+(?:[a-z]{2,})$)'
}
fqdn=$(ask_fqdn "Enter first FQDN")
echo "You wrote $fqdn"
fqdn=$(ask_fqdn "Enter second FQDN")
echo "This time it was $fqdn"
number=$(ask "And now a number because why not" '^\d+$')
echo "So $number"
Now you don't have to write a new loop every time you want new information, and you can easily ask for new things without modifying the existing functions.
Have the function return a status, which you can test with if in the loop.
And rather than use test to check the result of grep, just test it directly with if. grep returns a non-zero status if the input doesn't match.
function RegexValidation() {
if [ "$2" = "fqdn" ]; then
if ! echo "$1" | grep -q -P '(?=^.{1,254}$)(^(?>(?!\d+\.)[a-z0-9_\-]{1,63}\.?)+(?:[a-z]{2,})$)'; then
echo "ERROR - $1 is not a valid FQDN"
return 1
fi
return 0
fi
}
while :; do
read -e -r -p "Enter the Fully Qualified Domain Name > " fqdn
if RegexValidation "$fqdn" fqdn
then break
fi
done
Also, remember to quote your variables.
I'm creating a sh script for a Chat using netcat.
This is the code:
#!/bin/bash
clear
echo
echo "-----------------------"
echo "| handShaker Chat 2.0 |"
echo "-----------------------"
echo
read -p 'Server or Client setUp? (s or c) > ' type
if [ $type == 's' ] || [ $type == 'S' ] || [ $type == 'server' ]
then
read -p 'Port (4321 Default) > ' port
if [ $port -gt 2000 ] && [ $port -lt 6500 ]
then
echo
echo "Started listening on port $port."
echo "Stream (Press ctrl + shift to end session) >"
echo
awk -W interactive '$0="Anonymous: "$0' | nc -l $port > /dev/null
else
echo "handShaker Error > The port $port is not a in the valid range (2000 ... 6500)."
fi
elif [ $type == 'c' ] || [ $type == 'C' ] || [ $type == 'client' ]
then
read -p 'Port (4321 Default) > ' port
if [ $port -gt 2000 ] && [ $port -lt 6500 ]
then
read -p 'Destination IP > ' ip
if [[ $ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]
then
echo
echo "Started streaming $ip on port $port."
echo "Stream (Press ctrl + shift to end session) >"
echo
awk -W interactive '$0="Anonymous: "$0' | nc $ip $port > /dev/null
else
echo "handShaker Error > Invalid IP Address."
fi
else
echo "handShaker Error > The port $port is not a in the valid range (2000 ... 6500)."
fi
else
echo "handShaker Error > $type is not a valid keyword."
fi
But I have the following problems: the awk -W parameter doesn't seem to exist, and the program actually stops after running the client.
I'm using the macOS terminal.
Can someone help me to fix this bugs and to improve my script?
Your script has an incorrect & unnecessary usage of awk with -W interactive flags which are not defined in any of the flavours of awk. Removing it should solve your problem.
Also your script has a bunch of bash variables defined and used without double-quoting. Remember to double quote variables prevent globbing and word splitting.
I've been playing with bash scripting for 40'ish days with 0 experience so forgive me if my code looks like crap. I have a script that will take the configured NTP servers out of the /etc/ntp.conf file (/root/ntp.conf for testing)
NTPSRVCounter=1
echo "--- NTP Configuration ---"
echo " "
while read -r line; do
if [ $NTPSRVCounter == 1 ] ; then
echo "Primary NTP: $line"
SEDConfiguredNTP1="$(echo $line | sed 's/\./\\./g')"
((NTPSRVCounter++))
echo " "
else
SEDConfiguredNTP2="$(echo $line | sed 's/\./\\./g')"
echo "Secondary NTP: $line"
echo ""
fi
done < <(grep -o -P '(?<=server ).*(?= iburst)' /root/ntp.conf)
And asks you if you want to change it with a case statement:
echo "Do you wish to change it? [Y/n]"
NTPSRVCounter2=1
read opt
case $opt in
Y|y) read -p "Enter in your primary NTP server: " -e -i '0.debian.pool.ntp.org' UserInputNTP1
read -p "Enter in your secondary NTP serer: " -e -i '1.debian.pool.ntp.org' UserInputNTP2
for NTP in "$UserInputNTP1" "$UserInputNTP2" ; do
is_fqdn "$NTP"
if [[ $? == 0 && $NTPSRVCounter2 == 1 ]] ; then
SEDUserInput1=$(echo $UserInputNTP1 | sed 's/\./\\./g')
((NTPSRVCounter2++))
elif [[ $? == 0 && $NTPSRVCounter2 == 2 ]] ; then
SEDUserInput2=$(echo $UserInputNTP2 | sed 's/\./\\./g')
sudo sed -i "s/$SEDConfiguredNTP1/$SEDUserInput1/g" /root/ntp.conf
sudo sed -i "s/$SEDConfiguredNTP2/$SEDUserInput2/g" /root/ntp.conf
else
echo "Fail!!! :-( "
fi
done
;;
N|n) return 0
;;
*) echo "I don't know what happened, but, eh, you're not supposed to be here."
;;
esac
The problem is with the "elif" statement and the function "is_fqdn" on the second run of the function. If I put "bash -x" on the script and run it, I see "is_fqdn" returning 0 on both runs of the function, but the elif statement "$?" is coming up as 1 instead of 0.
The two functions used are below. Have to validate NTP addresses as either valid domain names or I.P. addresses, right? :)
is_fqdn() {
hostname=$1
if [[ "$hostname" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
valid_ip "$hostname"
elif [[ "$hostname" == *"."* && "$hostname" != "localhost." && "$hostname" != "localhost" ]] ; then
return 0
else
return 1
fi
host $hostname > /dev/null 2>&1 || return 1
}
valid_ip(){
local stat=1
local ip=$1
if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
OIFS=$IFS
IFS="."
ip=($ip)
IFS=$OIFS
[[ ${ip[0]} -le 255 && ${ip[1]} -le 255 && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]]
stat=$?
fi
return "$stat"
}
The condition in your if sets the value of $?, and that is what's used by the condition in the elif part, not the return value of is_fqdn. You need to save the value if you want to use it in multiple places:
is_fqdn "$NTP"
is_fqdn_rv=$?
if [[ $is_fqdn_rv == 0 && $NTPSRVCounter2 == 1 ]] ; then
SEDUserInput1=$(echo $UserInputNTP1 | sed 's/\./\\./g')
((NTPSRVCounter2++))
elif [[ $is_fqdn_rv == 0 && $NTPSRVCounter2 == 2 ]] ; then
SEDUserInput2=$(echo $UserInputNTP2 | sed 's/\./\\./g')
sudo sed -i "s/$SEDConfiguredNTP1/$SEDUserInput1/g" /root/ntp.conf
sudo sed -i "s/$SEDConfiguredNTP2/$SEDUserInput2/g" /root/ntp.conf
else
echo "Fail!!! :-( "
fi
I have a bash script that asks the user for their details.
I'm setting a limit to how long we wait for the input. I've found this and it appears to what I want.
timelimit=5
echo -e " You have $timelimit seconds\n Enter your name quickly: \c"
name=""
read -t $timelimit name
#read -t $timelimit name <&1
# for bash versions bellow 3.x
if [ ! -z "$name" ]
then
echo -e "\n Your name is $name"
else
echo -e "\n TIME OUT\n You failed to enter your name"
fi
It shows "You have 5 seconds..." any way to update the output so it shows 4,3,2,1 etc as it counts down ?
Thanks
I have tried most of these answers and none of them worked perfectly for me.
Been playing with this for a local developer deployment script.
This solves a few of the issues noted, like including printed output, etc.
Also wrapped as a function for portability. I'm keen to see any improvements.
Here is my solution:
#!/bin/bash
# set -euo pipefail
READTIMEOUT=5
function read_yn {
MESSAGE=$1
TIMEOUTREPLY=$2
NORMALREPLY="Y"
if [ -z "${TIMEOUTREPLY}" ]; then
TIMEOUTREPLY="Y"
fi
TIMEOUTREPLY_UC=$( echo $TIMEOUTREPLY | awk '{print toupper($0)}' )
TIMEOUTREPLY_LC=$( echo $TIMEOUTREPLY | awk '{print tolower($0)}' )
if [ "${TIMEOUTREPLY_UC}" == "Y" ]; then
NORMALREPLY="N"
fi
NORMALREPLY_UC=$( echo $NORMALREPLY | awk '{print toupper($0)}' )
NORMALREPLY_LC=$( echo $NORMALREPLY | awk '{print tolower($0)}' )
for (( i=$READTIMEOUT; i>=0; i--)); do
printf "\r${MESSAGE} [${NORMALREPLY_UC}${NORMALREPLY_LC}/${TIMEOUTREPLY_UC}${TIMEOUTREPLY_LC}] ('${TIMEOUTREPLY_UC}' in ${i}s) "
read -s -n 1 -t 1 waitreadyn
if [ $? -eq 0 ]
then
break
fi
done
yn=""
if [ -z $waitreadyn ]; then
echo -e "\nNo input entered: Defaulting to '${TIMEOUTREPLY_UC}'"
yn="${TIMEOUTREPLY_UC}"
else
echo -e "\n${waitreadyn}"
yn="${waitreadyn}"
fi
}
read_yn "TESTING" "y"
GIST: https://gist.github.com/djravine/7a66478c37974940e8c39764d59d35fa
LIVE DEMO: https://repl.it/#DJRavine/read-input-with-visible-countdownsh
This should work and shouldn't overwrite input, bit more long winded than the other solutions.
#!/bin/bash
abend()
{
stty sane
exit
#Resets stty and then exits script
}
DoAction(){
stty -echo
#Turn off echo
tput sc
#Save cursor position
echo -ne "\033[0K\r"
# Remove previous line
tput cuu1
#Go to previous line
tput el
#clear to end of line
echo "You have $(($time-$count)) seconds"
#Echo timer
echo -n "$Keys"
#Echo currently typed text
stty echo
#turn echo on
tput rc
#return cursor
}
main()
{
trap abend SIGINT # Trap ctrl-c to return terminal to normal
stty -icanon time 0 min 0 -echo
#turn of echo and set read time to nothing
keypress=''
time=5
echo "You have $time seconds"
while Keys=$Keys$keypress; do
sleep 0.05
read keypress && break
((clock = clock + 1 ))
if [[ clock -eq 20 ]];then
((count++))
clock=0
DoAction $Keys
fi
[[ $count -eq $time ]] && echo "you have run out of time" && abend
done
stty sane
echo Your username was $Keys
echo "Thanks for using this script."
exit 0
}
main
This seems to work:
$ cat test.sh
total=5 # total wait time in seconds
count=0 # counter
while [ ${count} -lt ${total} ] ; do
tlimit=$(( $total - $count ))
echo -e "\rYou have ${tlimit} seconds to enter your name: \c"
read -t 1 name
test ! -z "$name" && { break ; }
count=$((count+1))
done
if [ ! -z "$name" ] ; then
echo -e "\nyour name is $name"
else
echo -e "\ntime out"
fi
#!/bin/bash
timelimit=6
name=""
for (( i = 1 ; i <= $timelimit; i++ )); do
echo -ne "\rYou have $(expr $timelimit - $i) seconds. Enter your name quickly: \c"
[ ! -z "$name" ] && { break ; }
read -t 1 name
done
if [ -z "$name" ]; then
echo -e "\n TIME OUT\n You failed to enter your name"
else
echo -e "\n Your name is $name"
fi
this should work
This works fine and fast for me:
#!/bin/bash
#Sets starttimestamp
starttime=$(date +%s)
#Sets timeout
timeout=5
#sets successflag default to false
success=false
#Save Cursorposition
echo -n -e "\033[s"
#While time not up
while [ $(($starttime+$timeout)) -gt $(date +%s) ] ; do
#Return to saved Cursorpositon
echo -n -e "\033[u"
#Display time left
echo "$(((starttime+timeout)-$(date +%s))) seconds left"
#Ask for 1 char then go on in loop make it look like an ongoing input by adding the user variable to the prompt
if read -p foo="Username: $user" -n 1 -t 1 c ; then
#If user hits return in time c will be empty then break out of loop and set success true
if [[ $c == "" ]] ; then
success=true
break
fi
# Append latest character to user variable
user=${user}${c}
unset c
fi
done
if $success ; then
echo "Yiha!"
else
echo "Too late!"
fi
I'm trying to write an SSH version of ICMP Ping, as follows:
TARGET_IP=""
count=0
time_start=0
time_end=0
time_taken=0
TARGET_IP=$1
while [ $count -lt 5 ]
do
((count=count+1))
time_start=$(date +%s%N)
temp_target_key=$(ssh-keyscan $TARGET_IP) > /dev/null 2>&1
time_end=$(date +%s%N)
time_taken=$((( $time_end - $time_start ) / 1000000))
echo "Time taken=$time_taken ms."
target_key=$(echo $temp_target_key | awk '{print $3}') > /dev/null 2>&1
echo $temp_target_key > target_key.txt
ssh-keygen -l -f target_key.txt > /dev/null 2>&1
test=$?
if [ $test -ne 0 ]
then
echo "Device returned invalid RSA Public Key"
echo -e "\n"
echo -e "\n"
else
echo "Device responding correctly."
echo -e "\n"
fi
done
exit
ICMP Ping reports ping times of 5ms compared to this script reporting 300ms for the same device. I realise that I'm timing the script AND device programming/firmware response times too, but am I doing this the best way please?
Thanks
I can't see a better method to simulate a ssh ping. Nevertheless I've tried to improve your script in a more pure bash style:
#! /bin/bash
if (($# != 1)); then
echo "Usage: ${0##*/} host" >&2
exit 1
fi
TARGET_IP="$1"
target_key_file="target_key.txt"
for ((count=0; count < 5; count++))
do
time_start=$(date +%s%N)
temp_target_key=$(ssh-keyscan "$TARGET_IP") > /dev/null 2>&1
time_end=$(date +%s%N)
time_taken=$(((time_end - time_start ) / 1000000))
echo "Time taken=$time_taken ms."
read _ _ target_key _ <<< "$temp_target_key"
echo "$target_key"
echo "$temp_target_key" >| "$target_key_file"
if ! ssh-keygen -l -f "$target_key_file" > /dev/null 2>&1
then echo -e "Device returned invalid RSA Public Key.\n\n"
else echo -e "Device responding correctly.\n\n"
fi
done