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 variabletrimmedwith 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"
}
