Bash split substring - bash

I am receiving numeric variables sometimes with 2, other times with 3 digits, like '321' and '32'. And I want to put a dot among each of the numbers. So if I receive '32', I got to echo '3.2' and if I receive '321' I echo '3.2.1'.
This is what I did:
S='321'
SL="${#S}" #string lentgh
n1=`echo $S | cut -c 1-1`
n2=`echo $S | cut -c 2-2`
if [ "$SL" -eq 2 ]; then
echo $n1.$n2
elif [ "$SL" -eq 3 ]; then
n3=`echo $S | cut -c 3-3`
echo $n1.$n2.$n3
else
die 'Works only with 2 or 3 digits'
fi
My question is: is there any shorter way of doing the same thing?
UPDATE:
Shorter but still verbose:
SL="${#1}" #string lentgh
S=$1
if [ "$1" -eq 3 ]; then
$n3=".${S:2:1}"
fi
if [ "$SL" -lt 2 ] && [ "$SL" -gt 3 ]; then
die 'Works only with 2 or 3 digits'
fi
echo "${S:0:1}.${S:1:1}$n3"
UPDATE 1:
If I include the if block, the sed+regex version will be quite as long as the pure bash version:
SL="${#1}" #string lentgh
S=$1
N=$(echo $S | sed -r "s/([0-9])/\1./g")
echo ${N%%.}
if [ "$SL" -lt 2 ] && [ "$SL" -gt 3 ]; then
die 'Works only with 2 or 3 digits'
fi
Or, using a one line sed+regex with two expressions:
SL="${#1}" #string lentgh
echo $1 | sed -e 's/\([[:digit:]]\)/.\1/g' -e 's/^\.//'
if [ "$SL" -lt 2 ] && [ "$SL" -gt 3 ]; then
die 'Works only with 2 or 3 digits'
fi
Thanks.

I prefer also the sed for that:
echo 321 | sed -e 's/\([[:digit:]]\)/.\1/g' | cut -b2- -> 3.2.1
echo 32 | sed -e 's/\([[:digit:]]\)/.\1/g' | cut -b2- -> 3.2
Or without cut it looks like this
echo 321 | sed -e 's/\([[:digit:]]\)/.\1/g' -e 's/^\.//'

Here is one. This will work for any string length.
#!/bin/bash
#s is the string
#fs is the final string
echo "Enter string"
read s
n="${#s}"
fs=""
i=0
for ((i=0; i<n; i++))
do
fs="$fs.${s:i:1}"
done
#find the length of the final string and
#remove the leading '.'
n="${#fs}"
fs="${fs:1}"
echo "$fs"

It's not that pretty, but at least it's short:
num=$(echo $S | sed -r "s/([0-9])/\1./g")
echo ${num%%.}

S='321'
perl -e "print join '.', split //, shift" "$S"

Related

BASH Script syntax error using grep on variable containing special characters

