I am using this bash function to print a phrase with stars below it.
outline ()
{
titl="$1"
n="${#titl}"
st=$( printf '%*s' $n " " | tr ' ' '*' )
printf '%s\n' "$titl" "$st"
}
The result of outline "Stack Overflow"
will be
1234567890123456
Stack Overflow
**************
But with three spaces before the first letter S
, the command outline " Stack Overflow"
gives
12345678901234567890
Stack Overflow
*****************
I want to start the stars from the beginning of the first non-space character, so that the result of outline " Stack Overflow"
will be
12345678901234567890
Stack Overflow
**************
Have including column numbers on top to understand where things are to be placed.
CodePudding user response:
this works for all non-space characters: would your teacher accept it?
outline ()
{
titl="$1"
printf '%s\n%s\n' "${titl}" "${titl//[! ]/*}"
}
outline " Stack Overflow"
Stack Overflow
***** ********
CodePudding user response:
With bash
:
outline ()
{
local titl spaces
titl="$1"
# extract leading spaces
[[ $titl =~ ^(\ )* ]]; spaces="${BASH_REMATCH[0]}"
# remove leading spaces from titl
titl="${titl/#$spaces/}"
echo "$spaces$titl"
echo "$spaces${titl//?/*}"
}
CodePudding user response:
With only bash the following does what you show, thanks to the extended pattern matching (extglob
) option:
outline () {
restore="$(shopt -p extglob)"
shopt -s extglob
spc="${1/[^[:space:]]*}"
str="${1## ([[:space:]])}"
printf '%s\n%s\n' "$1" "$spc${str//?/*}"
eval "$restore"
}
Explanation: first record the current extglob
status such that it can be restored before leaving the function. Then store the leading spaces in variable spc
and the rest in variable str
. Finally, print the original string, followed by a line formed by $spc
and $str
where each character has been replaced by a star.
A one-liner with sed (tested with GNU sed):
outline () {
sed -E 'p;s/^([[:space:]]*)./\1*/;:a;s/\*[^*]/**/;ta' <<< "$1"
}
Explanation: first print the string. Next, substitute the first non-space character by a star. Then substitute a star followed by a non-star character by two stars and repeat as long as there is a match. Finally sed automatically prints the result.
CodePudding user response:
In pure bash:
outline () {
local leading_blanks=${1%%[![:blank:]]*}
local trimmed=${1#"$leading_blanks"}
printf '%s\n%s%s\n' "$1" "$leading_blanks" "${trimmed//?/*}"
}
outline " Stack Overflow"
Explanation:
${1%%[![:blank:]]*}
deletes from the first non-blank character through end of string, from$1
. Remaining characters are just leading blanks, if any.${1#"$leading_blanks"}
removes the leading blank characters (if any) from$1
."${trimmed//?/*}"
replaces each character in the variabletrimmed
with a*
.
For detailed information about all types of parameter expansion in bash, read Shell Parameter Expansion
Alternatively, a single sed
command will do the job, though, I would definitely prefer the pure bash version:
outline () {
sed 'h
s/^[[:blank:]]*//
s/./*/g
H
g
s/^\([[:blank:]]*\)\(.*\n\)\(.*\)/\1\2\1\3/' <<< "$1"
}