I am attempting to gather my ip address and subnet bits (such as 192.168.2.17/24) to pass to fing along with a name to scan a network and create a set of output files. I wrote a working script that takes in the address/len and a job name. I ultimately want to be able to extend the shell script automatically pull the address/len so that I only need to enter the job name. I'm using a Mac, if that helps.
fing.sh (This works!)
#Create a fing profile and scan
mkdir $2
fing -n $1 -r 3 -d false --session $2/persist.fing \
-o table,html,$2/fing.html -o table,csv,$2/fing.csv \
-o table,xml,$2/fing.xml -o table,json,$2/fing.json
test.sh
#!/bin/bash
#Variables
ipaddr=ip
hexmask=netmask
testmask=255.255.252.0
#thing=0
The ip and netmask functions work; at least they seem to.
#Functions
ip()
{
ifconfig | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}'
}
netmask()
{
ifconfig | grep "inet " | grep -v 127.0.0.1 | awk '{print $4}'
}
The functions, mask2cidr and hex2decip to work as individual scripts.
mask2cidr() {
#Source:
#http://www.linuxquestions.org/questions/programming-9/bash-cidr-calculator-646701/#post3173472
#Convert dotted decimal subnet mask to cidr format
nbits=0
IFS=.
for dec in $1 ; do
case $dec in
255) let nbits+=8;;
254) let nbits+=7;;
252) let nbits+=6;;
248) let nbits+=5;;
240) let nbits+=4;;
224) let nbits+=3;;
192) let nbits+=2;;
128) let nbits+=1;;
0);;
*) echo "Error: $dec is not recognised"; exit 1
esac
done
echo "$nbits"
}
hex2decip()
{
#Source:
#https://forums.freebsd.org/threads/ifconfig-display-non-hex-netmasks.2834/#post-86216
#Converts hex formatted subnet to dotted decimal format
if [ ! "$1" ] ; then
echo
echo "$MyName - converts an IP address in hexadecimal to dotted decimal"
echo "Usage: $MyName <hex_address>"
echo
exit 1
fi
echo $1 | sed 's/0x// ; s/../& /g' | tr [:lower:] [:upper:] | while read B1 B2 B3 B4 ; do
echo "ibase=16;$B1;$B2;$B3;$B4" | bc | tr '\n' . | sed 's/\.$//'
done
}
ipaddr and hexmask output as expected
${ipaddr}
${hexmask}
For hex2decip, hexmask seems to pass "netmask" instead of the result of the netmask function.
hex2decip $hexmask
exit 0
./test.sh OUTPUT
10.0.180.14
0xffffff00
(standard_in) 1: illegal character: N
(standard_in) 1: illegal character: T
(standard_in) 1: illegal character: M
(standard_in) 1: illegal character: S
(standard_in) 1: illegal character: K
hexmask=netmask
Sets the shell variable hexmask to the string netmask. That's just how the shell works.
If you want the result of calling the shell function netmask, you need to use command substitution:
hexmask=$(netmask)
(after first defining the netmask function, of course.)
By the way,
ipaddr=ip
testmask=255.255.252.0
work the same way. ipaddr is set to the string ip and testmask to the string 255.255.252.0.
Related
I want to compare a group of words (individuals) in pairs and extract the one with the lowest numeric variable. My files and scripts are made this way.
Relatedness_3rdDegree.txt (example):
Individual1 Individual2
Individual5 Individual23
Individual50 Individual65
filename.imiss
INDV N_DATA N_GENOTYPES_FILTERED N_MISS F_MISS
Individual1 375029 0 782 0.00208517
Individual2 375029 0 341 0.000909263
Individual3 375029 0 341 0.000909263
Main script:
numlines=$(wc -l Relatedness_3rdDegree.txt|awk '{print $1}')
for line in `seq 1 $numlines`
do
ind1=$(sed -n "${line}p" Relatedness_3rdDegree.txt|awk '{print $1}')
ind2=$(sed -n "${line}p" Relatedness_3rdDegree.txt|awk '{print $2}')
miss1=$(grep $ind1 filename.imiss|awk '{print $5}')
miss2=$(grep $ind2 filename.imiss|awk '{print $5}')
if echo "$miss1 > $miss2" | bc -l | grep -q 1
then
echo $ind1 >> miss.txt
else
echo $ind2 >> miss.txt
fi
echo "$line / $numlines"
done
This last script will echo a series of line like this :
1 / 208
2 / 208
3 / 208
and so on, until getting to this error:
91 / 208
(standard_in) 1: syntax error
92 / 208
(standard_in) 1: syntax error
93 / 208
If I go to my output (miss.txt), the printed individuals are not correct.
It should print the individuals, within the pairs contained in the file "Relatedness_3rdDegree.txt", that have the lowest value of F_MISS (column $5 of the "filename.imiss").
For instance, in the pair "Individual1 Individual2", it should compare their values of F_MISS and print only the individual with the lowest value, which in this example would be Individual 2.
I have manually checked the values and the printed individual, and it looks like it printed random individuals per each pair.
What is wrong in this script?
Bash version:
#!/bin/bash
declare -A imiss
while read -r ind nd ngf nm fm # we'll ignore most of these
do
imiss[$ind]=$fm
done < filename.imiss
while read -r i1 i2
do
if (( $(echo "${imiss[$i1]} > ${imiss[$i2]}" | bc -l) ))
then
echo "$i1"
else
echo "$i2"
fi
done < Relatedness_3rdDegree.txt
Run* it like:
bash-imiss
AWK version:
#!/usr/bin/awk -f
NR == FNR {imiss[$1] = $5; next}
{
if (imiss[$1] > imiss[$2]) {
print $1
} else {
print $2
}
}
Run* it like:
awk-imiss filename.imiss Relatedness_3rdDegree.txt
These two scripts do exactly the same thing in exactly the same way using associative arrays.
* This assumes that you have set the script file executable using chmod and that it's in your PATH and that the data files are in your current directory.
Hello i need help with one script that its on Solaris system:
I will explain the script analytically:
i have these files :
i)
cat /tmp/BadTransactions/TRANSACTIONS_DAILY_20180730.txt
201807300000000004
201807300000000005
201807300000000006
201807300000000007
201807300000000008
201807200002056422
201807230003099849
201807230003958306
201806290003097219
201806080001062012
201806110001633519
201806110001675603
ii)
cat /tmp/BadTransactions/test_data_for_validation_script.txt
20180720|201807200002056422||57413620344272|030341-213T |580463|WIRE||EUR|EUR|20180720|20180720|||||||00000000000019.90|00000000000019.90|Debit||||||||||MPA|||574000|129|||||||||||||||||||||||||31313001103712|BFNJKL|K| I P P BONNIER PUBLICATIO|||FI|PERS7
20180723|201807230003099849||57100440165173|140197-216U|593619|WIRE||EUR|EUR|20180723|20180723|||||||00000000000060.00|00000000000060.00|Debit||||||||||MPA|||571004|106|||||||||||||||||||||||||57108320141339|Ura Basket / UraNaiset|||-div|||FI|PERS
20180723|201807230003958306||57206820079775|210489-0788|593619|WIRE||EUR|EUR|20180721|20180723|||||||00000000000046.00|00000000000046.00|Debit||||||||||MPA|||578800|106|||||||||||||||||||||||||18053000009026|IC Kodit||| c/o Newsec Asset Manag|||FI|PERS
20180629|201806290003097219||57206820079775|210489-0788|593619|WIRE||EUR|EUR|20180628|20180629|||||||00000000000856.00|00000000000856.00|Debit||||||||||MPA|||578800|106|||||||||||||||||||||||||18053000009018|IC Kodit||| c/o Newsec Asset Manag|||FI|PERS
20180608|201806080001062012||57206820079441|140197-216S|580463|WIRE||EUR|EUR|20180608|20180608|||||||00000000000019.90|00000000000019.90|Debit||||||||||MPA|||541002|129|||||||||||||||||||||||||57108320141339|N FN|K| IKI I P BONNIER PUBLICATION|||FI|PERS7
20180611|201806110001633519||57206820079525|140197-216B|593619|WIRE||EUR|EUR|20180611|20180611|||||||00000000000242.10|00000000000242.10|Debit||||||||||MPA|||535806|106|||||||||||||||||||||||||57108320141339|As Oy Haikkoonsilta|| mannerheimin|||FI|PERS9
20180611|201806110001675603||57206820079092|140197-216Z|580463|WIRE||EUR|EUR|20180611|20180611|||||||00000000000019.90|00000000000019.90|Debit||||||||||MPA|||536501|129|||||||||||||||||||||||||57108320141339|N ^NLKL|K| I P NJ BONNIER PUBLICAT|||FI|PERS7
The script has to check each line of the
/tmp/BadTransactions/TRANSACTIONS_DAILY_20180730.txt and if the strings are on
the /tmp/BadTransactions/test_data_for_validation_script.txt it will create a
new file `/tmp/BadTransactions/TRANSACTIONS_DAILY_NEW_20180730.txt
From this new file it will count all the " | " in each line and if its more than 64 it will delete the " | " in 61th posistion of the line . This will be continued until its line has 64 pipes.
For example if one line has 67 " | " it will delete the 61th , then it will check it again and now has 66 " | | so it will delete the 61th " | " , etc... until it reach 64 pipes.So all the line have to have 64th " | ".
Here is my code , but in this code i have managed to delete only the 61th pipe in each line , i cannot make the loop so that it will check each line until it reach the 64 pipes.
I will appreciate it if you could help me.
#!/bin/bash
PATH=/usr/xpg4/bin:/bin:/usr/bin
while read line
do
grep "$line" /tmp/BadTransactions/test_data_for_validation_script.txt
awk 'NR==FNR { K[$1]; next } ($2 in K)' /tmp/BadTransactions/TRANSACTIONS_DAILY_20180730.txt FS="|" /opt/NorkomC
onfigS2/inbox/TRANSACTIONS_DAILY_20180730.txt > /tmp/BadTransactions/TRANSACTIONS_DAILY_NEW_20180730.txt
sed '/\([^|]*[|]\)\{65\}/ s/|//61' /tmp/BadTransactions/TRANSACTIONS_DAILY_NEW_20180730.txt
done < /tmp/BadTransactions/TRANSACTIONS_DAILY_20180730.txt > /tmp/BadTransactions/TRANSACTIONS_DAILY_NEW_201807
30.txt
Ok, in this problem you have several pieces of code.
You need to read a file line by line
Check each line against another file
Examine the matching line for the occurrences of "|"
Delete recursively the 61st "|" until the string will remain with 64 of them
You could do something like this
#!/bin/bash
count() { ### We will use this to count how many pipes are there
string="${1}"; shift
char="${1}"
printf "%s" "${string}" | grep -o -e "${char}" | grep -c .
}
file1="/tmp/BadTransactions/TRANSACTIONS_DAILY_20180730.txt" ### File to read
file2="/tmp/BadTransactions/test_data_for_validation_script.txt" ### File to check for duplicates
file3="/tmp/BadTransactions/TRANSACTIONS_DAILY_NEW_20180730.txt" ### File where to save our final work
printf "" > "${file3}" ### Delete (eventual) history
exec 3<"${file1}" ### Put our data in file descriptor 3
while read -r line <&3; do ### read each line and put it in var "$line"
string="$(grep -e "${line}" "${file2}")" ### Check the line against second file
while [ "$(count "${string}" "|")" -gt 64 ]; do ### While we have more than 64 "|"
string="$(printf "%s" "${string}" | sed -e "s/|//61")" ### Delete the 61st occurrence
done
printf "%s" "${string}" >> "${file3}" ### Save the correct line in the third file
done
exec 3>&- ### Clean file descriptor 3
This is not tested, but should work.
N.B. Please note that I am giving for granted that grep will return only one occurrence from second file...
If it is not your case you have to manually check each value with something like:
for value in $(grep -e "${line}" "${file2}"); do
...
done
EDIT:
For systems like Solaris or others that doesn't have GNU grep installed you can substitute the count method as follow:
count() {
string="${1}"; shift
char="${1}"
printf "%s" "${string}" | awk -F"${char}" '{print NF-1}'
}
This question was recently asked in an interview.
Question: Write a bash script to subdivide an given subnet into a pre-defined number of smaller subnets.
After division IP addresses shouldn't be wasted, i.e. accumulation of your subdivisions should make up the divided subnet.
Every subnet has 3 IP addresses reserved and not usable by hosts: network, broadcast, gateway.
Show network/broadcast address, number of hosts and assign gateway. Gateway should be first IP available in divided subnet. Examples:
INPUT: ./subnetter.sh 192.168.0.0/24 3
OUTPUT:
subnet=192.168.0.0/25 network=192.168.0.0 broadcast=192.168.0.127 gateway=192.168.0.1 hosts=125
subnet=192.168.0.128/26 network=192.168.0.128 broadcast=192.168.0.191 gateway=192.168.0.129 hosts=61
subnet=192.168.0.192/26 network=192.168.0.192 broadcast=192.168.0.255 gateway=192.168.0.193 hosts=61
INPUT: ./subnetter.sh 192.168.0.0/24 4
OUTPUT:
subnet=192.168.0.0/26 network=192.168.0.0 broadcast=192.168.0.63 gateway=192.168.0.1 hosts=61
subnet=192.168.0.64/26 network=192.168.0.64 broadcast=192.168.0.127 gateway=192.168.0.65 hosts=61
subnet=192.168.0.128/26 network=192.168.0.128 broadcast=192.168.0.191 gateway=192.168.0.129 hosts=61
subnet=192.168.0.192/26 network=192.168.0.192 broadcast=192.168.0.255 gateway=192.168.0.193 hosts=61
INPUT: ./subnetter.sh 10.55.10.64/28 2
OUTPUT:
subnet=10.55.10.64/29 network=10.55.10.64 broadcast=10.55.10.71 gateway=10.55.10.65 hosts=5
subnet=10.55.10.72/29 network=10.55.10.72 broadcast=10.55.10.79 gateway=10.55.10.73 hosts=5
First of all, I am trying to analyse what logic is used to divide the subnets.
Secondly, I am trying to use the ipcalc command to get outputs but no luck.
Thanks
Here is the bash script, I have tried my hands on:
Should work fine. The script takes 2 arguments, CIDR block and number of subnets to divide into.
#!/bin/bash
cidr=$1
total_subnet=$2
nw_addr=`echo $cidr | awk -F'/' '{ print $1 }'` # retrieving network IP from input 1
nw_mask=`echo $cidr | awk -F'/' '{ print $2 }'` # retrieving network mask from input 1
dbit=`echo $nw_addr | awk -F'.' '{ print $4 }'` # retrieving the D-bit from network ( A.B.C.D )
significant_bit=`echo $nw_addr | awk -F'.' 'BEGIN {OFS = ""}{print $1,".",$2,".",$3 }'` # retrieving A.B.C bits from n/w address
change_bit=$(( 32 - $nw_mask))
max_addr=$(( 2 ** $change_bit)) # calculating maximum addresses available in the network that can be subdivided
dbit_max=$dbit
# A Funtion to calculate the least power of 2 that is equal or greater than the argument
least_greater_power_of_two()
{
next_power=2
power=1
subnet_range=$1
while [ $next_power -lt $subnet_range ]; do
power=$(($power+1))
next_power=$(( 2 ** $power))
done
}
#initialising Loop Variables
remaining_addr=$max_addr
max_subnet_dbit=$dbit
total_subnet_addr=0
starting_dbit=$dbit
i=$total_subnet
while [ $i -gt 0 ]; do
starting_dbit=$(( $starting_dbit + $total_subnet_addr )) #Finding the starting D bit of the subnet
#finding the total number of addresses in the subnet
subnet_range=$(( $remaining_addr / $i ))
least_greater_power_of_two $subnet_range
total_subnet_addr=$(( 2 ** $power ))
max_subnet_dbit=$(( $max_subnet_dbit + $total_subnet_addr ))
remaining_addr=$(( $remaining_addr - $total_subnet_addr )) # Remaining addresses left to be assigned to the other subnets
last_dbit=$(( $max_subnet_dbit - 1)) #calculating last D bit in the subnet range
subnet_mask=$(( $change_bit - $power + $nw_mask )) #calculating the subnet mask
gateway_dbit=$(( $starting_dbit + 1 )) # calculating the Gateway D bit
total_hosts=$(( $total_subnet_addr - 3 )) # calculating the Total-hosts in the network
echo "Subnet= $significant_bit.$starting_dbit/$subnet_mask Network=$significant_bit.$starting_dbit Broadcast=$significant_bit.$last_dbit Gateway= $significant_bit.$gateway_dbit Hosts=$total_hosts"
i=$(($i-1)) # updating loop variable
done
I believe I have done 70% of the stuff that you require off the script. Since you were using ipcalc, dediced to use a similar binary. To start off, do the following:
yum install sipcalc if you have a RPM based OS or apt-get install sipcalc depending on the distro of your Linux OS.
Then write the following script and save it as subnetter.sh and give it 'x' permissions so that it can be executed.
#!/bin/bash
if [ $# == 0 ]; then
echo "Usage: ./subnetter.sh IP/SUBNET RANGE"
exit
fi
subnet=$1
network=`echo $subnet | cut -d / -f1`
broadcast=`/usr/bin/sipcalc $1 | grep -i broadcast | cut -d '-' -f2`
gateway=`/usr/bin/sipcalc $1 | grep -i usable | awk '{print $4}'`
hosts=`/usr/bin/sipcalc $1 | grep -i addresses | cut -d '-' -f2`
echo "subnet =" $subnet "network =" $network "broadcast =" $broadcast "gateway =" $gateway "hosts =" $hosts
Output of my script:
[root#puppet ~]# ./subnetter.sh 192.168.0.0/24
subnet = 192.168.0.0/24 network = 192.168.0.0 broadcast = 192.168.0.255 gateway = 192.168.0.1 hosts = 256
Please note that the requirement of the third argument is very simple and can be simply done using a for loop. I expect you to do that.
You can use the following tool to make sure that your output is correct: http://www.subnet-calculator.com/subnet.php?net_class=C
I have gone through the above requirment and below is what i have programmed to achieve the result .
Python Code integrated with the above shell script to achieve the result as users are expecting
Below code will create sub subnet for the existing subnet and then will call the shell script to perform the operation in loop and will provide records based on the users request.
#
from netaddr import *
import ipaddress
import csv
import subprocess
import os
import shlex
import sys
import numpy as np
from itertools import islice
if os.path.exists('hst.txt'):
os.remove('hst.txt')
else:
print("Sorry, I can not remove " )
if os.path.exists('hst_refined.txt'):
os.remove('hst_refined.txt')
else:
print("Sorry, I can not remove " )
fd = open('store_subnet',"w")
enter_subnet = raw_input('Enter subnet please: ')
fd.write(enter_subnet)
fd = open('store_sub_subnet',"w")
sub_subnet = raw_input('Enter nested_subet_count: ')
fd.write(sub_subnet)
ip=ipaddress.ip_network(unicode(enter_subnet))
list_demo = list(ip.subnets(int(sub_subnet)))
for i in list_demo:
hs = open("hst.txt","a")
hs.write(str(i))
hs.write("\n")
hs.close()
p = subprocess.Popen([ "/home/ramkumar5/network_cal/report_demo.sh" ], stdout=subprocess.PIPE).communicate()[0]
for i in p:
hs = open("hst_refined.txt","a")
hs.write(i)
hs.close()
print(sub_subnet)
records_req = raw_input('Enter Number of Records needed: ')
f=open("hst_refined.txt")
for i in xrange(int(records_req)):
line=f.next().strip()
print line
f.close()
#
#
Code2
#!/bin/bash
for res in `cat hst.txt` ; do
subnet=$res
network=`echo $subnet | cut -d / -f1`
broadcast=`/usr/bin/sipcalc $res | grep -i broadcast | cut -d '-' -f2`
gateway=`/usr/bin/sipcalc $res | grep -i usable | awk '{print $4}'`
hosts=`/usr/bin/sipcalc $res | grep -i addresses | cut -d '-' -f2`
echo "subnet =" $subnet "network =" $network "broadcast =" $broadcast "gateway =" $gateway "hosts =" $hosts
done
#
Sample Out from the result###
How do you get the length of a string stored in a variable and assign that to another variable?
myvar="some string"
echo ${#myvar}
# 11
How do you set another variable to the output 11?
To get the length of a string stored in a variable, say:
myvar="some string"
size=${#myvar}
To confirm it was properly saved, echo it:
$ echo "$size"
11
Edit 2023-02-13: Use of printf %n instead of locales...
UTF-8 string length
In addition to fedorqui's correct answer, I would like to show the difference between string length and byte length:
myvar='Généralités'
chrlen=${#myvar}
oLang=$LANG oLcAll=$LC_ALL
LANG=C LC_ALL=C
bytlen=${#myvar}
LANG=$oLang LC_ALL=$oLcAll
printf "%s is %d char len, but %d bytes len.\n" "${myvar}" $chrlen $bytlen
will render:
Généralités is 11 char len, but 14 bytes len.
you could even have a look at stored chars:
myvar='Généralités'
chrlen=${#myvar}
oLang=$LANG oLcAll=$LC_ALL
LANG=C LC_ALL=C
bytlen=${#myvar}
printf -v myreal "%q" "$myvar"
LANG=$oLang LC_ALL=$oLcAll
printf "%s has %d chars, %d bytes: (%s).\n" "${myvar}" $chrlen $bytlen "$myreal"
will answer:
Généralités has 11 chars, 14 bytes: ($'G\303\251n\303\251ralit\303\251s').
Nota: According to Isabell Cowan's comment, I've added setting to $LC_ALL along with $LANG.
Same, but without having to play with locales
I recently learn %n format of printf command (builtin):
myvar='Généralités'
chrlen=${#myvar}
printf -v _ %s%n "$myvar" bytlen
printf "%s is %d char len, but %d bytes len.\n" "${myvar}" $chrlen $bytlen
Généralités is 11 char len, but 14 bytes len.
Syntax is a little counter-intuitive, but this is very efficient! (further function strU8DiffLen is about 2 time quicker by using printf than previous version using local LANG=C.)
Length of an argument, working sample
Argument work same as regular variables
showStrLen() {
local -i chrlen=${#1} bytlen
printf -v _ %s%n "$1" bytlen
LANG=$oLang LC_ALL=$oLcAll
printf "String '%s' is %d bytes, but %d chars len: %q.\n" "$1" $bytlen $chrlen "$1"
}
will work as
showStrLen théorème
String 'théorème' is 10 bytes, but 8 chars len: $'th\303\251or\303\250me'
Useful printf correction tool:
If you:
for string in Généralités Language Théorème Février "Left: ←" "Yin Yang ☯";do
printf " - %-14s is %2d char length\n" "'$string'" ${#string}
done
- 'Généralités' is 11 char length
- 'Language' is 8 char length
- 'Théorème' is 8 char length
- 'Février' is 7 char length
- 'Left: ←' is 7 char length
- 'Yin Yang ☯' is 10 char length
Not really pretty output!
For this, here is a little function:
strU8DiffLen() {
local -i bytlen
printf -v _ %s%n "$1" bytlen
return $(( bytlen - ${#1} ))
}
or written in one line:
strU8DiffLen() { local -i _bl;printf -v _ %s%n "$1" _bl;return $((_bl-${#1}));}
Then now:
for string in Généralités Language Théorème Février "Left: ←" "Yin Yang ☯";do
strU8DiffLen "$string"
printf " - %-$((14+$?))s is %2d chars length, but uses %2d bytes\n" \
"'$string'" ${#string} $((${#string}+$?))
done
- 'Généralités' is 11 chars length, but uses 14 bytes
- 'Language' is 8 chars length, but uses 8 bytes
- 'Théorème' is 8 chars length, but uses 10 bytes
- 'Février' is 7 chars length, but uses 8 bytes
- 'Left: ←' is 7 chars length, but uses 9 bytes
- 'Yin Yang ☯' is 10 chars length, but uses 12 bytes
Unfortunely, this is not perfect!
But there left some strange UTF-8 behaviour, like double-spaced chars, zero spaced chars, reverse deplacement and other that could not be as simple...
Have a look at diffU8test.sh or diffU8test.sh.txt for more limitations.
I wanted the simplest case, finally this is a result:
echo -n 'Tell me the length of this sentence.' | wc -m;
36
You can use:
MYSTRING="abc123"
MYLENGTH=$(printf "%s" "$MYSTRING" | wc -c)
wc -c or wc --bytes for byte counts = Unicode characters are counted with 2, 3 or more bytes.
wc -m or wc --chars for character counts = Unicode characters are counted single until they use more bytes.
In response to the post starting:
If you want to use this with command line or function arguments...
with the code:
size=${#1}
There might be the case where you just want to check for a zero length argument and have no need to store a variable. I believe you can use this sort of syntax:
if [ -z "$1" ]; then
#zero length argument
else
#non-zero length
fi
See GNU and wooledge for a more complete list of Bash conditional expressions.
If you want to use this with command line or function arguments, make sure you use size=${#1} instead of size=${#$1}. The second one may be more instinctual but is incorrect syntax.
Using your example provided
#KISS (Keep it simple stupid)
size=${#myvar}
echo $size
Here is couple of ways to calculate length of variable :
echo ${#VAR}
echo -n $VAR | wc -m
echo -n $VAR | wc -c
printf $VAR | wc -m
expr length $VAR
expr $VAR : '.*'
and to set the result in another variable just assign above command with back quote into another variable as following:
otherVar=`echo -n $VAR | wc -m`
echo $otherVar
http://techopsbook.blogspot.in/2017/09/how-to-find-length-of-string-variable.html
I know that the Q and A's are old enough, but today I faced this task for first time. Usually I used the ${#var} combination, but it fails with unicode: most text I process with the bash is in Cyrillic...
Based on #atesin's answer, I made short (and ready to be more shortened) function which may be usable for scripting. That was a task which led me to this question: to show some message of variable length in pseudo-graphics box. So, here it is:
$ cat draw_border.sh
#!/bin/sh
#based on https://stackoverflow.com/questions/17368067/length-of-string-in-bash
border()
{
local BPAR="$1"
local BPLEN=`echo $BPAR|wc -m`
local OUTLINE=\|\ "$1"\ \|
# line below based on https://www.cyberciti.biz/faq/repeat-a-character-in-bash-script-under-linux-unix/
# comment of Bit Twiddler Jun 5, 2021 # 8:47
local OUTBORDER=\+`head -c $(($BPLEN+1))</dev/zero|tr '\0' '-'`\+
echo $OUTBORDER
echo $OUTLINE
echo $OUTBORDER
}
border "Généralités"
border 'А вот еще одна '$LESSCLOSE' '
border "pure ENGLISH"
And what this sample produces:
$ draw_border.sh
+-------------+
| Généralités |
+-------------+
+----------------------------------+
| А вот еще одна /usr/bin/lesspipe |
+----------------------------------+
+--------------+
| pure ENGLISH |
+--------------+
First example (in French?) was taken from someone's example above.
Second one combines Cyrillic and the value of some variable. Third one is self-explaining: only 1s 1/2 of ASCII chars.
I used echo $BPAR|wc -m instead of printf ... in order to not rely on if the printf is buillt-in or not.
Above I saw talks about trailing newline and -n parameter for echo. I did not used it, thus I add only one to the $BPLEN. Should I use -n, I must add 2.
To explain the difference between wc -m and wc -c, see the same script with only one minor change: -m was replaced with -c
$ draw_border.sh
+----------------+
| Généralités |
+----------------+
+---------------------------------------------+
| А вот еще одна /usr/bin/lesspipe |
+---------------------------------------------+
+--------------+
| pure ENGLISH |
+--------------+
Accented characters in Latin, and most of characters in Cyrillic are two-byte, thus the length of drawn horizontals are greater than the real length of the message.
Hope, it will save some one some time :-)
p.s. Russian text says "here is one more"
p.p.s. Working "two-liner"
#!/bin/sh
#based on https://stackoverflow.com/questions/17368067/length-of-string-in-bash
border()
{
# line below based on https://www.cyberciti.biz/faq/repeat-a-character-in-bash-script-under-linux-unix/
# comment of Bit Twiddler Jun 5, 2021 # 8:47
local OUTBORDER=\+`head -c $(( $(echo "$1"|wc -m) +1))</dev/zero|tr '\0' '-'`\+
echo $OUTBORDER"\n"\|\ "$1"\ \|"\n"$OUTBORDER
}
border "Généralités"
border 'А вот еще одна '$LESSCLOSE' '
border "pure ENGLISH"
In order to not clutter the code with repetitive OUTBORDER's drawing, I put the forming of OUTBORDER into separate command
Maybe just use wc -c to count the number of characters:
myvar="Hello, I am a string."
echo -n $myvar | wc -c
Result:
21
Length of string in bash
str="Welcome to Stackoveflow"
length=`expr length "$str"`
echo "Length of '$str' is $length"
OUTPUT
Length of 'Welcome to Stackoveflow' is 23
I am working on a script which does subnet calculations. So far, it looks like this (in part):
echo "Subnet Address : "$sn1.$sn2.$sn3.$sn4
echo "BCast Address : "$br1.$br2.$br3.$br4
echo -e "\nSubnet address in binary" :
echo "obase=2;$ip1"+"obase=2;$ip2"+"obase=2;$ip3"+"obase=2;$ip4" \
| bc | awk '{printf("%08d",$ip1)}'
echo -e "\nBroadcast address in binary" :
echo "obase=2;$br1"+"obase=2;$br2"+"obase=2;$br3"+"obase=2;$br4" \
| bc | awk '{printf("%08d",$br1)}'
which gives me this output:
Subnet address in binary :
11000010101010100000001100000000
Broadcast address in binary :
11000010101010100000001100011111
I tried '{printf("%08d.",$br1)}' and '{printf(".%08d",$br1)}' to separate the octets but I get an extra dot on the beginning or at the end.
I want to calculate how many aces the netmask has, but I really cant find a way to get the output of the echo "obase=2;$br1"+"obase=2;$br2"+"obase=2;$br3"+"obase=2;$br4"| bc | awk to a string so I can count them.
Any suggestions?
So you want the output to be in the format of: 01111111.00000000.00000000.00000001?
Well, the cheat method, I used when I banged my head against this was to wrap around ipcalc:
%ipcalc 127.0.0.1
Address: 127.0.0.1 01111111.00000000.00000000. 00000001
Netmask: 255.255.255.0 = 24 11111111.11111111.11111111. 00000000
Wildcard: 0.0.0.255 00000000.00000000.00000000. 11111111
=>
Network: 127.0.0.0/24 01111111.00000000.00000000. 00000000
HostMin: 127.0.0.1 01111111.00000000.00000000. 00000001
HostMax: 127.0.0.254 01111111.00000000.00000000. 11111110
Broadcast: 127.0.0.255 01111111.00000000.00000000. 11111111
Hosts/Net: 254 Class A, Loopback
And then extract what I needed. Runs MUCH faster than parsing multiple times through 'bc'. Ie, no sense re-inventing the wheel if you don't need to.
If you do feel like re-inventing the wheel a bit:
$ echo "obase=2;200" + "obase=2;150" + "obase=2;200" + "obase=2;150" | \
bc | awk '{printf "%08d\." ,$1}' | \
sed -e 's/[.]*$//'
11001010.10011000.11001010.10010110
That will get you the 8 digit binary output format you want.
To separate the octets I would use printf and paste instead, e.g.:
ip1=127; ip2=0; ip3=0; ip4=1
printf "%08d\n" $(bc <<<"obase=2; $ip1; $ip2; $ip3; $ip4") | paste -sd.
Or if the ip is in one variable:
ip=127.0.0.1
printf "%08d\n" $(bc <<<"obase=2; ${ip//./;}") | paste -sd.
Output:
00001010.00101010.01100110.11111100
To count the number of 1s in a netmask just add them:
netmask=255.255.255.252
printf "%08d" $(bc <<<"obase=2; ${netmask//./;}") | grep -o . | paste -sd+ | bc
Output:
30
Two quick solutions:
$ echo $br1.$br2.$br3.$br4 |
perl -F\\\. -anE 'say join ".", map { sprintf "%08b", $_ } #F'
$ perl -e 'printf( "%08b.%08b.%08b.%08b\n", '$br1,$br2,$br3,$br4')'