#!/bin/bash
password=$1
if [[ $# -gt 1 || $1 = "-h" ]]; then
echo 'Usage: pw | pw -h | pw password'
echo "Note: Valid passwords must be between 8-16 characters long.
contain at least 1 digit
contain at least 1 lowercase letter
contain at least 1 uppcase letter
contain one of # # $ % & * + - ="
fi
if [[ $# -lt 1 ]]; then
regex='0-9A-Z#*+#$%&a-z'
password=$( cat /dev/urandom | tr -dc '0-9A-Z#*+#$%&a-z' | head -c $(( 8 + $RANDOM % 8 )))
check=$( echo $password | grep -o ['#*+#$%&'] | wc -m )
while [[ $check -gt 2 || $check -lt 1 ]]
do
password=$( cat /dev/urandom | tr -dc '0-9A-Z#*+#$%&a-z' | head -c $(( 8 + $RANDOM % 8 )))
check=$( echo $password | grep -o ['#*+#$%&'] | wc -m )
done
echo $password
fi
if [[ $# = 1 ]]; then
password=$1
echo "$password" | grep [A-Z][a-z]
if [[ $? = 1 ]]; then
echo "Password must contain at least one upper or lowercase characer"
exit 2
fi
echo "$password" | grep [0-9]
if [[ $? = 1 ]]; then
echo "Password must contain at least one digit"
exit 3
fi
if [[ `echo "$password" | grep -o ['##$%&*+-'] 2>/dev/null` -gt 2 ]]; then
echo "You must only use one special character"
exit 4
fi
if [[ `echo "$password" | grep -o ['\#\#\$\%\&\*\+\-'] 2>/dev/null` -lt 1 ]]; then
echo "Password must contain at least one special character"
exit 5
fi
woc=$( echo "$password" | wc -m )
if [[ $woc -lt 8 || $woc -gt 16 ]]; then
echo "Password must be between 8 and 16 characters"
exit 6
fi
echo "$1 is a valid password"
fi
The first part of this script works perfectly and generates a random password 8-16 characters long with only one special character. Problems are on line 40 and 45, both giving me the same error "Syntax error: expecting operand "[special character]". I have tried creating variables and calling them, using the $? variable to check output and more. Unfortunately this error stays consistently even though the script does run correctly.
I fixed the code, it was a simple mistake. I forgot to include wc -m.
if [[ `echo "$password" | grep [\#\#\$\%\&\*\+\-] | wc -m` -lt 2 ]]

Coloring not colored text in Bash

I have to colorize some words in the text, that works good, but I have a problem when its already colored. When its colored I dont want to colour it again with different color. My problem is that my code color it again even if it is already colored.
Here is my code:
var=$(echo -e $line | grep ".*[^m]${word}[^\][^e].*" | sed -e "s/${word}/${color}${word}${endColor}/g")
if(var -n);then
line=$var
f
Its a script where is every odd the color and even is word you want to color. The problem is when there is a word that is already colored and I dont want to recolore it.
Input could be anything
here is full code
function GetColor {
if [ $1 == 'r' ];then
color=$red;
fi
if [ $1 == 'b' ];then
color=$blue;
fi
if [ $1 == 'g' ];then
color=$green;
fi
}
red=$'\e[31m'
green=$'\e[32m'
blue=$'\e[34m'
endColor=$'\e[0m'
a=0
color=""
word=""
while read input
do
radek=$input
for i in $*; do
if (( a% 2 )); then
word=$i
var=$(echo -e $line | grep ".*[^m]${word}[^\][^e].*" | sed -e "s/${word}/${color}${word}${endColor}/g")
if(var -n);then
line=$var
fi
else
color=""
GetColor "$i"
fi
let "a += 1"
done
echo -e $line
exit
done
thanks for help
This is my version, but I've just made the script run without errors. I'm not sure what the problem is, but the colour of already coloured words are not changed.
I suspect
if(var -n);then
I corrected it to
if [ -n "$var" ]; then
Here's the script
function GetColor {
if [ $1 == 'r' ];then
color=$red;
fi
if [ $1 == 'b' ];then
color=$blue;
fi
if [ $1 == 'g' ];then
color=$green;
fi
}
red=$'\e[31m'
green=$'\e[32m'
blue=$'\e[34m'
endColor=$'\e[0m'
a=0
color=""
word=""
while read input
do
line=$input
for i in $*; do
if (( a% 2 )); then
word=$i
var=$(echo -e $line | grep ".*[^m]${word}[^\][^e].*" | sed -e "s/${word}/${color}${word}${endColor}/g")
if [ -n "$var" ]; then
line=$var
fi
else
color=""
GetColor "$i"
fi
let "a += 1"
done
echo -e $line
exit
done

Extra character added to the output of an array when looping through it

#!/bin/bash
a=coop; b=(`echo $a | sed 's/\(.\)/\1\n/g'`)
for i in ${b[#]}
do
echo -n $i
count=$((count+1))
if [ $count = 2 ]; then
echo -e '\e[0;34m'$i
shift
echo -ne $*'\e[0m'
fi
done
Output: cooop (the middle one is in blue). What I want the script to do is show the exact word stored in the variable named "a". But as you can see, another "o" is added next to "p". So how can i go about removing the extra letter?
Try this:
#!/bin/bash
blue='\e[0;34m'
nc='\e[0m'
a=coop
b=($(echo $a | sed 's/\(.\)/\1\n/g'))
count=0
for i in ${b[#]}; do
if [ $count = 2 ]; then
echo -ne "${blue}${i}"
echo -ne "${nc}"
else
echo -n "$i"
fi
count=$((count+1))
done

Autocomplete file name in a bash script

I'm trying to imitate the bash file completion.
Suppose I have the following files:
test1
test2
With an input string of "te" I would like to get the output "test"
This is my current attempt ($c is the input string):
l=1
q="$c"
for j in $(ls -A | grep "^$c"); do
if [ "${j/$c}" != "$j" ]; then
n=$(ls -A | grep ^$j | wc -l)
if [ $n -gt $l ]; then
q="$j"
fi
fi
done
c="$q"
echo $c
Thanks for any help
I tend to think there is no a way to get this from completion engine since it’s not a part of GNU Bash but Readline. But at least we can get list of possible completions with compgen. And an inmplementaion of finding longest common prefix should not be problem. So...
#!/bin/bash
SCRIPTNAME="${0##*/}"
USAGE="Usage: $SCRIPTNAME <prefix>
Print common prefix of possible file name completions. Like <TAB> but to
stdout."
(( $# == 1 )) || { printf >&2 '%s\n' "$USAGE"; exit 1; }
PREFIX="$1"
commonprefix() {
(( $# >= 2 )) || {
echo "$1"
return 0
}
local -i i N M
for ((i=0; i<=${#1}; i++)); do
for ((N=1; N<=$#-1; N++)); do
let M=$N+1
[[ ${!N:i:1} == ${!M:i:1} ]] || break 2
done
done
echo "${1:0:i}"
}
readarray -t COMPLETIONS < <(compgen -f "$PREFIX")
commonprefix "${COMPLETIONS[#]}"
Although Dmitry Alexandrov already provided a better solution, I still would like to post my own one which I made while waiting for the answers:
l=1
while [ -n $l ]; do
l=${#c}
a=$(ls -A | grep "^$c" | wc -l)
q=$c
for i in $(ls -A | grep "^$q"); do
if [ $i == $q ]; then
unset l
break
else
v=$(ls -A | grep "^$q${i:$l:1}" | wc -l)
if [ $v == $a ]; then
q="$c${i:$l:1}"
break
fi
fi
done
if [ $c == $q ]; then break; fi
c=$q
done
echo $c
It works with all of my tests, but it's slow (although it could be optimized).
Just to show that you were thinking in correct direction, I made your code work:
#!/bin/bash
c=$1
q="$c"
for j in $c*; do
if [ "${j/$c}" != "$j" ]; then
startn=$(ls -1A | grep -c "^${j:0:$((${#c} + 1))}")
for (( i=${#c}; i <= ${#j}; i++ )); do
n=$(ls -1A | grep -c "^${j:0:$i}")
if [ "$n" -lt "$startn" ]; then
q="${j:0:$((i - 1))}"
elif [ "$n" -le "$startn" ]; then
q="${j:0:$i}"
fi
done
fi
done
c="$q"
echo "$c"
But, it's just a proof of concept, don't use it. See answer by Dmitry Alexandrov for a good solution.

Counting characters, words, length of the words and total length in a sentence

I have to write a script that takes a sentence and prints the word count, character count (excluding the spaces), length of each word and the length. I know that there exist wc -m to counter number of characters in the word, but how to make use of it in script?
#!/bin/bash
mystring="one two three test five"
maxlen=0;
for token in $mystring; do
echo -n "$token: ";
echo -n $token | wc -m;
if [ ${#token} -gt $maxlen ]; then
maxlen=${#token}; fi;
done
echo "--------------------------";
echo -n "Total words: ";
echo "$mystring" | wc -w;
echo -n "Total chars: ";
echo "$mystring" | wc -m;
echo -n "Max length: ";
echo $maxlen
riffing on Jaypal Singh's answer:
jcomeau#intrepid:~$ mystring="one two three four five"
jcomeau#intrepid:~$ echo "string length: ${#mystring}"
string length: 23
jcomeau#intrepid:~$ echo -n "lengths of words: "; i=0; for token in $mystring; do echo -n "${#token} "; i=$((i+1)); done; echo; echo "word count: $i"
lengths of words: 3 3 5 4 4
word count: 5
jcomeau#intrepid:~$ echo -n "maximum string length: "; maxlen=0; for token in $mystring; do if [ ${#token} -gt $maxlen ]; then maxlen=${#token}; fi; done; echo $maxlen
maximum string length: 5
echo $mystring | wc -w
or
echo $mystring | wc --words
will do a word count for you.
You can pipe each word to wc:
echo $token | wc -m
to store the result in a variable:
mycount=`echo $token | wc -m`
echo $mycount
to add to the total as you go word by word, do math with this syntax:
total=0
#start of your loop
total=$((total+mycount))
#end of your loop
echo $total
#!/bin/bash
mystring="one two three test five"
for token in $mystring; do
echo -n "$token: ";
echo -n $token | wc -m;
done
echo "--------------------------";
echo -n "Total words: ";
echo "$mystring" | wc -w;
echo -n "Total chars: ";
echo "$mystring" | wc -m;
string="i am a string"
n=$(echo $string | wc -w )
echo $n
4
The value of n can be used as an integer in expressions
eg.
echo $((n+1))
5
You are very close. In bash you can use # to get the length of your variable.
Also, if you want to use bash interpreter use bash instead of sh and the first line goes like this -
#!/bin/bash
Use this script -
#!/bin/bash
mystring="one two three test five"
for token in $mystring
do
if [ $token = "one" ]
then
echo ${#token}
elif [ $token = "two" ]
then
echo ${#token}
elif [ $token = "three" ]
then
echo ${#token}
elif [ $token = "test" ]
then
echo ${#token}
elif [ $token = "five" ]
then
echo ${#token}
fi
done
The wc command is a good bet.
$ echo "one two three four five" | wc
1 5 24
where the result is number of lines, words and characters. In a script:
#!/bin/sh
mystring="one two three four five"
read lines words chars <<< `wc <<< $mystring`
echo "lines: $lines"
echo "words: $words"
echo "chars: $chars"
echo -n "word lengths:"
declare -i nonspace=0
declare -i longest=0
for word in $mystring; do
echo -n " ${#word}"
nonspace+=${#word}"
if [[ ${#word} -gt $longest ]]; then
longest=${#word}
fi
done
echo ""
echo "nonspace chars: $nonspace"
echo "longest word: $longest chars"
The declare built-in casts a variable as an integer here, so that += will add rather than append.
$ ./doit
lines: 1
words: 5
chars: 24
word lengths: 3 3 5 4 4
nonspace chars: 19
code
var=(one two three)
length=${#var[#]}
echo $length
output
3

Resources