How can I line break in ecal echo? in bash script - for-loop

I need a help, i need line brake in my "eval echo".
It is a code for adding multiple additional ip in my server.
code:
#!/bin/bash
echo enter all ip
vi textfile.txt
echo gateway
read g
echo netmask
read n
echo how many ip do you need
read t
for ((u=1;; u++));
do
read "d$u" || break;
done < textfile.txt
for i in `eval echo {1..$t}`
do
eval "echo DEVICE=eth0:$i\nIPADDR=\$d$i\nNETMASK=$n\nGATEWAY=$g\n" # > "ifcfg-eth0:$i";
done
output:
DEVICE=eth0:1nIPADDR=192.168.0.2nNETMASK=255.255.255.0nGATEWAY=192.168.0.1n
required output:
DEVICE=eth0:1
IPADDR=192.168.0.2
NETMASK=255.255.255.0
GATEWAY=192.168.0.1

Related

shell script to find dynamic public ip address in ubuntu does not show any output

I wrote the following script to find my dynamic public IP address and save how often it is changes
#!/usr/bin/env bash
ip=0
change=0
for ((count = 10000; count != 0, change == 10; count--)); do
fetch="$(dig +short myip.opendns.com #resolver1.opendns.com)"
dig +short myip.opendns.com #resolver1.opendns.com >>/home/nik/Desktop/file.txt
if [ $ip == 0 ]; then
ip=fetch
elif [ $ip != "$fetch" ]; then
change++
echo $ip
echo " changed to "
echo "$fetch"
echo " at "
echo date
else
echo ""
fi
echo "123"
sleep 13
(( count--))
done
I saved file as script.sh and changed it's executable permissions using
chmod +x script.sh
When I independently run dig command(in next line) or echo command directly in terminal, they log output to file without any problem
dig +short myip.opendns.com #resolver1.opendns.com>>/home/nik/Desktop/file.txt
but when I run the script, It shows no output nor does it log anything into text file.
I use Ubuntu 19.10 if it matters.
Edit: added shebang and changed wait to sleep
You have change=0 in the beginning of your file, and then depend on change == 10 in the conditional expression of your for loop.
I think you should review your code first :-)
A good place to start with a script that tracks public IP Address changes might be this guy:
#!/usr/bin/env bash
CURRENT_IP="$(timeout 5 dig +short myip.opendns.com #resolver1.opendns.com 2>/dev/null)"
num_changes=0
while [ 1 ]
do
NEW_IP="$(timeout 5 dig +short myip.opendns.com #resolver1.opendns.com 2>/dev/null)"
if echo "${CURRENT_IP}" | grep -q "${NEW_IP}"
then
echo "IP is the same" > /dev/null
else
let num_changes++
echo "${num_changes}: ${CURRENT_IP} -> ${NEW_IP}"
CURRENT_IP="${NEW_IP}"
fi
done
There are two variables being used, CURRENT_IP and NEW_IP
They are both being updated the same way timeout 5 dig +short ... 2>/dev/null
The timeout serves to ensure our script never hangs forever
The 2>/dev/null serves to filter error messages away
The num_changes variable keeps track of the number of times the IP changed
The only time this script will ever print any message at all is when your address changes
Example output: [NUM_CHANGES]: [LAST ADDRESS] -> [NEW ADDRESS]
1: 75.72.13.89 -> 74.76.77.88
2: 75.72.13.88 -> 74.76.77.87
3: 75.72.13.87 -> 74.76.77.86

BASH Script that loops thru 4 different text files

