Bash: How to use read with conditions & loops

Hello,

Below I try to control that the input is good an IP :

#!/bin/bash

cp /home/scripts/choice_interfaces.txt /home/scripts/interfaces.txt
chmod 644 /home/scripts/interfaces.txt

echo -e "Please enter the network informations into the /etc/network/interfaces file, complete them below :\n"

sed -n '1,10p' choice_interfaces.txt

printf "address " ; read -r address
if [[ $address =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
printf "network " ; read -r network
printf "netmask " ; read -r netmask
printf "broadcast " ; read -r broadcast
printf "gateway " ; read -r gateway
else
echo "$address doesn't correct"
fi
sed -i '12s/$/\n address '$address'/' interfaces.txt
sed -i '13s/$/\n network '$network'/' interfaces.txt
sed -i '14s/$/\n netmask '$netmask'/' interfaces.txt
sed -i '15s/$/\n broadcast '$broadcast'/' interfaces.txt
sed -i '16s/$/\n gateway '$gateway'/' interfaces.txt

mv /home/scripts/interfaces.txt /etc/network/interfaces

I try a loop.. :

printf "address " ; read -r address
while [[ $address =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; do
printf "address " ; read -r address
done 
I wanted that the user be invited to type until his input is good
printf "network " ; read -r network 
printf "netmask " ; read -r netmask
printf "broadcast " ; read -r broadcast
printf "gateway " ; read -r gateway

I do an example that I will wish, because I row.. :

#!/bin/bash

cp /home/scripts/choice_interfaces.txt /home/scripts/interfaces.txt
chmod 644 /home/scripts/interfaces.txt

echo -e "Please enter the network informations into the /etc/network/interfaces file, complete them below :\n"

sed -n '1,10p' choice_interfaces.txt

while[[ $address $network $netmask $broadcast $gateway  =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] || [[-z $address $network $netmask $broadcast $gateway]]; do (I found it on google to test if the input is empty but Its not working)
printf "address " ; read -r address
printf "network " ; read -r network
printf "netmask " ; read -r netmask
printf "broadcast " ; read -r broadcast
printf "gateway " ; read -r gateway
done

sed -i '12s/$/\n address '$address'/' interfaces.txt
sed -i '13s/$/\n network '$network'/' interfaces.txt
sed -i '14s/$/\n netmask '$netmask'/' interfaces.txt
sed -i '15s/$/\n broadcast '$broadcast'/' interfaces.txt
sed -i '16s/$/\n gateway '$gateway'/' interfaces.txt

#mv /home/scripts/interfaces.txt /etc/network/interfaces1

Thanks in advance :b::slight_smile: I hope I was enough clear :rolleyes:

You could write a function to prompt for and set your IP address like this:

#!/bin/bash
fetchip() {

    printf "%s: " "$1"
    read
    while ! [[ "$REPLY" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; do
      printf "Invalid $1 IP address - Please re-enter\n%s: " "$1"
      read
    done

    printf -v $1 "%s" "$REPLY"
}

fetchip network
fetchip netmask
fetchip broadcast
fetchip gateway

echo "-----------------"
echo "Network=$network"
echo "Netmask=$netmask"
echo "Broadcase=$broadcast"
echo "gateway=$gateway"
1 Like

You can have more than one line in a while/until condition.
The following uses a code block between until...do plus another explicit code block within {...}

#!/bin/bash
fetchip() {
  printf "%s: " "$1"
  until
    read
    [[ "$REPLY" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] &&
    {
    IFS=. read ip1 ip2 ip3 ip4 <<< "$REPLY"
    [[ $ip1 -le 255 && $ip2 -le 255 && $ip3 -le 255 && $ip4 -le 255 ]]
    }
  do
    printf "Invalid $1 IP address - Please re-enter\n%s: " "$1"
  done
}
...
1 Like

Hi, thanks to you both for the solutions :b::slight_smile: I make a script for a basic installation of a servers, here the beginning, if anyone is interested and if you have some critics or advices don't hesitate, it will be beneficial to me :
1st step, I have created a file with a template from an interface file, named choice_interfaces.txt :

#--------------------------------------------------------------
# The loopback network interface
#--------------------------------------------------------------

auto lo
iface lo inet loopback

#--------------------------------------------------------------
# Eth0
#--------------------------------------------------------------
auto eth0
iface eth0 inet static

2nd step, I have created another file with my script :

#!/bin/bash

#!/bin/bash
fetchip() {
  printf "%s: " "$1"
  until
    read
    [[ "$REPLY" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] &&
    {
    IFS=. read ip1 ip2 ip3 ip4 <<< "$REPLY"
    [[ $ip1 -le 255 && $ip2 -le 255 && $ip3 -le 255 && $ip4 -le 255 ]]
    }
  do
    printf "Invalid $1 IP address - Please re-enter\n%s: " "$1"
  done
}

cp /home/scripts/choice_interfaces.txt /home/scripts/interfaces.txt
chmod 644 /home/scripts/interfaces.txt

echo -e "Please enter the network informations into the /etc/network/interfaces file, complete them below :\n"

sed -n '1,10p' choice_interfaces.txt

fetchip address
fetchip network
fetchip netmask
fetchip broadcast
fetchip gateway

sed -i '12s/$/\n address '$address'/' interfaces.txt
sed -i '13s/$/\n network '$network'/' interfaces.txt
sed -i '14s/$/\n netmask '$netmask'/' interfaces.txt
sed -i '15s/$/\n broadcast '$broadcast'/' interfaces.txt
sed -i '16s/$/\n gateway '$gateway'/' interfaces.txt

#mv /home/scripts/interfaces.txt /etc/network/interfaces

on this, see you later :b::slight_smile:

A few comments:

  • the files' path seems to be used inconsistently - either use absolute paths on file names, or cd into the working directory at the start.
  • where do you store and/or use fetchip 's result/output?
  • those many sed commands could be collected into one single one only.
  • the keywords you use for the sed commands don't seem to show up in the choice_interfaces.txt.
1 Like

thank you, I noted, I'll think about it :b:

You need a trick that stuffs the result into the variable name that has been passed to the function as an argument.
Like Chubler_XL's

  printf -v "$1" "%s" "$REPLY"

Or with read:

fetchip() {
  until
    read -p "$1: "
    [[ "$REPLY" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] &&
    {
    IFS=. read ip1 ip2 ip3 ip4 <<< "$REPLY"
    [[ $ip1 -le 255 && $ip2 -le 255 && $ip3 -le 255 && $ip4 -le 255 ]]
    }
  do
    echo "Invalid $1 IP address - Please re-enter"
  done
  # Read the $REPLY into the given variable name
  read "$1" <<< "$REPLY"
}

For consistent file naming you best define a variable, like

iffile=interfaces.txt

and then use $iffile .
With sed you can append to a certain line number (or to a line with a certain content)

sed -i '
12a\
 address '$address'\
 network '$network'\
 netmask '$netmask'\
 broadcast '$broadcast'\
 gateway '$gateway'
' "$iffile"

If this is at the end of the file, the shell can simply append a multi-line string to the file:

echo "\
 address $address
 network $network
 netmask $netmask
 broadcast $broadcast
 gateway $gateway" >> "$iffile"

Thanks a lot, I have found a possible solution alone I know that isn't the best but It works ^^ But I will take your solution ^^:

#!/bin/bash


address_status() {
 printf "%s: " "$1"
  until
    read
    [[ "$REPLY" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] &&
    {
    IFS=. read ip1 ip2 ip3 ip4 <<< "$REPLY"
    [[ $ip1 -le 255 && $ip2 -le 255 && $ip3 -le 255 && $ip4 -le 255 ]]
    }
  do
    printf "Invalid $1 IP address - Please re-enter\n%s: " "$1"
  done
 sed -i '12s/$/\n address '$REPLY'/' interfaces
}

network_status() {
 printf "%s: " "$1"
  until
    read
    [[ "$REPLY" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] &&
    {
    IFS=. read ip1 ip2 ip3 ip4 <<< "$REPLY"
    [[ $ip1 -le 255 && $ip2 -le 255 && $ip3 -le 255 && $ip4 -le 255 ]]
    }
  do
    printf "Invalid $1 IP address - Please re-enter\n%s: " "$1"
  done
sed -i '13s/$/\n network '$REPLY'/' interfaces
}

netmask_status() {
 printf "%s: " "$1"
  until
    read
    [[ "$REPLY" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] &&
    {
    IFS=. read ip1 ip2 ip3 ip4 <<< "$REPLY"
    [[ $ip1 -le 255 && $ip2 -le 255 && $ip3 -le 255 && $ip4 -le 255 ]]
    }
  do
    printf "Invalid $1 IP address - Please re-enter\n%s: " "$1"
  done
sed -i '14s/$/\n netmask '$REPLY'/' interfaces
}

broadcast_status() {
 printf "%s: " "$1"
  until
    read
    [[ "$REPLY" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] &&
    {
    IFS=. read ip1 ip2 ip3 ip4 <<< "$REPLY"
    [[ $ip1 -le 255 && $ip2 -le 255 && $ip3 -le 255 && $ip4 -le 255 ]]
    }
  do
    printf "Invalid $1 IP address - Please re-enter\n%s: " "$1"
  done
sed -i '15s/$/\n broadcast '$REPLY'/' interfaces
}
gateway_status() {
  printf "%s: " "$1"
  until
    read
    [[ "$REPLY" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] &&
    {
    IFS=. read ip1 ip2 ip3 ip4 <<< "$REPLY"
    [[ $ip1 -le 255 && $ip2 -le 255 && $ip3 -le 255 && $ip4 -le 255 ]]
    }
  do
    printf "Invalid $1 IP address - Please re-enter\n%s: " "$1"
  done
sed -i '16s/$/\n gateway '$REPLY'/' interfaces
}

cd /etc/network/

sed -i '1,30d' interfaces

echo -e "#--------------------------------------------------------------\n# The loopback network interface\n#--------------------------------------------------------------\n\nauto lo\niface lo inet loopback\n\n
#--------------------------------------------------------------\n# Eth0\n#--------------------------------------------------------------\nauto eth0\niface eth0 inet static" > interfaces


echo -e "Please enter the network informations into the /etc/network/interfaces file, complete them below :\n"

sed -n '1,10p' interfaces

printf "address " ; address_status
printf "network " ; network_status
printf "netmask " ; netmask_status
printf "broadcast " ; broadcast_status
printf "gateway " ; gateway_status

Nice touch - I'm a bit of a RegEx fan, and considering we are already matching with one I'm inclined to extend it perhaps like this:

fetchip() {

    local IPDIGIT="(25[0-5]|2[0-4][0-9]|[1]?[0-9]?[0-9])"
    printf "%s: " "$1"
    until 
        read
        [[ "$REPLY" =~ ^(${IPDIGIT}\.){3}${IPDIGIT}$ ]]
    do
      printf "Invalid IP address - Please re-enter\n%s: " "$1"
    done

    printf -v $1 "%s" "$REPLY"
}

Yuck, repeating the same code 4 times. At least you realize this is bad LOL.

better late than never :slight_smile: