Как я могу повторить символ в Bash?

180

Как я могу сделать это с помощью echo?

perl -E 'say "=" x 100'
  • 1
    возможный дубликат сценария оболочки, создающий строку повторяющихся символов
  • 0
    К сожалению, это не Баш.
Показать ещё 2 комментария
Теги:
echo

22 ответа

317
Лучший ответ

Вы можете использовать:

printf '=%.0s' {1..100}

Как это работает:

Bash расширяет {1..100}, поэтому команда становится:

printf '=%.0s' 1 2 3 4 ... 100

Я установил формат printf в =%.0s, что означает, что он всегда будет печатать один = независимо от того, какой аргумент он задает. Поэтому он печатает 100 = s.

  • 11
    Отличное решение, которое работает достаточно хорошо даже при большом количестве повторений. Вот функция-обертка, которую вы можете вызвать с repl = 100 , например (к сожалению, для обоснования расширения скобки на переменной требуется eval trickery): repl() { printf "$1"'%.s' $(eval "echo {1.."$(($2))"}"); }
  • 4
    Можно ли установить верхний предел, используя переменную? Я пытался и не могу заставить его работать.
Показать ещё 15 комментариев
74

Нет простого способа. Но, например:

seq -s= 100|tr -d '[:digit:]'

Или, может быть, стандартный способ:

printf %100s |tr " " "="

Там также есть tput rep, но что касается моих терминалов под рукой (xterm и linux), они, похоже, не поддерживают его:)

  • 2
    Обратите внимание, что первая опция с seq печатает на единицу меньше указанного числа, поэтому в этом примере будет напечатано 99 = символов.
  • 0
    printf %100s отлично, если вам просто нужны пробелы - спасибо!
Показать ещё 8 комментариев
38

Там более одного способа сделать это.

Использование цикла:

  • Расширение скобки можно использовать с целыми литералами:

    for i in {1..100}; do echo -n =; done    
    
  • C-подобный цикл позволяет использовать переменные:

    start=1
    end=100
    for ((i=$start; i<=$end; i++)); do echo -n =; done
    

Использование printf builtin:

printf '=%.0s' {1..100}

Указание точности здесь обрезает строку, чтобы она соответствовала указанной ширине (0). Поскольку printf повторно использует строку формата для использования всех аргументов, это просто печатает "=" 100 раз.

Использование head (printf и т.д.) и tr:

head -c 100 < /dev/zero | tr '\0' '='
printf %100s | tr " " "="
  • 1
    ++ для решения head / tr , которое хорошо работает даже при большом количестве повторений (небольшое предостережение: head -c не соответствует POSIX, но его реализуют как BSD, так и GNU head ); в то время как два других решения будут медленными в этом случае, они также имеют преимущество работы со строками из нескольких символов.
  • 0
    Использование yes и head - полезно, если вы хотите определенное количество новых строк: yes "" | head -n 100 . tr может заставить его печатать любой символ: yes "" | head -n 100 | tr "\n" "="; echo
Показать ещё 2 комментария
34

Совет для шляпы @gniourf_gniourf для его ввода.

Примечание. Этот ответ не отвечает на исходный вопрос, но дополняет существующие, полезные ответы, сравнивая производительность.

Решения сравниваются только с точки зрения скорости выполнения - требования к памяти не принимаются во внимание (они различаются между решениями и могут иметь значение с большим количеством повторений).

