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}
}
Related
I had problem with cut variables from string in " quotes. I have some scripts to write for my sys classes, I had a problem with a script in which I had to read input from the user in the form of (a="var1", b="var2")
I tried the code below
#!/bin/bash
read input
a=$($input | cut -d '"' -f3)
echo $a
it returns me a error "not found a command" on line 3 I tried to double brackets like
a=$(($input | cut -d '"' -f3)
but it's still wrong.
In a comment the OP gave a working answer (should post it as an answer):
#!/bin/bash
read input
a=$(echo $input | cut -d '"' -f2)
b=$(echo $input | cut -d '"' -f4)
echo sum: $(( a + b))
echo difference: $(( a - b))
This will work for user input that is exactly like a="8", b="5".
Never trust input.
You might want to add the check
if [[ ${input} =~ ^[a-z]+=\"[0-9]+\",\ [a-z]+=\"[0-9]+\"$ ]]; then
echo "Use your code"
else
echo "Incorrect input"
fi
And when you add a check, you might want to execute the input (after replacing the comma with a semicolon).
input='testa="8", testb="5"'
if [[ ${input} =~ ^[a-z]+=\"[0-9]+\",\ [a-z]+=\"[0-9]+\"$ ]];
then
eval $(tr "," ";" <<< ${input})
set | grep -E "^test[ab]="
else
echo no
fi
EDIT:
#PesaThe commented correctly about BASH_REMATCH:
When you use bash and a test on the input you can use
if [[ ${input} =~ ^[a-z]+=\"([0-9]+)\",\ [a-z]+=\"([0-9])+\"$ ]];
then
a="${BASH_REMATCH[1]}"
b="${BASH_REMATCH[2]}"
fi
To extract the digit 1 from a string "var1" you would use a Bash substring replacement most likely:
$ s="var1"
$ echo "${s//[^0-9]/}"
1
Or,
$ a="${s//[^0-9]/}"
$ echo "$a"
1
This works by replacing any non digits in a string with nothing. Which works in your example with a single number field in the string but may not be what you need if you have multiple number fields:
$ s2="1 and a 2 and 3"
$ echo "${s2//[^0-9]/}"
123
In this case, you would use sed or grep awk or a Bash regex to capture the individual number fields and keep them distinct:
$ echo "$s2" | grep -o -E '[[:digit:]]+'
1
2
3
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}"
I have the next two regex in Bash:
1.^[-a-zA-Z0-9\,\.\;\:]*$
2.^[]a-zA-Z0-9\,\.\;\:]*$
The first matches when the string contains a "-" and the other values.
The second when contains a "]".
I put this values at the beginning of my regex because I can't scape them.
How I can get match the two values at the same time?
You can also place the - at the end of the bracket expression, since a range must be closed on both ends.
^[]a-zA-Z0-9,.;:-]*$
You don't have to escape any of the other characters, either. Colons, semicolons, and commas have no special meaning in any part of a regular expression, and while a period loses its special meaning inside a bracket expression.
Basically you can use this:
grep -E '^.*\-.*\[|\[.*\-.*$'
It matches either a - followed by zero or more arbitrary chars and a [ or a [ followed by zero or more chars and a -
However since you don't accept arbitrary chars, you need to change it to:
grep -E '^[a-zA-Z0-9,.;:]*\-[a-zA-Z0-9,.;:]*\[|\[[a-zA-Z0-9,.;:]*\-[a-zA-Z0-9,.;:]*$'
Maybe, this can help you
#!/bin/bash
while read p; do
echo $p | grep -E '\-.*\]|\].*\-' | grep "^[]a-zA-Z0-9,.;:-]*$"
done <$1
user-host:/tmp$ cat test
-i]string
]adfadfa-
string-
]string
str]ing
]123string
123string-
?????
++++++
user-host:/tmp$ ./test.sh test
-i]string
]adfadfa-
There are two questions in your post.
One is in the description:
How I can get match the two values at the same time?
That is an OR match, which could be done with a range that mix your two ranges:
pattern='^[]a-zA-Z0-9,.;:-]*$'
That will match a line that either contains one (or several) -…OR…]…OR any of the included characters. That would be all the lines (except ?????, ++++++ and as df gh) in the test script below.
Two is in the title:
… a string contains “-” and “]” at the same time
That is an AND match. The simplest (and slowest) way to do it is:
echo "$line" | grep '-' | grep ']' | grep '^[-a-zA-Z0-9,.;:]*$'
The first two calls to grep select only the lines that:
contain both (one or several) - and (one or several) ]
Test script:
#!/bin/bash
printlines(){
cat <<-\_test_lines_
asdfgh
asdfgh-
asdfgh]
as]df
as,df
as.df
as;df
as:df
as-df
as]]]df
as---df
asAS]]]DFdf
as123--456DF
as,.;:-df
as-dfg]h
as]dfg-h
a]s]d]f]g]h
a]s]d]f]g]h-
s-t-r-i-n-g]
as]df-gh
123]asdefgh
123asd-fgh-
?????
++++++
as df gh
_test_lines_
}
pattern='^[]a-zA-Z0-9,.;:-]*$'
printf '%s\n' "Testing the simple pattern of $pattern"
while read line; do
resultgrep="$( echo "$line" | grep "$pattern" )"
printf '%13s %-13s\n' "$line" "$resultgrep"
done < <(printlines)
echo "#############################################################"
echo
p1='-'; p2=']'; p3='^[]a-zA-Z0-9,.;:-]*$'
printf '%s\n' "Testing a 'grep AND' of '$p1', '$p2' and '$p3'."
while read line; do
resultgrep="$( echo "$line" | grep "$p1" | grep "$p2" | grep "$p3" )"
[[ $resultgrep ]] && printf '%13s %-13s\n' "$line" "$resultgrep"
done < <(printlines)
echo "#############################################################"
echo
printf '%s\n' "Testing an 'AWK AND' of '$p1', '$p2' and '$p3'."
while read line; do
resultawk="$( echo "$line" |
awk -v p1="$p1" -v p2="$p2" -v p3="$p3" '$0~p1 && $0~p2 && $0~p3' )"
[[ $resultawk ]] && printf '%13s %-13s\n' "$line" "$resultawk"
done < <(printlines)
echo "#############################################################"
echo
printf '%s\n' "Testing a 'bash AND' of '$p1', '$p2' and '$p3'."
while read line; do
rgrep="$( echo "$line" | grep "$p1" | grep "$p2" | grep "$p3" )"
[[ ( $line =~ $p1 ) && ( $line =~ $p2 ) && ( $line =~ $p3 ) ]]
rbash=${BASH_REMATCH[0]}
[[ $rbash ]] && printf '%13s %-13s %-13s\n' "$line" "$rgrep" "$rbash"
done < <(printlines)
echo "#############################################################"
echo
I'm trying to make a simple bash script that will iterate through a text file containing IP addresses,
ping them one time, and see if they are alive or not.
This is my work so far:
#!/bin/bash
for ip in $(cat ips.txt); do
if [[ "1" == "$(ping -c 1 $ip | grep 'packets transmitted' | cut -d ' ' -f 4)"]]
echo $ip
fi
done
Any Suggestions?
Thanks!
This seems to work:
#!/bin/bash
for ip in $(cat ips.txt); do
if [ "1" == "$(ping -c 1 $ip | grep 'packets transmitted' | cut -d ' ' -f 4)" ]; then
echo $ip
fi
done
You needed the ; then after the if [ ... ] statement (same thing goes for elif, not else), and a space between the last bracket of the statement and the statement's contents. Also this appears to work fine with just single brackets, and this may be more portable (see here).
Works on Bash 4.2.47
Yes. You can use a newline instead of ; if you like, but you always need the then keyword.
if [ "1" == "$(ping -c 1 $ip | grep 'packets transmitted' | cut -d ' ' -f 4)" ]
then echo $ip
fi
# or
if [ "1" == "$(ping -c 1 $ip | grep 'packets transmitted' | cut -d ' ' -f 4)" ]
then
echo $ip
fi
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'