I'm trying to create a BASH script that takes data from four different text files and assigns each line from each of the text files to a variable and then run a command using those variables. I already have a for loop constructed to do this task but the challenge I'm having is the for loop isn't doing one on one mapping.
I want this loop to iterate thru every line and run the command once per line and stop the loop when it reaches the EOF. When I run this script, I get the output as follows;
The username is jsmith The hostname is 0000-Ubuntu The description is P-Ubuntu The IP is 0.0.0.0/24
The username is jsmith The hostname is 0000-Ubuntu The description is P-Ubuntu The IP is 1.1.1.1/24
The username is jsmith The hostname is 0000-Ubuntu The description is P-Ubuntu The IP is 2.2.2.2/24
I need this to be one one one mapped; for example:
The username is jsmith The hostname is 0000-Ubuntu The description is P-Ubuntu The IP is 0.0.0.0/24
The username is pstone The hostname is 1111-Ubuntu The description is P-Ubuntu The IP is 1.1.1.1/24
The username is drogers The hostname is 3333-Ubuntu The description is P-Ubuntu The IP is 3.3.3.3/24
and so on....
As you can tell, I'm a newbie in bash scripting and I'd really appreciate any guidance/help.
Thanks!
#!/bin/bash
HOSTNAMES=$(cat /home/squadri/hostnames.txt)
DESCRIPTIONS=$(cat /home/squadri/descriptions.txt)
USERNAMES=$(cat /home/squadri/usernames.txt)
IPS=$(cat /home/squadri/ips.txt)
for i in $HOSTNAMES ; do
for j in $DESCRIPTIONS ; do
for k in $USERNAMES ; do
for l in $IPS ; do
echo "The username is $k"
echo "The hostname is $i"
echo "The description is $j"
echo "The IP is $l"
done
done
done
done
You can use the paste command for that:
paste /home/squadri/{hostnames,descriptions,usernames,ips}.txt |\
while read hostname description username ip; do
echo "The username is $username"
echo "The hostname is $hostname"
echo "The description is $description"
echo "The IP is $ip"
done
One potential pitfall with this approach is whitespace characters in your files but it should work fine if there are none in any of the lines in your files.
Read from 4 different file descriptors:
while IFS= read -r hostname;
IFS= read -r description <&3;
IFS= read -r username <&4;
IFS= read -r ip <&5; do
echo "The username is $username"
echo "The hostname is $hostname"
echo "The description is $description"
echo "The IP is $ip"
done < hostnames.txt 3< descriptions.txt 4< usernames.txt 5< ips.txt
Technically, this reads until the end of descriptions.txt, regardless of the length of the other files. To stop when the shortest file ends, join the commands with &&:
while IFS= read -r hostname &&
IFS= read -r description <&3 &&
IFS= read -r username <&4 &&
IFS= read -r ip <&5; do
echo "The username is $username"
echo "The hostname is $hostname"
echo "The description is $description"
echo "The IP is $ip"
done < hostnames.txt 3< descriptions.txt 4< usernames.txt 5< ips.txt
Iterating until you reach the end of the longest file is a little more complex (you can't simply join the commands with ||), so I'll leave it as an exercise to the interested reader unless requested.

Variable scope in Bash [duplicate]

Please explain to me why the very last echo statement is blank? I expect that XCODE is incremented in the while loop to a value of 1:
#!/bin/bash
OUTPUT="name1 ip ip status" # normally output of another command with multi line output
if [ -z "$OUTPUT" ]
then
echo "Status WARN: No messages from SMcli"
exit $STATE_WARNING
else
echo "$OUTPUT"|while read NAME IP1 IP2 STATUS
do
if [ "$STATUS" != "Optimal" ]
then
echo "CRIT: $NAME - $STATUS"
echo $((++XCODE))
else
echo "OK: $NAME - $STATUS"
fi
done
fi
echo $XCODE
I've tried using the following statement instead of the ++XCODE method
XCODE=`expr $XCODE + 1`
and it too won't print outside of the while statement. I think I'm missing something about variable scope here, but the ol' man page isn't showing it to me.
Because you're piping into the while loop, a sub-shell is created to run the while loop.
Now this child process has its own copy of the environment and can't pass any
variables back to its parent (as in any unix process).
Therefore you'll need to restructure so that you're not piping into the loop.
Alternatively you could run in a function, for example, and echo the value you
want returned from the sub-process.
http://tldp.org/LDP/abs/html/subshells.html#SUBSHELL
The problem is that processes put together with a pipe are executed in subshells (and therefore have their own environment). Whatever happens within the while does not affect anything outside of the pipe.
Your specific example can be solved by rewriting the pipe to
while ... do ... done <<< "$OUTPUT"
or perhaps
while ... do ... done < <(echo "$OUTPUT")
This should work as well (because echo and while are in same subshell):
#!/bin/bash
cat /tmp/randomFile | (while read line
do
LINE="$LINE $line"
done && echo $LINE )
One more option:
#!/bin/bash
cat /some/file | while read line
do
var="abc"
echo $var | xsel -i -p # redirect stdin to the X primary selection
done
var=$(xsel -o -p) # redirect back to stdout
echo $var
EDIT:
Here, xsel is a requirement (install it).
Alternatively, you can use xclip:
xclip -i -selection clipboard
instead of
xsel -i -p
I got around this when I was making my own little du:
ls -l | sed '/total/d ; s/ */\t/g' | cut -f 5 |
( SUM=0; while read SIZE; do SUM=$(($SUM+$SIZE)); done; echo "$(($SUM/1024/1024/1024))GB" )
The point is that I make a subshell with ( ) containing my SUM variable and the while, but I pipe into the whole ( ) instead of into the while itself, which avoids the gotcha.
#!/bin/bash
OUTPUT="name1 ip ip status"
+export XCODE=0;
if [ -z "$OUTPUT" ]
----
echo "CRIT: $NAME - $STATUS"
- echo $((++XCODE))
+ export XCODE=$(( $XCODE + 1 ))
else
echo $XCODE
see if those changes help
Another option is to output the results into a file from the subshell and then read it in the parent shell. something like
#!/bin/bash
EXPORTFILE=/tmp/exportfile${RANDOM}
cat /tmp/randomFile | while read line
do
LINE="$LINE $line"
echo $LINE > $EXPORTFILE
done
LINE=$(cat $EXPORTFILE)

bash - Comparing variables

I am trying to do the following in bash:
get my external IP
read first line of a file
compare both values
if it is not the same, delete the file and recreate it with the current address
I really don't know why this fails, all my script does is to output my current address and the first line of the file (which by the way is simply "asd" for testing)
#!/bin/bash
IP= curl http://ipecho.net/plain
OLD= head -n 1 /Users/emse/Downloads/IP/IP.txt
if [ "$IP" = "$OLD" ]; then
exit
else
rm /Users/emse/Downloads/IP/IP.txt
$IP> /Users/emse/Downloads/IP/IP.txt
exit
fi
Some obvious problems in your script:
Don't put spaces on either side of equal sign if you want to do assignment
You want the output of curl, head so wrap them in backticks (`)
You want to write $IP into the file, not to execute the content of it as a command, so echo it
The script becomes:
#!/bin/bash
IP=`curl http://ipecho.net/plain`
OLD=`head -n 1 /Users/emse/Downloads/IP/IP.txt`
if [ "$IP" = "$OLD" ]; then
exit
else
rm /Users/emse/Downloads/IP/IP.txt
echo $IP > /Users/emse/Downloads/IP/IP.txt
exit
fi
Excellent answer qingbo, just a tad bit of refinement:
#!/bin/bash
IP=`curl http://ipecho.net/plain`
OLD=`head -n 1 /Users/emse/Downloads/IP/IP.txt`
if [ "$IP" != "$OLD" ]; then
echo $IP > /Users/emse/Downloads/IP/IP.txt # > creates/truncates/replaces IP.txt
fi

Bash variable scope

Please explain to me why the very last echo statement is blank? I expect that XCODE is incremented in the while loop to a value of 1:
#!/bin/bash
OUTPUT="name1 ip ip status" # normally output of another command with multi line output
if [ -z "$OUTPUT" ]
then
echo "Status WARN: No messages from SMcli"
exit $STATE_WARNING
else
echo "$OUTPUT"|while read NAME IP1 IP2 STATUS
do
if [ "$STATUS" != "Optimal" ]
then
echo "CRIT: $NAME - $STATUS"
echo $((++XCODE))
else
echo "OK: $NAME - $STATUS"
fi
done
fi
echo $XCODE
I've tried using the following statement instead of the ++XCODE method
XCODE=`expr $XCODE + 1`
and it too won't print outside of the while statement. I think I'm missing something about variable scope here, but the ol' man page isn't showing it to me.
Because you're piping into the while loop, a sub-shell is created to run the while loop.
Now this child process has its own copy of the environment and can't pass any
variables back to its parent (as in any unix process).
Therefore you'll need to restructure so that you're not piping into the loop.
Alternatively you could run in a function, for example, and echo the value you
want returned from the sub-process.
http://tldp.org/LDP/abs/html/subshells.html#SUBSHELL
The problem is that processes put together with a pipe are executed in subshells (and therefore have their own environment). Whatever happens within the while does not affect anything outside of the pipe.
Your specific example can be solved by rewriting the pipe to
while ... do ... done <<< "$OUTPUT"
or perhaps
while ... do ... done < <(echo "$OUTPUT")
This should work as well (because echo and while are in same subshell):
#!/bin/bash
cat /tmp/randomFile | (while read line
do
LINE="$LINE $line"
done && echo $LINE )
One more option:
#!/bin/bash
cat /some/file | while read line
do
var="abc"
echo $var | xsel -i -p # redirect stdin to the X primary selection
done
var=$(xsel -o -p) # redirect back to stdout
echo $var
EDIT:
Here, xsel is a requirement (install it).
Alternatively, you can use xclip:
xclip -i -selection clipboard
instead of
xsel -i -p
I got around this when I was making my own little du:
ls -l | sed '/total/d ; s/ */\t/g' | cut -f 5 |
( SUM=0; while read SIZE; do SUM=$(($SUM+$SIZE)); done; echo "$(($SUM/1024/1024/1024))GB" )
The point is that I make a subshell with ( ) containing my SUM variable and the while, but I pipe into the whole ( ) instead of into the while itself, which avoids the gotcha.
#!/bin/bash
OUTPUT="name1 ip ip status"
+export XCODE=0;
if [ -z "$OUTPUT" ]
----
echo "CRIT: $NAME - $STATUS"
- echo $((++XCODE))
+ export XCODE=$(( $XCODE + 1 ))
else
echo $XCODE
see if those changes help
Another option is to output the results into a file from the subshell and then read it in the parent shell. something like
#!/bin/bash
EXPORTFILE=/tmp/exportfile${RANDOM}
cat /tmp/randomFile | while read line
do
LINE="$LINE $line"
echo $LINE > $EXPORTFILE
done
LINE=$(cat $EXPORTFILE)

Resources