I am having a go at printing a sequence of strings, either using a newline between entries. Or printing them next to each other.
Would like to simplify this if I can.
When nwline=1, new lines are used between the printing of arguments. When nwline=0, arguments are printed on same line.
nl determines the number of arguments that get coloured using ctp.
pfm ()
{
nwline=0
nl=3
ctp=$(tput bold)$(tput setaf 39)
if (( nl >= 1 )); then
case $nwline in
1) printf '%s\n' "${ctp}${#:1:nl}${rst}" ;;
*) printf '%s' "${ctp}${#:1:nl}${rst}" ;;
esac
if (( nl + 1 <= $# )); then
case $nwline in
1) printf '%s\n' "${#:nl+1}" ;;
*) printf '%s' "${#:nl+1}" ;;
esac
fi
else
case $nwline in
1) printf '%s\n' "$#" ;;
*) printf '%s' "$#" ;;
esac
fi
}
Using suggestions I did the following
aggr=("$#")
nk=$((nl-1))
rst=$(tput sgr0) ; ctp=$(tput bold)$(tput setaf 39)
(( nwline == 1 )) && fs=$'\n' || fs=' '
( IFS=$fs ; echo "${ctp}${aggr[*]:0:nk}${rst}" )
(( nl + 1 <= $# )) && ( IFS=$fs ; echo "${aggr[*]:nl}" )
But when I use pfm "Mary" "had" "a" "little" "lamb", I get
Mary
had
little
lamb
As observed, I am missing the "a".
Would you please try the following:
pfm ()
{
local nwline=1
local nl=3
local ctp=$(tput bold)$(tput setaf 39)
local rst=$(tput sgr0)
local -a ary=("$#") # make a copy of arguments
local fs # field separator: "\n" or ""
if (( nl > $# )); then nl=$#; fi # limit the value of nl
for (( i = 0; i < nl; i++ )); do # highlight the elements indexed lower than nl
ary[i]="${ctp}${ary[i]}${rst}"
done
(( nwline == 1 )) && fs=$'\n' || fs='' # assign fs to the field separator
(IFS=$fs; echo "${ary[*]}") # execute in subshell not to modify IFS
}
As for the printing of the elements using newline or without, I have
used the array variable expansion "${name[*]}" which expands
to a concatenation of array elements separated by the first character of IFS.
Related
I want to print a string in a particular colour depending on options supplied as
positional arguments. I will pass a multiline string to tho bash script after the
command line options.
The problem I face is how to print a multiline string. I am currently using
printf '%s%s%s\n' "$red" "$#" "$rst". But wouldn't the use of '%s%s%s\n'
be a problem when using a multiline string "$#" ?
local chorn=0 ihorn=0 clk=0 bell=0 syn=0
while (( $# > 0 )); do
case $1 in
("--chorn") chorn=1 ; shift 1 ;;
("--ihorn") ihorn=1 ; shift 1 ;;
("-c"|"--clacker") clk=1 ; shift 1 ;;
("-b"|"--bell") bell=1 ; shift 1 ;;
("--") shift 1 ; break ;;
(*) break ;;
esac # case ends here
done # while ends here
rst="$( tput sgr0 )"
red="$( tput bold; tput setaf 196 )"
amb="$( tput bold; tput setaf 214 )"
if (( chorn == 1 )); then
printf '%s%s%s\n' "$red" "$#" "$rst"
elif (( ihorn == 1 )); then
printf '%s%s%s\n' "$red" "$#" "$rst"
elif (( clk == 1 )); then
printf '%s%s%s\n' "$amb" "$#" "$rst"
elif (( bell == 1 )); then
printf '%s%s%s\n' "$amb" "$#" "$rst"
fi
As long as the $# is contained in double quotes, the shell will only see that as a single string, as per your quotes definition. Hence, only need 3 x %s.
I want to center a word in X spaces so I wrote this function:
function center {
str=$1
M=$2
N=${#str}
if (( N >= M )); then
echo $str
exit
fi
P=$((M-N))
HP=$((P/2))
left=""
right=""
for i in [1..HP]; do :
left=" $left"
right=" $right"
done
if (( (P-2*HP) == 1 )); then
right=" "$right""
fi
echo "HP is $HP"
echo "Right is |"${right}"|"
echo "Left is |$left|"
echo "str is |$str|"
res=$right$str$left
echo "$res"
}
Problem is not matter what I do can't get right or left to hold on to more than one whitespace. I have tried the suggestions on other answers but I can't seem to make them work. Please help.
Try this (after some variable quoting and some other fixes):
function center {
str=$1
M=$2
N=${#str}
if (( N >= M )); then
echo "$str"
exit
fi
(( P=M-N ))
(( HP=P/2 ))
left=$(printf '%*s' "$HP" "")
right=$left
(( (P-2*HP) == 1 )) && right=" "$right""
echo "HP is $HP"
echo "Right is |${right}|"
echo "Left is |$left|"
echo "str is |$str|"
res="$right$str$left"
echo "$res"
}
The for i in [1..HP] can not work.
A printf '%*s' "$HP" "" is a more idiomatic way to get $HP spaces.
Is better to build one variable $left and copy it to $right than to build the two.
I am new to Bourne shells and was curious about reading stdin on multiple lines/that includes newline.
IN: For example, if someone types in
1
3 #only ignore whitespace
5.3 #ignore floating point number all together
I could have it read them and echo them on separate lines. My goal is to also ignore floating point numbers (integers only!) and ignore whitespace.
OUT: My ideal output would calculate the average (example with above input)
1
2
So far I can read stdin and echo it back if it is on one line and limited to a hardcoded value (should be whenever user stops with Ctrl-D).
#! /bin/sh
read a b c
echo "$a";
echo "$b";
echo "$c";
It is been a while, but the following should obtain a sum and average for only integer input:
#!/bin/sh
sum=0;
cnt=0
while read line
do
case "$line" in
*[.]* )
continue
;;
[0-9]* )
sum=`expr "$sum" + "$line"`
cnt=`expr "$cnt" + 1`
;;
esac
done
printf "\nThe sum is '%s', cnt is '%s' the average is '%s'\n\n", "$sum" "$cnt" `expr $sum / $cnt`
Output
$ printf "1\n 3\n5.3\n" | sh bourne_sum.sh
The sum is '4', cnt is '2' the average is '2'
Input in a File
$ cat dat/bsum.txt
1
3
5.3
Simple redirection will do:
$ sh bourne_sum.sh <dat/bsum.txt
The sum is '4', cnt is '2' the average is '2'
Modified to output on 1 line
#!/bin/sh
sum=0;
cnt=0
while read line
do
case "$line" in
*[.]* )
printf " 0"
continue
;;
[0-9]* )
sum=`expr "$sum" + "$line"`
cnt=`expr "$cnt" + 1`
printf " %s" `expr $sum / $cnt`
;;
esac
done
printf "\n"
# printf "\nThe sum is '%s', cnt is '%s' the average is '%s'\n\n" "$sum" "$cnt" `expr $sum / $cnt`
Output
$ printf "1\n3\n5\n4.4\n" | sh bourne_sum.sh
1 2 3 0
Responding to ONLY Integers
#!/bin/sh
sum=0;
cnt=0
while read line
do
if [ $line -eq $line 2>/dev/null ]
then
sum=`expr "$sum" + "$line"`
cnt=`expr "$cnt" + 1`
printf " %s" `expr $sum / $cnt`
fi
done
printf "\n"
Output
$ printf "1\n3\n5\n4.4\n'2 3'\n" | sh bourne_sum.sh
1 2 3
I wrote this sample KornShell (ksh) code but it is getting bad substitution error during the if clause.
while ((i < $halflen))
do
if [[${strtochk:i:i}==${strtochk:j:j}]];then
i++
j--
else
ispalindrome = false
fi
done
Please help.
NB: I am using ksh88, not ksh93.
shell syntax is very whitespace sensitive:
[[ is acually the name of a command, it's not just syntax, so there must be a space following it.
The last argument of [[ must be ]], so it needs to be preceded by a space.
[[ works differently depending on the number of arguments it receives, so you want to have spaces around ==
In a variable assignment, you must not have spaces around =.
Tips:
once you figure out it's not a palindrome, break out of the while loop
you are probably checking character by character, so you want ${strtochk:i:1}
i++ and j-- are arithmetic expressions, not commands, so you need the double parentheses.
are you starting with i=0 and j=$((${#strtochk} - 1))?
while ((i < halflen))
do
if [[ ${strtochk:i:1} == ${strtochk:j:1} ]];then
((i++))
((j--))
else
ispalindrome=false
break
fi
done
Check if your system has rev, then you can simply do:
if [[ $strtochk == $( rev <<< "$strtochk" ) ]]; then
echo "'$strtochk' is a palindrome"
fi
function is_palindrome {
typeset strtochk=$1
typeset -i i=1 j=${#strtochk}
typeset -i half=$(( j%2 == 1 ? j/2+1 : j/2 ))
typeset left right
for (( ; i <= half; i++, j-- )); do
left=$( expr substr "$strtochk" $i 1 )
right=$( expr substr "$strtochk" $j 1 )
[[ $left == $right ]] || return 1
done
return 0
}
if is_palindrome "abc d cba"; then
echo is a palindrome
fi
You are using ksh88 but the code you tried is using ksh93 feature missing for the 88 version.
You need to replace
if [[${strtochk:i:i}==${strtochk:j:j}]];then
with these portable lines:
if [ "$(printf "%s" "$strtochk" | cut -c $i)" =
"$(printf "%s" "$strtochk" | cut -c $j)" ]; then
and the incorrect:
i++
j--
with:
i=$((i+1))
j=$((j-1))
consider the string
s=pqrPQR456PQRpqr
Now
expr index $s p
gives me ans as 1
But how to find index of p which is after R or index of any repetitive character; that is what I'm unable to get.
You can get substrings based on the index.
$ s=pqrPQR456PQRpqr
$ n=$(expr index $s p)
$ echo "${s:$n}"
qrPQR456PQRpqr
This at least gives you the string which follows the character you were searching for.
#!/bin/ksh
ind=`expr index string substring`
count_occ=`echo "$string"|tr -cd $substring|wc -c`
count=1
echo " The $substring occurs at : $ind "
while [ $count -lt $count_ind ]
do
rem_str=${string:$ind}
new_ind=`expr index $rem_str $substring`
count=`expr $count + 1`
ind=`expr $ind + $new_ind`
echo "$ind"
done
Here is a function that will give a single character offset without calling an external program like expr. It counts positions from zero rather than one (zero is more commonly used for the first character position).
# Take two parameters:
# $1 - the string
# $2 - target character
# $3 - the offset, default is 0
function strchr {
typeset -i offset
string="$1"
target="$2"
offset="$3"
(( $# < 3 )) && offset=0
while (( $offset < ${#string} ))
do
char=${string:$offset:1}
[[ $char == $target ]] && break;
(( offset++ ))
done
echo "$offset"
}
s='pqrPQR456PQRpqr'
i=$(strchr "$s" 'p')
echo "i: $i"
j=$(strchr "$s" 'p' $(( $i + 1 )) )
echo "j: $j"
Gives:
i: 0
j: 12