Форматирование вывода: Подчеркивание

Я написал следующую функцию в ksh которая выводит свой первый аргумент на экран и подчеркивает его с соответствующим количеством символов:

 print_underlined () { word=$1 echo $word i=${#word} while [[ i -gt 0 ]]; do printf "-" (( i = $i - 1 )) done printf "\n" } 

пример:

 $ print_underlined foobar foobar ------ $ 

Интересно, есть ли более простой и элегантный способ отображения подчеркнутого слова на экран.

Для записи я использую:

  • Solaris 10
  • ksh88

Ядром вашего вопроса является построение строки, состоящей исключительно из символов подчеркивания, которые имеют такую ​​же длину, что и существующая строка. В достаточно последних версиях bash, ksh или zsh вы можете построить эту строку с конструкцией ${VARIABLE//PATTERN/REPLACEMENT} : underlines=${word//?/_} . Но эта конструкция не существует в ksh88.

В любой оболочке вы можете использовать tr вместо. POSIX-совместимые реализации tr позволяют писать:

 underlines=$(printf %s "$word" | tr -c '_' '[_*]') 

Я думаю, что Solaris 10 по умолчанию имеет POSIX-совместимый tr , но может быть историческая реализация (совместимая с более ранними версиями Solaris). Исторические реализации tr могут не понимать синтаксис [x*] , но они, как правило, принимают вместо этого следующий синтаксис (который не гарантируется POSIX), означающий «заменить все, что не является новой линией на _ »:

 underlines=$(echo "$word" | tr -c '\010' '_') underlines=${underlines%_} 

И вот немного сумасшедший метод, который не использует никакой цикл или внешнюю программу и должен работать в любой оболочке Bourne (по крайней мере, с тех пор, как был введен set -f хотя запуск в пустом каталоге уменьшил бы отсутствие set -f ). К сожалению, он работает только в том случае, если строка не содержит пробелов.

 set -f # turn off globbing IFS=$word # split at any character in $word set a $word # split $word into one word between each character, ie empty words shift # remove the leading a (needed in case $word starts with -) IFS=_ underlines=$* # join the empty words, separated with the new value of $IFS 

Более сложный вариант имеет дело с пробелами, но только в том случае, если нет последовательной последовательности пробелов. Я не думаю, что вы можете пойти дальше с этим трюком, поскольку последовательности символов пробелов в IFS всегда рушились.

 set -f unset IFS; set a $0 # split at whitespace IFS=$*; set $* # split into empty words IFS=_; underlines=$* # collect the empty 

В новых оболочках вы можете делать printf %s\\n "${word//?/-}" . Я не думаю, что у ksh88 есть это особое расширение.

Если вы не возражаете против дополнительного процесса, вы можете сделать printf %s\\n "${word}" | sed -e 's/./-/g' printf %s\\n "${word}" | sed -e 's/./-/g' .

Ваш подход тоже прекрасен, хотя я бы сделал следующие крошечные изменения:

 print_underlined () { word=$1 printf %s\\n "$word" i=${#word} while (( i )); do printf - (( i = i - 1 )) done echo } 

И для совершенно другого подхода используйте способность терминала отображать истинные подчеркивания, если они доступны:

 tput smul; printf %s\\n "$word"; tput rmul 

Конечно, этот подход работает только в том случае, если вы знаете, что терминал работает под его поддержкой.

Я нашел это просто, перейдя по ссылке:

 underline() { echo $1; echo "${1//?/${2:--}}";} 

В основном то же самое, но гораздо более компактное. Если вы считаете, что это сбивает с толку, здесь можно найти дополнительную информацию о замене фигурных скобок. Очень похоже на синтаксис sed.

Это POSIX-совместимый способ, который будет работать на Solaris 10 & ksh88:

 print_underlined () { printf "%s\n%s\n" "$1" $(printf "%s\n" "$1" | sed "s/./-/g") } 

 $ print_underlined "hello world" hello world ----------- 
 print_underlined () { word=$1 tput smul print $word tput sgr0 }