Резюме:

  • Если количество повторений меньше, скажем, около 100, стоит пойти с Bash -онными решениями, поскольку затраты на запуск внешних коммунальных услуг, особенно Perl.
    • Однако, если вам нужен только один экземпляр повторяющихся символов, все существующие решения могут быть точными.
  • С большим количеством повторений, используйте внешние утилиты, так как они будут намного быстрее.
    • В частности, избегайте замены глобальной подстроки Bash большими строками
      (например, ${var// /=}), поскольку он является слишком медленным.

Ниже приведены timings, принятые на iMac в конце 2012 года с процессором Intel Core i5 с тактовой частотой 3,2 ГГц и Fusion Drive с OSX 10.10.4 и Bash 3.2.57, и это в среднем 1000 прогонов.

Записи:

  • отображается в порядке возрастания продолжительности выполнения (быстрее всего)
  • с префиксом:
    • M... потенциально многосимвольное решение
    • S... односимвольное решение
    • P... POSIX-совместимое решение
  • а затем краткое описание решения
  • суффикс с именем автора исходящего ответа

  • Малый кол-во повторений: 100
[M, P] printf %.s= [dogbane]:                           0.0002
[M   ] printf + bash global substr. replacement [Tim]:  0.0005
[M   ] echo -n - brace expansion loop [eugene y]:       0.0007
[M   ] echo -n - arithmetic loop [Eliah Kagan]:         0.0013
[M   ] seq -f [Sam Salisbury]:                          0.0016
[M   ] jot -b [Stefan Ludwig]:                          0.0016
[M   ] awk - $(count+1)="=" [Steven Penny (variant)]:   0.0019
[M, P] awk - while loop [Steven Penny]:                 0.0019
[S   ] printf + tr [user332325]:                        0.0021
[S   ] head + tr [eugene y]:                            0.0021
[S, P] dd + tr [mklement0]:                             0.0021
[M   ] printf + sed [user332325 (comment)]:             0.0021
[M   ] mawk - $(count+1)="=" [Steven Penny (variant)]:  0.0025
[M, P] mawk - while loop [Steven Penny]:                0.0026
[M   ] gawk - $(count+1)="=" [Steven Penny (variant)]:  0.0028
[M, P] gawk - while loop [Steven Penny]:                0.0028
[M   ] yes + head + tr [Digital Trauma]:                0.0029
[M   ] Perl [sid_com]:                                  0.0059
  • Bash - единственные решения ведут пакет - но только с повторением подсчитайте это маленькое! (см. ниже).
  • Стоимость запуска внешних утилит имеет значение здесь, особенно Perl. Если вы должны вызывать это в цикле - с небольшим количеством повторений на каждой итерации - избегайте решений с несколькими утилитами, awk и perl.

  • Большое количество повторений: 1000000 (1 миллион)
[M   ] Perl [sid_com]:                                  0.0067
[M   ] mawk - $(count+1)="=" [Steven Penny (variant)]:  0.0254
[M   ] gawk - $(count+1)="=" [Steven Penny (variant)]:  0.0599
[S   ] head + tr [eugene y]:                            0.1143
[S, P] dd + tr [mklement0]:                             0.1144
[S   ] printf + tr [user332325]:                        0.1164
[M, P] mawk - while loop [Steven Penny]:                0.1434
[M   ] seq -f [Sam Salisbury]:                          0.1452
[M   ] jot -b [Stefan Ludwig]:                          0.1690
[M   ] printf + sed [user332325 (comment)]:             0.1735
[M   ] yes + head + tr [Digital Trauma]:                0.1883
[M, P] gawk - while loop [Steven Penny]:                0.2493
[M   ] awk - $(count+1)="=" [Steven Penny (variant)]:   0.2614
[M, P] awk - while loop [Steven Penny]:                 0.3211
[M, P] printf %.s= [dogbane]:                           2.4565
[M   ] echo -n - brace expansion loop [eugene y]:       7.5877
[M   ] echo -n - arithmetic loop [Eliah Kagan]:         13.5426
[M   ] printf + bash global substr. replacement [Tim]:  n/a
  • Решение Perl из вопроса является самым быстрым.
  • Bash глобальная замена строки (${foo// /=}) необъяснимо мучительно медленна с большими строками и выведена из работы (заняла около 50 минут (!) в Bash 4.3.30 и даже дольше в Bash 3.2.57 - я никогда не ждал, пока он закончится).
  • Bash петли медленны, а арифметические циклы ((( i= 0; ... ))) медленнее, чем расширенные с помощью скобок ({1..n}), хотя арифметические циклы более эффективны с точки зрения памяти.
  • awk относится к BSD awk (также найденному на OSX) - он заметно медленнее, чем gawk (GNU Awk) и особенно mawk.
  • Обратите внимание, что с большими значениями и multi- char. строки, потребление памяти может стать предметом рассмотрения - в этом отношении разные подходы.

Здесь Bash script (testrepeat), который создал выше. Он принимает 2 аргумента:

  • количество повторений символов
  • опционально, количество тестовых прогонов для выполнения и вычисление среднего времени с

Иными словами: приведенные выше тайминги были получены с помощью testrepeat 100 1000 и testrepeat 1000000 1000

#!/usr/bin/env bash

title() { printf '%s:\t' "$1"; }

TIMEFORMAT=$'%6Rs'

# The number of repetitions of the input chars. to produce
COUNT_REPETITIONS=${1?Arguments: <charRepeatCount> [<testRunCount>]}

# The number of test runs to perform to derive the average timing from.
COUNT_RUNS=${2:-1}

# Discard the (stdout) output generated by default.
# If you want to check the results, replace '/dev/null' on the following
# line with a prefix path to which a running index starting with 1 will
# be appended for each test run; e.g., outFilePrefix='outfile', which
# will produce outfile1, outfile2, ...
outFilePrefix=/dev/null

{

  outFile=$outFilePrefix
  ndx=0

  title '[M, P] printf %.s= [dogbane]'
  [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
  # !! In order to use brace expansion with a variable, we must use `eval`.
  eval "
  time for (( n = 0; n < COUNT_RUNS; n++ )); do 
    printf '%.s=' {1..$COUNT_REPETITIONS} >"$outFile"
  done"

  title '[M   ] echo -n - arithmetic loop [Eliah Kagan]'
  [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
  time for (( n = 0; n < COUNT_RUNS; n++ )); do 
    for ((i=0; i<COUNT_REPETITIONS; ++i)); do echo -n =; done >"$outFile"
  done


  title '[M   ] echo -n - brace expansion loop [eugene y]'
  [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
  # !! In order to use brace expansion with a variable, we must use `eval`.
  eval "
  time for (( n = 0; n < COUNT_RUNS; n++ )); do 
    for i in {1..$COUNT_REPETITIONS}; do echo -n =; done >"$outFile"
  done
  "

  title '[M   ] printf + sed [user332325 (comment)]'
  [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
  time for (( n = 0; n < COUNT_RUNS; n++ )); do 
    printf "%${COUNT_REPETITIONS}s" | sed 's/ /=/g' >"$outFile"
  done


  title '[S   ] printf + tr [user332325]'
  [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
  time for (( n = 0; n < COUNT_RUNS; n++ )); do 
    printf "%${COUNT_REPETITIONS}s" | tr ' ' '='  >"$outFile"
  done


  title '[S   ] head + tr [eugene y]'
  [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
  time for (( n = 0; n < COUNT_RUNS; n++ )); do 
    head -c $COUNT_REPETITIONS < /dev/zero | tr '\0' '=' >"$outFile"
  done


  title '[M   ] seq -f [Sam Salisbury]'
  [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
  time for (( n = 0; n < COUNT_RUNS; n++ )); do 
    seq -f '=' -s '' $COUNT_REPETITIONS >"$outFile"
  done


  title '[M   ] jot -b [Stefan Ludwig]'
  [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
  time for (( n = 0; n < COUNT_RUNS; n++ )); do 
    jot -s '' -b '=' $COUNT_REPETITIONS >"$outFile"
  done


  title '[M   ] yes + head + tr [Digital Trauma]'
  [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
  time for (( n = 0; n < COUNT_RUNS; n++ )); do 
    yes = | head -$COUNT_REPETITIONS | tr -d '\n'  >"$outFile"
  done

  title '[M   ] Perl [sid_com]'
  [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
  time for (( n = 0; n < COUNT_RUNS; n++ )); do 
    perl -e "print \"=\" x $COUNT_REPETITIONS" >"$outFile"
  done

  title '[S, P] dd + tr [mklement0]'
  [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
  time for (( n = 0; n < COUNT_RUNS; n++ )); do 
    dd if=/dev/zero bs=$COUNT_REPETITIONS count=1 2>/dev/null | tr '\0' "=" >"$outFile"
  done

  # !! On OSX, awk is BSD awk, and mawk and gawk were installed later.
  # !! On Linux systems, awk may refer to either mawk or gawk.
  for awkBin in awk mawk gawk; do
    if [[ -x $(command -v $awkBin) ]]; then

      title "[M   ] $awkBin"' - $(count+1)="=" [Steven Penny (variant)]'
      [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
      time for (( n = 0; n < COUNT_RUNS; n++ )); do 
        $awkBin -v count=$COUNT_REPETITIONS 'BEGIN { OFS="="; $(count+1)=""; print }' >"$outFile"
      done

      title "[M, P] $awkBin"' - while loop [Steven Penny]'
      [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
      time for (( n = 0; n < COUNT_RUNS; n++ )); do 
        $awkBin -v count=$COUNT_REPETITIONS 'BEGIN { while (i++ < count) printf "=" }' >"$outFile"
      done

    fi
  done

  title '[M   ] printf + bash global substr. replacement [Tim]'
  [[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
  # !! In Bash 4.3.30 a single run with repeat count of 1 million took almost
  # !! 50 *minutes*(!) to complete; n Bash 3.2.57 it seemingly even slower -
  # !! didn't wait for it to finish.
  # !! Thus, this test is skipped for counts that are likely to be much slower
  # !! than the other tests.
  skip=0
  [[ $BASH_VERSINFO -le 3 && COUNT_REPETITIONS -gt 1000 ]] && skip=1
  [[ $BASH_VERSINFO -eq 4 && COUNT_REPETITIONS -gt 10000 ]] && skip=1
  if (( skip )); then
    echo 'n/a' >&2
  else
    time for (( n = 0; n < COUNT_RUNS; n++ )); do 
      { printf -v t "%${COUNT_REPETITIONS}s" '='; printf %s "${t// /=}"; } >"$outFile"
    done
  fi
} 2>&1 | 
 sort -t$'\t' -k2,2n | 
   awk -F $'\t' -v count=$COUNT_RUNS '{ 
    printf "%s\t", $1; 
    if ($2 ~ "^n/a") { print $2 } else { printf "%.4f\n", $2 / count }}' |
     column -s$'\t' -t
  • 0
    Интересно видеть сравнение синхронизации, но я думаю, что во многих программах выходные данные буферизируются, поэтому их синхронизацию можно изменить, если буферизацию отключить.
24

Я только что нашел очень простой способ сделать это с помощью seq:

UPDATE: это работает на BSD seq, который поставляется с OS X. YMMV с другими версиями

seq  -f "#" -s '' 10

Будет напечатан "#" 10 раз, например:

##########
  • -f "#" устанавливает строку формата, чтобы игнорировать числа и просто печатать # для каждого из них.
  • -s '' устанавливает разделитель в пустую строку, чтобы удалить символы новой строки, которые seq вставляет между каждым числом
  • Пространства после -f и -s кажутся важными.

EDIT: здесь это удобно...

repeat () {
    seq  -f $1 -s '' $2; echo
}

Что вы можете назвать так...

repeat "#" 10

ПРИМЕЧАНИЕ. Если вы повторяете #, то кавычки важны!

  • 7
    Это дает мне seq: format '#' has no % directive . seq для чисел, а не для строк См. Gnu.org/software/coreutils/manual/html_node/seq-invocation.html.
  • 0
    Ах, так что я использовал BSD-версию seq, найденную в OS X. Я обновлю ответ. Какую версию вы используете?
Показать ещё 5 комментариев
16

Вот два интересных способа:

ubuntu@ubuntu:~$ yes = | head -10 | paste -s -d '' -
==========
ubuntu@ubuntu:~$ yes = | head -10 | tr -d "\n"
==========ubuntu@ubuntu:~$ 

Обратите внимание, что эти два метода несколько отличаются. Метод paste заканчивается в новой строке. Метод tr не работает.

  • 1
    Красиво сделано; Пожалуйста , обратите внимание , что BSD paste необъяснимо требует -d '\0' для указания пустого разделителя, и не с -d '' - -d '\0' должны работать остроумие всех POSIX-совместимые paste реализаций и действительно работает с GNU paste тоже.
  • 0
    Схожи по духу, с меньшим количеством подвесных инструментов: yes | mapfile -n 100 -C 'printf = \#' -c 1
Показать ещё 1 комментарий
10

Нет простого способа. Избегайте циклов, используя printf и замену.

str=$(printf "%40s")
echo ${str// /rep}
# echoes "rep" 40 times.
  • 2
    Хорошо, но работает разумно только с небольшим количеством повторений. Вот, например, оболочка функции, которая может быть вызвана как repl = 100 (не выводит завершающий \n ): repl() { local ts=$(printf "%${2}s"); printf %s "${ts// /$1}"; }
  • 1
    @ mklement0 Очень мило с вашей стороны предоставить функциональные версии обоих решений, +1 на оба!
7
#!/usr/bin/awk -f
BEGIN {
  OFS = "="
  NF = 100
  print
}

Или же

#!/usr/bin/awk -f
BEGIN {
  while (z++ < 100) printf "="
}

пример

  • 3
    Красиво сделано; это POSIX-совместимый и достаточно быстрый даже с большим количеством повторений, а также поддерживающий многосимвольные строки. Вот версия оболочки: awk 'BEGIN { while (c++ < 100) printf "=" }' . Оборачивается в параметризованную функцию оболочки (например, вызывается как repeat 100 = ): repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { txt=substr(txt, 2); while (i++ < count) printf txt }'; } (Фиктивный . Префикс символ и дополняют друг друг substr вызов необходимы , чтобы обойти ошибки в BSD awk , где проходящее значение переменного , которая начинается с = разрывает команду.)
  • 1
    Решение NF = 100 очень умное (хотя, чтобы получить 100 = , вы должны использовать NF = 101 ). Предостережения в том, что он аварийно завершает работу BSD awk (но это очень быстро с gawk и даже быстрее с mawk ), и что POSIX не обсуждает ни назначение NF , ни использование полей в блоках BEGIN . Вы также можете заставить его работать в BSD awk с небольшим изменением: awk 'BEGIN { OFS = "="; $101=""; print }' (но, что любопытно, в BSD awk это не быстрее, чем в цикле). Как параметризованное решение для оболочки: repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { OFS=substr(txt, 2); $(count+1)=""; print }'; }
Показать ещё 3 комментария
5

Если вы хотите, чтобы соответствие и согласованность POSIX для разных реализаций echo и printf и/или оболочек, отличных от bash:

seq(){ n=$1; while [ $n -le $2 ]; do echo $n; n=$((n+1)); done ;} # If you don't have it.

echo $(for each in $(seq 1 100); do printf "="; done)

... будет выдавать тот же результат, что и perl -E 'say "=" x 100' примерно везде.

  • 1
    Проблема заключается в том, что seq не является утилита POSIX (хотя BSD и Linux системы имеют реализацию этого) - вы можете сделать POSIX оболочки арифметику с в while петли вместо этого, как в @ ответ Xennex81 ( в с printf "=" , как вы правильно предложить , а не echo -n ).
  • 1
    Ой, ты совершенно прав. Такие вещи иногда проходят мимо меня, потому что этот стандарт не имеет смысла. cal это POSIX. seq нет. В любом случае, вместо того, чтобы переписывать ответ с помощью цикла while (как вы говорите, это уже есть в других ответах), я добавлю функцию RYO. Более образовательный способ ;-).
4

Я предполагаю, что первоначальная цель вопроса состояла в том, чтобы сделать это только с помощью встроенных команд оболочки. Таким образом, циклы for и printf будут законными, а rep, perl, а также jot ниже - нет. Тем не менее, следующая команда

jot -s "/" -b "\\" $((COLUMNS/2))

например, печатает всю ширину окна \/\/\/\/\/\/\/\/\/\/\/\/

  • 2
    Красиво сделано; это хорошо работает даже при большом количестве повторений (при одновременной поддержке многосимвольных строк). Чтобы лучше проиллюстрировать подход, вот эквивалент команды OP: jot -s '' -b '=' 100 . Предостережение заключается в том, что, хотя BSD-подобные платформы, включая OSX, поставляются с jot , Linux-дистрибутивы этого не делают .
  • 1
    Спасибо, мне нравится ваше использование -s '' еще лучше. Я изменил свои сценарии.
Показать ещё 1 комментарий
3

Как говорили другие, в bash расширение брекета предшествует расширение параметра, поэтому диапазоны {m,n} могут содержать только литералы. seq и jot обеспечивают чистые решения но не полностью переносимы из одной системы в другую, даже если вы используете одну и ту же оболочку для каждого. (Хотя seq становится все более доступным, например в FreeBSD 9.3 и выше. eval, и другие формы косвенности всегда работают, но несколько неэлегантные.

К счастью, bash поддерживает C-стиль для циклов (только с арифметическими выражениями). Итак, вот краткий "чистый bash" способ:

repecho() { for ((i=0; i<$1; ++i)); do echo -n "$2"; done; echo; }

Это принимает количество повторений в качестве первого аргумента и повторяющуюся строку (которая может быть одним символом, как в описании проблемы) в качестве второго аргумента. repecho 7 b выводит bbbbbbb (завершается символом новой строки).

Деннис Уильямсон дал принципиально это решение четыре года назад в своем превосходном ответе до Создание строки повторяющихся символов в оболочке script. Мое функциональное тело немного отличается от кода:

  • Поскольку основное внимание здесь уделяется повторению одного символа, а оболочка bash, вероятно, безопасно использовать echo вместо printf. И я прочитал описание проблемы в этом вопросе как выражение предпочтения печатать с echo. Вышеуказанное определение функции работает в bash и ksh93. Хотя printf более портативен (и обычно должен использоваться для такого рода вещей), синтаксис echo, возможно, более читабельен.

    Некоторые оболочки echo встроены в интерпретацию - сами по себе как опцию - хотя обычный смысл -, чтобы использовать stdin для ввода, для t28 не имеет смысла. zsh делает это. И определенно существует echo, которые не распознают -n, поскольку он не является стандартным. (Многие оболочки в стиле Бурна вообще не принимают C-стиль для циклов, поэтому их поведение echo не нужно учитывать.)

  • Здесь задача состоит в том, чтобы напечатать последовательность; там, он должен был назначить его переменной.

Если $n - желаемое количество повторений, и вам не нужно его повторно использовать, и вы хотите что-то еще более короткое:

while ((n--)); do echo -n "$s"; done; echo

n должен быть переменной - этот способ не работает с позиционными параметрами. $s - текст, который необходимо повторить.

  • 1
    Категорически не используйте циклические версии. printf "%100s" | tr ' ' '=' является оптимальным.
  • 0
    Хорошая справочная информация и благодарность за упаковку функциональности как функции, которая, кстати, работает и в zsh . Подход echo-in-a-loop хорошо работает для меньшего количества повторений, но для больших есть совместимые с POSIX альтернативы, основанные на утилитах , о чем свидетельствует комментарий @ Slomojo.
Показать ещё 1 комментарий
2
repeat() {
    # $1=number of patterns to repeat
    # $2=pattern
    printf -v "TEMP" '%*s' "$1"
    echo ${TEMP// /$2}
}
2

Чистый Bash способ без eval, без подоболочек, без внешних инструментов, без расширений (например, вы можете иметь число для повторения в переменной):

Если вам задана переменная n, которая расширяется до (неотрицательного) числа и переменной pattern, например,

$ n=5
$ pattern=hello
$ printf -v output '%*s' "$n"
$ output=${output// /$pattern}
$ echo "$output"
hellohellohellohellohello

Вы можете сделать функцию с этим:

repeat() {
    # $1=number of patterns to repeat
    # $2=pattern
    # $3=output variable name
    local tmp
    printf -v tmp '%*s' "$1"
    printf -v "$3" '%s' "${tmp// /$2}"
}

С этим набором:

$ repeat 5 hello output
$ echo "$output"
hellohellohellohellohello

Для этого небольшого трюка мы используем printf довольно много:

  • -v varname: вместо печати на стандартный вывод printf будет помещать содержимое форматированной строки в переменную varname.
  • '% * s': printf будет использовать аргумент для печати соответствующего количества пробелов. Например, printf '%*s' 42 будет печатать 42 пробела.
  • Наконец, когда у нас есть требуемое число пробелов в нашей переменной, мы используем расширение параметра для замены всех пространств нашим шаблоном: ${var// /$pattern} будет расширяться до расширения var со всеми замененными пространствами расширение $pattern.

Вы также можете избавиться от переменной tmp в функции repeat, используя косвенное расширение:

repeat() {
    # $1=number of patterns to repeat
    # $2=pattern
    # $3=output variable name
    printf -v "$3" '%*s' "$1"
    printf -v "$3" '%s' "${!3// /$2}"
}
  • 0
    Интересная вариация для передачи имени переменной. Хотя это решение подходит для счетчиков повторов до примерно 1000 (и, таким образом, вероятно, подходит для большинства реальных приложений, если я догадываюсь), оно становится очень медленным для больших счетчиков (см. Далее комментарий).
  • 0
    Кажется, что глобальные операции замены строк в bash в контексте раскрытия параметров ( ${var//old/new} ) особенно медленные: мучительно медленные в bash 3.2.57 и медленные в bash 4.3.30 , по крайней мере, на моя система OSX 10.10.3 на машине Intel Core i5 с тактовой частотой 3,2 ГГц: при подсчете 1000 все происходит медленно ( 3.2.57 ) / быстро ( 4.3.30 ): 0,1 / 0,004 секунды. Увеличение числа до 10000 приводит к поразительно другим числам: repeat 10000 = var занимает около 80 секунд (!) В bash 3.2.57 и около 0,3 секунды в bash 4.3.30 (намного быстрее, чем в 3.2.57 , но все еще медленно).
2
for i in {1..100}
do
  echo -n '='
done
echo
2

В bash 3.0 или выше

for i in {1..100};do echo -n =;done
1

Вопрос был о том, как это сделать с помощью echo:

echo -e ''$_{1..100}'\b='

Это будет сделано точно так же, как perl -E 'say "=" x 100' но только с echo.

1

Это более длинная версия того, что Элия Каган поддерживала:

while [ $(( i-- )) -gt 0 ]; do echo -n "  "; done

Конечно, вы можете использовать printf для этого, но не очень мне нравится:

printf "%$(( i*2 ))s"

Эта версия совместима с Dash:

until [ $(( i=i-1 )) -lt 0 ]; do echo -n "  "; done

где я - начальное число.

  • 0
    В bash и с положительным n: while (( i-- )); do echo -n " "; done работы.
1

Если вы хотите повторить символ n раз, когда n имеет значение VARIABLE, количество раз, в зависимости от длины строки, которую вы можете сделать:

#!/bin/bash
vari='AB'
n=$(expr 10 - length $vari)
echo 'vari equals.............................: '$vari
echo 'Up to 10 positions I must fill with.....: '$n' equal signs'
echo $vari$(perl -E 'say "=" x '$n)

Отображается:

vari equals.............................: AB  
Up to 10 positions I must fill with.....: 8 equal signs  
AB========  
  • 0
    length не будет работать с expr , вы, вероятно, имели в виду n=$(expr 10 - ${#vari}) ; однако проще и эффективнее использовать арифметическое расширение Баша: n=$(( 10 - ${#vari} )) . Кроме того, в основе вашего ответа лежит тот самый Perl-подход, которому OP ищет альтернативу Bash.
0

Как я мог сделать это с эхо?

Вы можете сделать это с помощью echo если за echo следует sed:

echo | sed -r ':a s/^(.*)$/=\1/; /^={100}$/q; ba'

На самом деле, это echo там не нужно.

0

Проще всего использовать эту однострочную строку в csh/tcsh:

printf "%50s\n" '' | tr '[:blank:]' '[=]'

0
function repeatString()
{
    local -r string="${1}"
    local -r numberToRepeat="${2}"

    if [[ "${string}" != '' && "${numberToRepeat}" =~ ^[1-9][0-9]*$ ]]
    then
        local -r result="$(printf "%${numberToRepeat}s")"
        echo -e "${result// /${string}}"
    fi
}

Пробные прогоны

$ repeatString 'a1' 10 
a1a1a1a1a1a1a1a1a1a1

$ repeatString 'a1' 0 

$ repeatString '' 10 

Ссылка на библиотеку по адресу: https://github.com/gdbtek/linux-cookbooks/blob/master/libraries/util.bash

0

Python вездесущ и работает везде одинаково.

python -c "import sys; print('*' * int(sys.argv[1]))" "=" 100

Символ и число передаются как отдельные параметры.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню