Using colours with bash argument options - bash

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.

Related

Printing sequence of strings either using a newlines or without

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.

Two ways to handle short and long options in bash

Have written two ways, trying to parse short and long options by hand. The first is complicated with the use of IFS=" =". Perhaps set -- $* is not necessary.
rando ()
{
IFSPREV="$IFS" # Save IFS (splits arguments on whitespace by default)
IFS=" =" # Split arguments on " " and "="
set -- $* # Set positional parameters to command line arguments
IFS="$IFSPREV" # Set original IFS
local iarg=0 narg="$#"
while (( narg > 0 )); do
opt="$1"
iarg=$(( iarg + 1 ))
case $opt in
("-s"|"--src"|"--source") src="$2" ; shift 2 ;;
("-d"|"--dst"|"--destin") dst="$2" ; shift 2 ;;
("--") shift 1 ; break ;;
("-"*) printf '%s\n' "Unknown option: $1" ; shift 1 ;;
(*) shift 1 ; break ;;
esac
done
}
And here is the other, but I also want to split on = for long-options.
rando ()
{
PARAMS=""
while (( "$#" )); do
case "$1" in
("-s"|"--src"|"--source")
if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then
src="$2"
shift 2
else
echo "Error: Argument for $1 is missing" >&2
exit 1
fi
;;
("-d"|"--dst"|"--destin")
if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then
dst="$2"
shift 2
else
echo "Error: Argument for $1 is missing" >&2
exit 1
fi
;;
-*|--*=) # unsupported flags
echo "Error: Unsupported flag $1" >&2
exit 1
;;
*) # preserve positional arguments
PARAMS="$PARAMS $1"
shift
;;
esac
done
}

How can I prevent and optimize a bad variable name?

When I run the code the output is ./fib.sh: 43: read: 6: bad variable name. What does it mean and how do I fix it?
case $# in
0)
echo "plz insert 3 integer number \n" && read a b n;;
1)a=$1 && echo "plz insert 2 integer number\n " && read b n ;;
2) a=$1 b=$2 && echo "plz insert 1 intger number \n" && read n ;;
3)a=$1 b=$2 n=$3 ;;
*)echo "The number of arguments is more than 3\n" && exit 1 ;;
esac
echo " a=$a b=$b n=$n\n"
while true ; do
if [ $n -eq 0 ] ;then
echo $a
elif [ $n -eq 1 ];then
echo $b
elif [ $n -ge 2 ] ; then
printf "%s ""$a"
printf "%s ""$b"
count=1
c=`expr $n - 1`
while [ $count -le $c ] ; do
printf "%s ""$answer"
answer=`expr $a + $b`
a=$b
b=$answer
count=`expr $count + 1`
done
echo "\nDo you want to try another n (yes/no):\n"
read s
case $s in
yes) echo"\nenter new value of n \n" && read $n && continue ;;
no) echo "Thank you" && exit 1 ;;
esac
fi
done
read: 6: bad variable name.
read $n expands the value of the variable n which, in this case, is 6. So that's read 6 trying to read into the variable 6. 6 is not a valid variable name in shell.
From context, you want to read into n which is read n.
yes) echo"\nenter new value of n \n" && read n && continue ;;

Loop Script throws 0 by executing touch

