Граница массива слов в BASH

#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) , если GNU wc (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 over echo -n . Однажды у меня сломалась куча моих скриптов, потому что обновление ОС изменило поведение по умолчанию echo -n для печати «-n» как части его вывода, поэтому я научился не полагаться echo ни на что нетривиальное. Кстати, вы можете просто использовать printf " " and printf "*" (не "%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.     *
****************