Home > front end >  Underline with stars from first non-space character
Underline with stars from first non-space character

Time:11-30

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 variable trimmed 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"
}
  •  Tags:  
  • bash
  • Related