I'm writing a script that makes a while loop for me, that I doesn't have to write a one line while loop again and again.
Short explanation:
The command "loop" will execute the command parameter -n times and with -p seconds pause. You can use -e to echo the command and not execute it. -v to show all parameters. Additionally its possible to use the iterator of the while loop.
Playing around with it, I've found a bug.
loop --verbose --times 5 'touch testfile$i'
#loop the touch command 5 times and generate the testfiles 0 to 4
same as:
loop -v -n 5 'touch testfile$i'
It generate "testfile0" to "testfile4" but also a file named "0"
Do somebody know why the file "0" will be generated?
And what do you think about a command like that? Would you like to use it?
Best
Steven
#!/bin/bash
# easy to use loop command
#
# Autor: Steven Wagner
# Date: 22 June 2018
# Version: 0.1
TIMES=0
FREQUENCY=0
PAUSE="0"
VERBOSE=false
ECHOONLY=false
SILENCE=false
POSITIONAL=()
while [[ $# -gt 0 ]]
do
key="$1"
case $key in
-n|--times)
temp="$2"
if [[ $temp =~ ^[[:digit:]]+$ ]];
then
TIMES=$temp
else
echo "ERROR: value is not a digit"
exit
fi
shift # past argument
shift # past value
;;
-p|--pause)
#PAUSE="$2"
temp="$2"
PAUSE=$temp
#if [[ $temp =~ ^[[:digit:]]+$ ]];
#then
# PAUSE=$temp
#else
# echo "ERROR: value is not a digit"
# exit
#fi
shift # past argument
shift # past value
;;
-v|--verbose)
VERBOSE=true
shift # past argument
;;
-e|--echo-only)
ECHOONLY=true
shift # past argument
;;
-s|--silence)
SILENCE=true
shift # past argument
;;
--*)
echo "Error \"$key\" not known"
shift #past argument
;;
-*)
i=0
while [[ $i -lt ${#key}-1 ]]
do
char=${key:$[$i+1]:1}
case $char in
v)
VERBOSE=true
;;
e)
ECHOONLY=true
;;
s)
SILENCE=true
;;
esac
(( i++ ))
done
shift # past argument
;;
*) # unknown option
POSITIONAL+=("$1") # save it in an array for later
shift # past argument
;;
esac
done
set -- "${POSITIONAL[#]}" # restore positional parameters
#Pause make sleep command
pause=""
if [ $PAUSE > 0 ]
then
pause="sleep $PAUSE"
fi
#times make while control-command
times=""
if [ $TIMES != 0 ]
then
times="[ \$i -lt \$TIMES ]"
else
times="true" #infinity loop
fi
#Echo Only
command=$#
if $ECHOONLY
then
command="echo $#"
fi
#Silence
silence=""
if $SILENCE && ! $ECHOONLY
then
silence="&>/dev/null"
fi
if $VERBOSE
then
echo "pause = $PAUSE"
if [ $TIMES != 0 ]
then
echo "times = $TIMES"
else
echo "times = infinity"
fi
if $ECHOONLY; then echo "echo only = yes"; fi
if $SILENCE; then echo "silence = yes"; fi
echo "command = '$#' $silence"
echo
fi
#start the loop
timestamp=$(date +%s%N)
eval "
i=0
while $times
do
eval '$command' $silence
$pause
(( i++ ))
done
"
if $VERBOSE
then
echo
echo "duration: $[($(date +%s%N)-$timestamp)/1000000]ms"
fi

need a shell script to convert big endian to little endian

I need a shell script program to print the hexadecimal number from big endian to little endian
For example
Input: my virtual address = 00d66d7e
Output: 7e6dd600
How can I can I create this in a bash script?
Just had to do this... but from decimal to little endian.. adapting that here:
echo 00d66d7e | tac -rs .. | echo "$(tr -d '\n')"
achieves the desired result, for arbitrarily sized hexadecimal representations of unsigned integers.
(h/t 'tac -rs' MestreLion, very nice!)
For 32 bit addresses, assuming it's zero padded:
v=00d66d7e
echo ${v:6:2}${v:4:2}${v:2:2}${v:0:2}
# 7e6dd600
Based on Karoly's answer you could use the following script, reading an argument or piped input:
#!/bin/bash
# check 1st arg or stdin
if [ $# -ne 1 ]; then
if [ -t 0 ]; then
exit
else
v=`cat /dev/stdin`
fi
else
v=$1
fi
i=${#v}
while [ $i -gt 0 ]
do
i=$[$i-2]
echo -n ${v:$i:2}
done
echo
For e.g. you could save this script as endian.sh and make it executable with:
chmod u+x endian.sh
Then:
echo 00d66d7e | ./endian.sh
gives you:
7e6dd600
For a different length string:
echo d76f411475428afc90947ee320 | ./endian.sh
result would be:
20e37e9490fc8a427514416fd7
#Update: Modified the script to accept the input either as an argument or from stdin, addressing Freewind's request. So now:
./endian.sh d76f411475428afc90947ee320
also works and gives you:
20e37e9490fc8a427514416fd7
This works for dash (and many other shells) :
v=0x12345678
v2=$(( (v<<8 & 0xff00ff00) | (v>>8 & 0xff00ff) ))
v2=$(( (v2<<16 & 0xffff0000) | v2>>16 ))
printf '0x%08x\n' $v2
Result should be "0x78563412"
${v:6:2} is for bash.
In response to Freewind's comment request and building off of hutheano's great answer, I wrote my own bash script and I include a condensed version below. The full script can be downloaded here.
The following implementation accounts for odd length strings, 0x or \x prefixes, and multiple output formats and can be used like the following:
$ be2le d76f411475428afc90947ee320 0xaaff 0xffa '\x3'
20e37e9490fc8a427514416fd7
0xffaa
0xfa0f
\x03
be2le bash script
#!/bin/bash
args=()
format=preserve
delimiter="\n"
nonewline=false
join=false
strip=false
while (( "$#" )); do
case "$1" in
-h|--help) usage;;
-f) format=$2; shift 2;;
--format=*) format="${1#*=}"; shift;;
-d) delimiter=$2; shift 2;;
--delimiter=*) delimiter="${1#*=}"; shift;;
-n|--no-newline) nonewline=true; shift;;
-j|--join) join=true; shift;;
-s|--strip-null) strip=true; shift;;
-*|--*) echo "Error: unsupported flag $1 specified"; exit 1;;
*) args=( "${args[#]}" "$1" ); shift;;
esac
done
case "$format" in
preserve);;
int) prefix="0x";;
char) prefix="\x";;
raw) ;;
*) echo "Error: unsupported format $format"; exit 1;;
esac
n=0
parts=()
for arg in ${args[#]}; do
digest=""
prefix=""
# remove prefix if string begins with "0x"
if [[ $arg =~ ^[0\\]x ]]; then
if [ "$format" == "preserve" ]; then
prefix=${arg:0:2}
fi
arg=${arg:2}
fi
# zero-pad if string has odd length
if [ $[${#arg} % 2] != 0 ]; then
arg="0$arg"
fi
part=""
i=${#arg}
while [ $i -gt 0 ]; do
i=$[$i-2]
byte=${arg:$i:2}
if [ $strip == true ] && [ -z "$part" ] && [ $byte == "00" ]; then
continue
fi
case "$format" in
int) part="$part"'0x'"$byte ";;
char) part="$part\x$byte";;
raw) part="$part$(printf "%b" "\x$byte")";;
*) part="$part$byte";;
esac
done
digest="$prefix$digest$part"
parts=( "${parts[#]}" "$digest" )
n=$[$n+1]
done
if [ $join == true ]; then
case "$format" in
*) printf "%s" "${parts[#]}";;
esac
else
i=0
for part in "${parts[#]}"; do
if [[ $(($i + 1)) < $n ]]; then
printf "%s$delimiter" "$part"
else
printf "%s" "$part"
fi
i=$(($i+1))
done
fi
if [ $nonewline == false ]; then
echo
fi
This script is for flipping 16 bit data.
#!/bin/bash
if [ -t 0 ]; then exit; fi
data=`cat /dev/stdin | od -An -vtx1 | tr -d ' ' | tr -d '\n'`
length=${#data}
i=0
while [ $i -lt $length ]; do
echo -n -e "\x${data:$[$i+2]:2}"
echo -n -e "\x${data:$[$i]:2}"
i=$[$i+4]
done

Resources