Я написал следующую функцию в 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 ------ $
Интересно, есть ли более простой и элегантный способ отображения подчеркнутого слова на экран.
Для записи я использую:
Ядром вашего вопроса является построение строки, состоящей исключительно из символов подчеркивания, которые имеют такую же длину, что и существующая строка. В достаточно последних версиях 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 }