#arrays #bash #counting
Вопрос:
Я изучал Bash, и мой учитель сделал упражнение, которое меня озадачило.
Я просто борюсь с добавлением пробелов перед словами.
Код должен учитывать количество символов в каждом слове и регулировать количество звездочек и интервалов, чтобы оно всегда выравнивалось.
Вот как это должно выглядеть:
***********
* Hello *
* World *
* good *
* etceter *
***********
Мой код:
#determine biggest word
words=("Hello" "World" "good" "etceter")
numchar=0
for i in ${words[@]}; do
if [ ${#i} -gt $numchar ]
then
numchar=${#i}
fi
done
#write *
a=-4
while [ $a -lt $numchar ]; do
printf "*"
((a ))
done
#write array
echo
for txt in ${words[@]}; do
space=$(($numchar-${#txt}))
s=0
echo "* $txt "
while [ $s -lt $space ]; do
printf " "
((s ))
printf "*"
done
done
#write *
a=-4
while [ $a -lt $numchar ]; do
printf "*"
((a ))
done
Я борюсь с частью массива #write.
Заранее большое спасибо!
Комментарии:
1. Здесь есть несколько мест, где вы неправильно заключили ссылки на переменные в двойные кавычки, что иногда может вызвать странные проблемы. Я рекомендую запускать ваши скрипты через shellcheck.net и исправление (или, по крайней мере, понимание) того, на что оно указывает, — это избавит вас от многих вредных привычек написания сценариев.
2. Первый
for
цикл можно заменить наnumchar=$(printf '%sn' "${words[@]}" | wc -L)
, если GNUwc
(1) доступен / приемлем.
Ответ №1:
Вам нужно всего лишь несколько изменений.
#write array
echo
for txt in ${words[@]}; do
space=$(($numchar-${#txt}))
s=0
echo -n "* $txt " # -n added to not append a newline
while [ $s -lt $space ]; do
echo -n " " # switched from printf to echo (cosmetics)
((s ))
# printf "*" # commented out
done
echo "*" # added
done
Ваш while
цикл добавляет только пробелы после текущего слова. Завершение *
идет после цикла, чтобы закончить эту строку.
Комментарии:
1. Спасибо за ваше понимание! Есть ли какая-либо причина выбрать «echo -n» вместо «printf»? Есть ли у вас какие-либо другие предложения по улучшению кода моего кода?
2.
printf
более переносима, чемecho
в сочетании с другими оболочками, если вы хотите избежать конечных пробелов. Однако правильный синтаксисprintf
более длинный:printf "%s" "*"
. Возможно, вы заметили, что вы повторяете код (write *
) для вывода строки*
. Вы можете переместить этот код в функцию, а затем вызвать функцию в требуемых точках.3. Да, я посмотрел и исправил это, должен ли я редактировать свой основной пост с помощью исправленного кода?
4. Нет, пожалуйста, оставьте свой первоначальный вопрос без изменений. Спасибо.
5. Несмотря на то, что он более подробный, я всегда предпочитаю
printf
overecho -n
. Однажды у меня сломалась куча моих скриптов, потому что обновление ОС изменило поведение по умолчаниюecho -n
для печати «-n» как части его вывода, поэтому я научился не полагатьсяecho
ни на что нетривиальное. Кстати, вы можете просто использоватьprintf " "
andprintf "*"
(не"%s"
требуется), поскольку эти строки не содержат%
или`. When printing the word, though, you should use the full
printf «%s»`version .
Ответ №2:
Переписать последние 3 раздела:
# define solid string of asterisks
printf -v stars '*%.0s' $(seq 1 $(( numchar 4)) ) # length = numchar 4
echo "${stars}"
for txt in "${words[@]}"
do
printf "* %-*s *n" "${numchar}" "${txt}"
done
echo "${stars}"
Это генерирует:
***********
* Hello *
* World *
* good *
* etceter *
***********
Комментарии:
1. У вас может быть один printf в цикле :
printf "* %-*s *n" $numchar "$txt"
Ответ №3:
Это немного жестко запрограммировано, но думаю, вы поняли идею:
$ echo '***********'; printf '* %-8s*n' "${words[@]}"; echo '***********'
***********
* Hello *
* World *
* good *
* etceter *
***********
Ответ №4:
Вы были на правильном пути, получив сначала длину самой длинной строки.
Границы и отступы могут быть выполнены полностью с printf
помощью спецификаторов формата и замены шаблона. Используется %-Ns
для выравнивания по левому краю и %Ns
для выравнивания по правому краю.
При правильном цитировании вы также можете содержать несколько слов и пробелов в каждой строке.
Вот пример:
lines=('Hello World!'
'Line two.'
a-line
'another line'
'The end.')
# border character
c='*'
# get length of longest line, required for padding
for i in "${lines[@]}"; do
((${#i} > pad)) amp;amp;
pad=${#i}
done
# make a string to fill top/bottom
fill=$(printf %$((pad 4))s "$c")
fill=${fill// /$c}
# print the text box
printf '%sn' "$fill"
for line in "${lines[@]}"; do
printf "$c %-${pad}s $cn" "$line"
done
printf '%sn' "$fill"
Вывод:
****************
* Hello World! *
* Line two. *
* a-line *
* another line *
* The end. *
****************