не с эхом, а на ту же тему ruby -e 'puts "=" * 100'илиpython -c 'print "=" * 100'
Евгений
1
Отличный вопрос Очень хорошие ответы. Я использовал один из ответов в реальной работе здесь, который я printfseqsvrb=`printf '%.sv' $(seq $vrb)`
доктор Беко
Общее решение для печати чего угодно (1 или более символов, даже включая переводы строки): Repeat_this () {i = 1; while ["$ i" -le "$ 2"]; сделать printf "% s" "$ 1"; i = $ (($ i + 1)); сделано ; printf '\ n';}. Используйте как это: Repeat_this "что-то" Number_of_repetitions. Например, чтобы продемонстрировать повторение 5 раз чего-то, включая 3 перевода строки: Repeat_this "$ (printf '\ n \ n \ nthis')" 5. Окончательный вывод printf '\ n' может быть удален (но я вставил его для создания текстовых файлов, и им нужен символ новой строки в качестве последнего символа!)
Оливье Дюлак
Ответы:
396
Ты можешь использовать:
printf '=%.0s'{1..100}
Как это работает:
Bash расширяет {1..100}, поэтому команда становится:
printf '=%.0s'1234...100
Я установил формат printf, =%.0sкоторый означает, что он всегда будет печатать один, =независимо от того, какой аргумент ему дан. Поэтому печатает 100 =с.
Отличное решение, которое работает достаточно хорошо даже при большом количестве повторений. Вот функция-обертка, которую вы можете вызвать repl = 100, например, ( evalк сожалению, для обоснования расширения скобки на переменной требуется хитрость):repl() { printf "$1"'%.s' $(eval "echo {1.."$(($2))"}"); }
mklement0
7
Можно ли установить верхний предел с помощью var? Я пытался и не могу заставить его работать.
Майк Перселл,
70
Вы не можете использовать переменные в расширении скобки. Используйте seqвместо, например $(seq 1 $limit).
Dogbane
11
Если вы используете эту функцию, лучше переставить ее, $s%.0sчтобы в %.0s$sпротивном случае штрихи вызывали printfошибку.
KomodoDave
5
Это заставило меня заметить поведение Bash printf: он продолжает применять строку формата, пока не останется аргументов. Я предполагал, что он обработал строку формата только один раз!
Jeenu
89
Нет простого пути. Но например:
seq -s=100|tr -d '[:digit:]'
Или, возможно, стандартным способом:
printf %100s|tr " ""="
Там тоже есть tput rep, но что касается моих терминалов под рукой (xterm и linux), то, похоже, они этого не поддерживают :)
Обратите внимание, что первая опция с seq печатает на единицу меньше указанного числа, поэтому в этом примере будет напечатано 99 =символов.
Камило Мартин
13
printftrявляется единственным решением POSIX, потому что seq, yesа {1..3}не POSIX.
Сиро Сантилли 郝海东 郝海东 冠状 六四 事件 法轮功
2
Чтобы повторить строку, а не только один символ: printf %100s | sed 's/ /abc/g'- выводит 'abcabcabc ...'
Джон Рикс
3
+1 за использование без циклов и только одной внешней команды ( tr). Вы также можете расширить его до чего-то вроде printf "%${COLUMNS}s\n" | tr " " "=".
Musiphil
2
@ mklement0 Ну, я надеялся, что ты по ошибке считал последний перевод строки wc. Единственный вывод, который я могу сделать из этого - « seqне должен использоваться».
Примечание. Этот ответ не отвечает на первоначальный вопрос, но дополняет существующие полезные ответы, сравнивая эффективность .
Решения сравниваются только по скорости выполнения - требования к памяти не учитываются (они различаются в зависимости от решения и могут иметь значение при большом количестве повторений).
Резюме:
Если ваш счетчик повторений мал , скажем, около 100, стоит пойти с решениями только для Bash , так как стоимость запуска внешних утилит имеет значение, особенно Perl.
Прагматично говоря, однако, если вам нужен только один экземпляр повторяющихся символов, все существующие решения могут подойти.
При больших подсчетах повторных , использовать внешние утилиты , так как они будут гораздо быстрее.
В частности, избегайте глобальной замены подстроки в Bash большими строками
(например, ${var// /=}), так как это слишком медленно.
Ниже приведены сроки сделанные на iMac в конце 2012 года с процессором Intel Core i5 с тактовой частотой 3,2 ГГц и Fusion Drive, работающие под управлением OSX 10.10.4 и bash 3.2.57, и представляют собой среднее значение 1000 запусков.
Записи:
перечислены в порядке возрастания продолжительности выполнения (сначала быстрее)
Решения Bash-only лидируют - но только с таким небольшим повторением! (увидеть ниже).
Стоимость запуска внешних утилит здесь имеет значение, особенно Perl. Если вы должны вызывать это в цикле - с небольшим количеством повторений в каждой итерации - избегайте использования мультисервиса awkи perlрешений.
Большое количество повторений: 1000000 (1 миллион)
Решение Perl из этого вопроса является самым быстрым.
Глобальная функция замены строк в Bash ${foo// /=}необъяснимо мучительно медленна с большими строками и была выведена из строя (заняла около 50 минут (!) В Bash 4.3.30 и даже дольше в Bash 3.2.57 - я никогда не ждала это до конца).
Циклы Bash медленны, а арифметические циклы ( (( i= 0; ... ))) медленнее, чем развернутые в скобках ( {1..n}) - хотя арифметические циклы более эффективны в памяти.
awkотносится к BSDawk (как и в OSX) - это заметно медленнее, чем gawk(GNU Awk) и особенноmawk .
Обратите внимание, что с большим количеством и мульти-символ. Строки, потребление памяти может стать соображением - подходы отличаются в этом отношении.
Вот скрипт Bash ( 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++));dofor((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=12>/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;doif[[-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"donefidone
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's 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=1if(( skip ));then
echo 'n/a'>&2else
time for(( n =0; n < COUNT_RUNS; n++));do{ printf -v t "%${COUNT_REPETITIONS}s"'='; printf %s "${t// /=}";}>"$outFile"donefi}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
Интересно видеть сравнение синхронизации, но я думаю, что во многих программах выходные данные буферизуются, поэтому их синхронизацию можно изменить, если буферизацию отключить.
Сергей Колодяжный
In order to use brace expansion with a variable, we must use `eval`Py
Пыб
2
Таким образом, решение perl (sid_com) в основном самое быстрое ... как только начальные накладные расходы запуска perl достигнуты. (от маленького повторения до 59 мс - до 67 мс за миллион повторений ... поэтому перкинг perl занял примерно 59 мс в вашей системе)
Оливье Дюлак
46
Есть несколько способов сделать это.
Используя цикл:
Расширение скобок можно использовать с целочисленными литералами:
for i in{1..100};do echo -n =;done
C-подобный цикл позволяет использовать переменные:
При указании точности здесь усекается строка, чтобы соответствовать заданной ширине ( 0). Так printfкак строка формата используется для использования всех аргументов, она просто печатается "="100 раз.
++ для решения head/ tr, которое хорошо работает даже при большом количестве повторений (небольшая оговорка: head -cPOSIX-совместима, но headее реализуют как BSD, так и GNU ); в то время как другие два решения будут медленными в этом случае, они также имеют преимущество работы со строками из нескольких символов.
mklement0
1
Использование yesи head- полезно , если вы хотите определенное количество новых строк: yes "" | head -n 100. trможет заставить его печатать любой символ:yes "" | head -n 100 | tr "\n" "="; echo
loxaxs
Несколько удивительно: dd if=/dev/zero count=1 bs=100000000 | tr '\0' '=' >/dev/nullзначительно медленнее, чем head -c100000000 < /dev/zero | tr '\0' '=' >/dev/nullверсия. Конечно, вы должны использовать размер блока 100M +, чтобы разумно измерить разницу во времени. 100M байтов занимает 1,7 с и 1 с с показанными двумя соответствующими версиями. Я снял tr и просто сбросил его /dev/nullи получил 0,287 с для headверсии и 0,675 с для ddверсии на миллиард байт.
Ах, так что я использовал BSD-версию seq, найденную в OS X. Я обновлю ответ. Какую версию вы используете?
Сэм Солсбери
Я использую seq из GNU coreutils.
Джон Б.
1
@JohnB: BSD seqидет умно переориентирован здесь дублирующие строки : строка формата передается -f- обычно используется для форматирования числа генерируемого - содержит только строку повторить здесь , так что вывод содержит копии только ту строку. К сожалению, GNU seqнастаивает на наличии числового формата в строке формата, что является ошибкой, которую вы видите.
mklement0
1
Красиво сделано; также работает со строками из нескольких символов. Пожалуйста, используйте "$1"(двойные кавычки), чтобы вы также могли передавать такие символы, как '*'и строки со встроенным пробелом. Наконец, если вы хотите иметь возможность использовать его %, вы должны удвоить его (в противном случае вы seqбудете думать, что это часть спецификации формата, например %f); использование "${1//%/%%}"позаботится об этом. Поскольку (как вы упоминаете) вы используете BSDseq , это будет работать на BSD-подобных ОС в целом (например, FreeBSD) - напротив, оно не будет работать на Linux , где используется GNUseq .
mklement0
18
Вот два интересных способа:
Ubuntu @ Ubuntu: ~ $ да = | голова -10 | вставить -s -d '' -
==========
Ubuntu @ Ubuntu: ~ $ да = | голова -10 | tr -d "\ n"
========== убунту @ убунт: ~ $
Обратите внимание, что эти два элемента немного различаются - pasteметод заканчивается новой строкой. trМетод не делает.
Красиво сделано; обратите внимание, что BSDpaste необъяснимо требует -d '\0'указания пустого разделителя и завершается неудачно с -d ''- -d '\0'должен работать со всеми POSIX-совместимыми pasteреализациями и действительно работает и с GNUpaste .
mklement0
Схожи по духу, с меньшим количеством подвесных инструментов:yes | mapfile -n 100 -C 'printf = \#' -c 1
епископ
@bishop: Хотя ваша команда действительно создает на одну оболочку меньше, она все еще медленнее для более высоких значений повторения, а для более низких значений повторения разница, вероятно, не имеет значения; точный порог, вероятно, зависит как от аппаратного обеспечения, так и от ОС, например, на моем компьютере с OSX 10.11.5 этот ответ уже быстрее - 500; попробуй time yes = | head -500 | paste -s -d '\0' -; time yes | mapfile -n 500 -C 'printf = \#' -c 1. Однако, что еще более важно: если вы все printfравно используете , вы можете использовать более простой и более эффективный подход из принятого ответа:printf '%.s=' $(seq 500)
mklement0
13
Простого пути нет. Избегайте использования циклов printfи замены.
Хорошо, но работает разумно только с небольшим количеством повторений. Вот обертка функции, которая может быть вызвана repl = 100, например, как (не выводит завершающий \n):repl() { local ts=$(printf "%${2}s"); printf %s "${ts// /$1}"; }
mklement0
1
@ mklement0 Очень мило с вашей стороны предоставить функциональные версии обоих решений, +1 на оба!
Камило Мартин
8
Если вы хотите 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 1100);do printf "=";done)
... будет производить тот же результат, perl -E 'say "=" x 100'что и везде.
Проблема в том, что seqэто не утилита POSIX (хотя в системах BSD и Linux есть ее реализации) - whileвместо этого вы можете выполнять арифметику оболочки POSIX с помощью цикла, как в ответе @ Xennex81 (с printf "=", как вы правильно предположили, вместо echo -n).
mklement0
1
Ой, ты совершенно прав. Такие вещи иногда проходят мимо меня, потому что этот стандарт не имеет смысла. calэто POSIX. seqне является. В любом случае, вместо того, чтобы переписывать ответ циклом while (как вы говорите, это уже есть в других ответах), я добавлю функцию RYO. Более образовательный таким образом ;-).
Джефф Никсон
8
Вопрос был о том, как это сделать echo:
echo -e ''$_{1..100}'\b='
Это будет делать точно так же, как и perl -E 'say "=" x 100'только с echo.
Чистый способ Bash без eval, без подоболочек, без внешних инструментов, без расширений скобок (т. Е. Вы можете иметь число для повторения в переменной):
Если вам дана переменная, nкоторая расширяется до (неотрицательного) числа и переменная pattern, например,
Для этого маленького трюка мы 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}"}
Интересная вариация для передачи имени переменной. Хотя это решение подходит для счетчиков повторов до примерно 1000 (и, таким образом, вероятно, подходит для большинства реальных приложений, если я догадываюсь), оно становится очень медленным для больших счетчиков (см. Далее комментарий).
mklement0
Кажется, что 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, но все же медленно).
Красиво сделано; это POSIX-совместимый и достаточно быстрый даже с большим количеством повторений, а также поддерживающий многосимвольные строки. Вот версия оболочки: awk 'BEGIN { while (c++ < 100) printf "=" }'. Завернутая в параметризованное функции оболочки (Invoke , как repeat 100 =, например): repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { txt=substr(txt, 2); while (i++ < count) printf txt }'; }. (Пустой .префикс char и дополнительный 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 }'; }.
mklement0
Примечание для пользователей - уловка NF = 100 вызывает ошибку сегмента в старшем awk. Это original-awkимя под Linux более старого awk, похожего на awk BSD, который также сообщал о сбое, если вы хотите попробовать это. Обратите внимание, что сбой обычно является первым шагом к поиску уязвимой ошибки. Этот ответ так продвигает небезопасный код.
2
Примечание для пользователей - original-awkэто нестандартно и не рекомендуется
Стивен Пенни
Альтернативой первому фрагменту кода может быть awk NF=100 OFS='=' <<< ""(использование bashи gawk)
oliv
4
Я предполагаю, что первоначальная цель вопроса состояла в том, чтобы сделать это только с помощью встроенных команд оболочки. Так forпетля и printfs будут законными, в то время как rep, perlи также jotниже не будет. Тем не менее, следующая команда
jot -s "/" -b "\\" $((COLUMNS/2))
например, выводит на экран всю строку \/\/\/\/\/\/\/\/\/\/\/\/
Красиво сделано; это хорошо работает даже при большом количестве повторений (при одновременной поддержке многосимвольных строк). Для того, чтобы лучше проиллюстрировать подход, здесь эквивалент команды OP в: jot -s '' -b '=' 100. Проблема заключается в том , что в то время как BSD-подобных платформах, включая OSX, приходят с jot, дистрибутивы Linux не .
mklement0
1
Спасибо, мне нравится ваше использование -s '' еще лучше. Я изменил свои сценарии.
Стефан Людвиг
На последних системах, основанных на Debian , apt install athena-jotпредоставит jot.
АРУ
4
Как уже говорили другие, расширение bash предшествует расширению параметров , поэтому диапазоны могут содержать только литералы. и предоставляют чистые решения, но не полностью переносимы из одной системы в другую, даже если вы используете одну и ту же оболочку на каждой. (Хотя он становится все более доступным; например, во FreeBSD 9.3 и выше .) И другие формы косвенного обращения всегда работают, но в некоторой степени не элегантны.{m,n}seqjotseqeval
Это принимает количество повторений в качестве первого аргумента и строку для повторения (которая может быть одним символом, как в описании проблемы) в качестве второго аргумента. repecho 7 bвыходы bbbbbbb(заканчиваются переводом строки).
Поскольку основное внимание здесь уделяется повторению одного символа, а оболочка - bash, ее, вероятно, можно использовать echoвместо printf. И я прочитал описание проблемы в этом вопросе как выражение предпочтения печати echo. Приведенное выше определение функции работает в bash и ksh93 . Хотя printfон более переносим (и обычно должен использоваться для такого рода вещей), echoсинтаксис, возможно, более читабелен.
echoВстроенные функции некоторых оболочек интерпретируются -как опция, хотя обычное значение -использования stdin для ввода не имеет смысла echo. Zsh делает это. И определенно существуют echos, которые не распознают -n, поскольку это не стандартно . (Многие оболочки типа Bourne вообще не принимают C-стиль для циклов, поэтому их echoповедение не нужно учитывать ..)
Здесь задача состоит в том, чтобы напечатать последовательность; там , это должно было назначить это переменной.
Если задано $nжелаемое количество повторений, и вам не нужно его повторно использовать, и вы хотите что-то еще более короткое:
while((n--));do echo -n "$s";done; echo
nдолжна быть переменной - этот способ не работает с позиционными параметрами. $sтекст для повторения
Хорошая справочная информация и похвалы за упаковку функциональности как функции, которая работает zsh, кстати, кстати. Подход echo-in-a-loop хорошо работает для меньшего числа повторений, но для больших есть совместимые с POSIX альтернативы, основанные на утилитах , о чем свидетельствует комментарий @ Slomojo.
mklement0
Добавление скобок вокруг вашего более короткого цикла сохраняет значение n без влияния на эхо:(while ((n--)); do echo -n "$s"; done; echo)
используйте printf вместо echo! это намного более переносимо (echo -n может работать только на некоторых системах). см. unix.stackexchange.com/questions/65803/… (один из удивительных ответов Стефана Шазеласа )
Оливье Дюлак
@OlivierDulac Здесь вопрос о bash. Независимо от того, какую операционную систему вы используете, если вы используете bash , в bash есть echoвстроенная поддержка -n. Дух того, что вы говорите, абсолютно верен. printfпочти всегда следует отдавать предпочтение echo, по крайней мере, при неинтерактивном использовании. Но я не думаю, что в каком-то смысле было бы неуместно или вводить в заблуждение дать echoответ на вопрос, который задал один и дал достаточно информации, чтобы понять, что это сработает . Обратите внимание также, что поддержка ((n--))(без $) сама по себе не гарантируется POSIX.
С терминалом ANSI и символами US-ASCII, чтобы повторить. Вы можете использовать управляющую последовательность ANSI CSI. Это самый быстрый способ повторить характер.
lengthне будет работать expr, вы, вероятно, имели в виду n=$(expr 10 - ${#vari}); Однако, это проще и эффективнее использовать арифметическое расширение в Bash: n=$(( 10 - ${#vari} )). Кроме того, в основе вашего ответа лежит тот самый Perl-подход, которому OP ищет альтернативу Bash .
mklement0
1
Это более длинная версия того, что поддерживал Элия Каган:
while[ $(( i--))-gt 0];do echo -n " ";done
Конечно, вы можете использовать printf и для этого, но не совсем так, как мне нравится:
Мой ответ немного сложнее и, вероятно, не идеален, но для тех, кто хочет выводить большие числа, я смог сделать около 10 миллионов за 3 секунды.
repeatString(){# argument 1: The string to print# argument 2: The number of times to print
stringToPrint=$1
length=$2
# Find the largest integer value of x in 2^x=(number of times to repeat) using logarithms
power=`echo "l(${length})/l(2)" | bc -l`
power=`echo "scale=0; ${power}/1" | bc`# Get the difference between the length and 2^x
diff=`echo "${length} - 2^${power}" | bc`# Double the string length to the power of xfor i in`seq "${power}"`;do
stringToPrint="${stringToPrint}${stringToPrint}"done#Since we know that the string is now at least bigger than half the total, grab however many more we need and add it to the string.
stringToPrint="${stringToPrint}${stringToPrint:0:${diff}}"
echo ${stringToPrint}}
Большинство существующих решений все зависит от {1..10}синтаксической поддержки оболочки, которая bash- и zsh- специфический, и не работает tcshили в OpenBSD kshи большинство не-Баш sh.
Следующее должно работать на OS X и всех системах * BSD в любой оболочке; фактически его можно использовать для создания целой матрицы различных видов декоративного пространства:
ruby -e 'puts "=" * 100'
илиpython -c 'print "=" * 100'
printf
seq
svrb=`printf '%.sv' $(seq $vrb)`
Ответы:
Ты можешь использовать:
Как это работает:
Bash расширяет {1..100}, поэтому команда становится:
Я установил формат printf,
=%.0s
который означает, что он всегда будет печатать один,=
независимо от того, какой аргумент ему дан. Поэтому печатает 100=
с.источник
repl = 100
, например, (eval
к сожалению, для обоснования расширения скобки на переменной требуется хитрость):repl() { printf "$1"'%.s' $(eval "echo {1.."$(($2))"}"); }
seq
вместо, например$(seq 1 $limit)
.$s%.0s
чтобы в%.0s$s
противном случае штрихи вызывалиprintf
ошибку.printf
: он продолжает применять строку формата, пока не останется аргументов. Я предполагал, что он обработал строку формата только один раз!Нет простого пути. Но например:
Или, возможно, стандартным способом:
Там тоже есть
tput rep
, но что касается моих терминалов под рукой (xterm и linux), то, похоже, они этого не поддерживают :)источник
=
символов.printf
tr
является единственным решением POSIX, потому чтоseq
,yes
а{1..3}
не POSIX.printf %100s | sed 's/ /abc/g'
- выводит 'abcabcabc ...'tr
). Вы также можете расширить его до чего-то вродеprintf "%${COLUMNS}s\n" | tr " " "="
.wc
. Единственный вывод, который я могу сделать из этого - «seq
не должен использоваться».Кончик шляпы @ gniourf_gniourf за его вклад.
Примечание. Этот ответ не отвечает на первоначальный вопрос, но дополняет существующие полезные ответы, сравнивая эффективность .
Решения сравниваются только по скорости выполнения - требования к памяти не учитываются (они различаются в зависимости от решения и могут иметь значение при большом количестве повторений).
Резюме:
(например,
${var// /=}
), так как это слишком медленно.Ниже приведены сроки сделанные на iMac в конце 2012 года с процессором Intel Core i5 с тактовой частотой 3,2 ГГц и Fusion Drive, работающие под управлением OSX 10.10.4 и bash 3.2.57, и представляют собой среднее значение 1000 запусков.
Записи:
M
... потенциально мульти -character решениеS
... решение, состоящее только из одного символаP
... POSIX-совместимое решениеawk
иperl
решений.${foo// /=}
необъяснимо мучительно медленна с большими строками и была выведена из строя (заняла около 50 минут (!) В Bash 4.3.30 и даже дольше в Bash 3.2.57 - я никогда не ждала это до конца).(( i= 0; ... ))
) медленнее, чем развернутые в скобках ({1..n}
) - хотя арифметические циклы более эффективны в памяти.awk
относится к BSDawk
(как и в OSX) - это заметно медленнее, чемgawk
(GNU Awk) и особенноmawk
.Вот скрипт Bash (
testrepeat
), который произвел выше. Требуется 2 аргумента:Другими словами: приведенные выше сроки были получены с
testrepeat 100 1000
иtestrepeat 1000000 1000
источник
In order to use brace expansion with a variable, we must use `eval`
PyЕсть несколько способов сделать это.
Используя цикл:
Расширение скобок можно использовать с целочисленными литералами:
C-подобный цикл позволяет использовать переменные:
Использование
printf
встроенного:При указании точности здесь усекается строка, чтобы соответствовать заданной ширине (
0
). Такprintf
как строка формата используется для использования всех аргументов, она просто печатается"="
100 раз.Используя
head
(printf
и т. Д.) Иtr
:источник
head
/tr
, которое хорошо работает даже при большом количестве повторений (небольшая оговорка:head -c
POSIX-совместима, ноhead
ее реализуют как BSD, так и GNU ); в то время как другие два решения будут медленными в этом случае, они также имеют преимущество работы со строками из нескольких символов.yes
иhead
- полезно , если вы хотите определенное количество новых строк:yes "" | head -n 100
.tr
может заставить его печатать любой символ:yes "" | head -n 100 | tr "\n" "="; echo
dd if=/dev/zero count=1 bs=100000000 | tr '\0' '=' >/dev/null
значительно медленнее, чемhead -c100000000 < /dev/zero | tr '\0' '=' >/dev/null
версия. Конечно, вы должны использовать размер блока 100M +, чтобы разумно измерить разницу во времени. 100M байтов занимает 1,7 с и 1 с с показанными двумя соответствующими версиями. Я снял tr и просто сбросил его/dev/null
и получил 0,287 с дляhead
версии и 0,675 с дляdd
версии на миллиард байт.dd if=/dev/zero count=1 bs=100000000 | tr '\0' '=' >/dev/null
=>0,21332 s, 469 MB/s
; Для:dd if=/dev/zero count=100 bs=1000000| tr '\0' '=' >/dev/null
=>0,161579 s, 619 MB/s
;Я только что нашел серьезно простой способ сделать это с помощью seq:
ОБНОВЛЕНИЕ: Это работает на BSD,
seq
который идет с OS X. YMMV с другими версиямиНапечатает '#' 10 раз, вот так:
-f "#"
устанавливает строку формата, чтобы игнорировать числа и просто печатать#
для каждого.-s ''
устанавливает разделитель на пустую строку, чтобы удалить символы новой строки, которые seq вставляет между каждым числом-f
и-s
кажутся важными.РЕДАКТИРОВАТЬ: Вот это в удобной функции ...
Который вы можете назвать так ...
ПРИМЕЧАНИЕ: если вы повторяете,
#
цитаты важны!источник
seq: format ‘#’ has no % directive
.seq
для чисел, а не строк. См. Gnu.org/software/coreutils/manual/html_node/seq-invocation.htmlseq
идет умно переориентирован здесь дублирующие строки : строка формата передается-f
- обычно используется для форматирования числа генерируемого - содержит только строку повторить здесь , так что вывод содержит копии только ту строку. К сожалению, GNUseq
настаивает на наличии числового формата в строке формата, что является ошибкой, которую вы видите."$1"
(двойные кавычки), чтобы вы также могли передавать такие символы, как'*'
и строки со встроенным пробелом. Наконец, если вы хотите иметь возможность использовать его%
, вы должны удвоить его (в противном случае выseq
будете думать, что это часть спецификации формата, например%f
); использование"${1//%/%%}"
позаботится об этом. Поскольку (как вы упоминаете) вы используете BSDseq
, это будет работать на BSD-подобных ОС в целом (например, FreeBSD) - напротив, оно не будет работать на Linux , где используется GNUseq
.Вот два интересных способа:
Обратите внимание, что эти два элемента немного различаются -
paste
метод заканчивается новой строкой.tr
Метод не делает.источник
paste
необъяснимо требует-d '\0'
указания пустого разделителя и завершается неудачно с-d ''
--d '\0'
должен работать со всеми POSIX-совместимымиpaste
реализациями и действительно работает и с GNUpaste
.yes | mapfile -n 100 -C 'printf = \#' -c 1
time yes = | head -500 | paste -s -d '\0' -; time yes | mapfile -n 500 -C 'printf = \#' -c 1
. Однако, что еще более важно: если вы всеprintf
равно используете , вы можете использовать более простой и более эффективный подход из принятого ответа:printf '%.s=' $(seq 500)
Простого пути нет. Избегайте использования циклов
printf
и замены.источник
repl = 100
, например, как (не выводит завершающий\n
):repl() { local ts=$(printf "%${2}s"); printf %s "${ts// /$1}"; }
Если вы хотите POSIX-совместимость и согласованность между различными реализациями
echo
иprintf
, и / или оболочек, отличными от простоbash
:... будет производить тот же результат,
perl -E 'say "=" x 100'
что и везде.источник
seq
это не утилита POSIX (хотя в системах BSD и Linux есть ее реализации) -while
вместо этого вы можете выполнять арифметику оболочки POSIX с помощью цикла, как в ответе @ Xennex81 (сprintf "="
, как вы правильно предположили, вместоecho -n
).cal
это POSIX.seq
не является. В любом случае, вместо того, чтобы переписывать ответ циклом while (как вы говорите, это уже есть в других ответах), я добавлю функцию RYO. Более образовательный таким образом ;-).Вопрос был о том, как это сделать
echo
:Это будет делать точно так же, как и
perl -E 'say "=" x 100'
только сecho
.источник
$_1
,$_2
или любая другая из ста переменных имеет значения.Чистый способ Bash без
eval
, без подоболочек, без внешних инструментов, без расширений скобок (т. Е. Вы можете иметь число для повторения в переменной):Если вам дана переменная,
n
которая расширяется до (неотрицательного) числа и переменнаяpattern
, например,Вы можете сделать функцию с этим:
С этим набором:
Для этого маленького трюка мы
printf
довольно часто используем:-v varname
: вместо печати на стандартный вывод,printf
содержимое форматированной строки будет помещено в переменнуюvarname
.printf
будет использовать аргумент для печати соответствующего количества пробелов. Например,printf '%*s' 42
будет напечатано 42 пробела.${var// /$pattern}
расширимся до расширенияvar
со всеми пробелами, замененными расширением$pattern
.Вы также можете избавиться от
tmp
переменной вrepeat
функции, используя косвенное расширение:источник
bash
глобальные операции по замене строк в контексте раскрытия параметров (${var//old/new}
) особенно медленны: мучительно медленные в bash3.2.57
и медленные в bash4.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 секунд (!) В bash3.2.57
и около 0,3 секунды в bash4.3.30
(намного быстрее, чем вкл3.2.57
, но все же медленно).Или
пример
источник
awk 'BEGIN { while (c++ < 100) printf "=" }'
. Завернутая в параметризованное функции оболочки (Invoke , какrepeat 100 =
, например):repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { txt=substr(txt, 2); while (i++ < count) printf txt }'; }
. (Пустой.
префикс char и дополнительныйsubstr
вызов необходимы для обхода ошибки в BSDawk
, когда передача значения переменной, которое начинается с=
NF = 100
Решение очень умный (хотя , чтобы получить 100=
, вы должны использоватьNF = 101
). Предостережений является то, что он выходит из строя BSDawk
(но это очень быстро сgawk
и даже быстрееmawk
), и что POSIX обсуждается ни не назначая доNF
, ни использование полей вBEGIN
блоках. Вы также можете заставить его работать в BSDawk
с небольшим изменением:awk 'BEGIN { OFS = "="; $101=""; print }'
(но, что любопытно, в BSDawk
это не быстрее, чем в циклическом решении). В качестве решения параметризованного оболочки:repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { OFS=substr(txt, 2); $(count+1)=""; print }'; }
.original-awk
имя под Linux более старого awk, похожего на awk BSD, который также сообщал о сбое, если вы хотите попробовать это. Обратите внимание, что сбой обычно является первым шагом к поиску уязвимой ошибки. Этот ответ так продвигает небезопасный код.original-awk
это нестандартно и не рекомендуетсяawk NF=100 OFS='=' <<< ""
(использованиеbash
иgawk
)Я предполагаю, что первоначальная цель вопроса состояла в том, чтобы сделать это только с помощью встроенных команд оболочки. Так
for
петля иprintf
s будут законными, в то время какrep
,perl
и такжеjot
ниже не будет. Тем не менее, следующая командаjot -s "/" -b "\\" $((COLUMNS/2))
например, выводит на экран всю строку
\/\/\/\/\/\/\/\/\/\/\/\/
источник
jot -s '' -b '=' 100
. Проблема заключается в том , что в то время как BSD-подобных платформах, включая OSX, приходят сjot
, дистрибутивы Linux не .apt install athena-jot
предоставитjot
.Как уже говорили другие, расширение bash предшествует расширению параметров , поэтому диапазоны могут содержать только литералы. и предоставляют чистые решения, но не полностью переносимы из одной системы в другую, даже если вы используете одну и ту же оболочку на каждой. (Хотя он становится все более доступным; например, во FreeBSD 9.3 и выше .) И другие формы косвенного обращения всегда работают, но в некоторой степени не элегантны.
{m,n}
seq
jot
seq
eval
К счастью, bash поддерживает C-стиль для циклов (только с арифметическими выражениями). Итак, вот краткий "чистый удар":
Это принимает количество повторений в качестве первого аргумента и строку для повторения (которая может быть одним символом, как в описании проблемы) в качестве второго аргумента.
repecho 7 b
выходыbbbbbbb
(заканчиваются переводом строки).Деннис Уильямсон дал по существу это решение четыре года назад в его прекрасном ответ на создание строки повторяющихся символов в сценарии оболочки . Мое тело функции немного отличается от кода там:
Поскольку основное внимание здесь уделяется повторению одного символа, а оболочка - bash, ее, вероятно, можно использовать
echo
вместоprintf
. И я прочитал описание проблемы в этом вопросе как выражение предпочтения печатиecho
. Приведенное выше определение функции работает в bash и ksh93 . Хотяprintf
он более переносим (и обычно должен использоваться для такого рода вещей),echo
синтаксис, возможно, более читабелен.echo
Встроенные функции некоторых оболочек интерпретируются-
как опция, хотя обычное значение-
использования stdin для ввода не имеет смыслаecho
. Zsh делает это. И определенно существуютecho
s, которые не распознают-n
, поскольку это не стандартно . (Многие оболочки типа Bourne вообще не принимают C-стиль для циклов, поэтому ихecho
поведение не нужно учитывать ..)Здесь задача состоит в том, чтобы напечатать последовательность; там , это должно было назначить это переменной.
Если задано
$n
желаемое количество повторений, и вам не нужно его повторно использовать, и вы хотите что-то еще более короткое:n
должна быть переменной - этот способ не работает с позиционными параметрами.$s
текст для повторенияисточник
printf "%100s" | tr ' ' '='
оптимально.zsh
, кстати, кстати. Подход echo-in-a-loop хорошо работает для меньшего числа повторений, но для больших есть совместимые с POSIX альтернативы, основанные на утилитах , о чем свидетельствует комментарий @ Slomojo.(while ((n--)); do echo -n "$s"; done; echo)
echo
встроенная поддержка-n
. Дух того, что вы говорите, абсолютно верен.printf
почти всегда следует отдавать предпочтениеecho
, по крайней мере, при неинтерактивном использовании. Но я не думаю, что в каком-то смысле было бы неуместно или вводить в заблуждение датьecho
ответ на вопрос, который задал один и дал достаточно информации, чтобы понять, что это сработает . Обратите внимание также, что поддержка((n--))
(без$
) сама по себе не гарантируется POSIX.Python вездесущ и работает везде одинаково.
python -c "import sys; print('*' * int(sys.argv[1]))" "=" 100
Символ и количество передаются как отдельные параметры.
источник
python -c "import sys; print(sys.argv[1] * int(sys.argv[2]))" "=" 100
В Баш 3.0 или выше
источник
Еще один способ повторить произвольную строку n раз:
Плюсы:
Минусы:
yes
команда Gnu Core Utils .С терминалом ANSI и символами US-ASCII, чтобы повторить. Вы можете использовать управляющую последовательность ANSI CSI. Это самый быстрый способ повторить характер.
Или статически:
Выведите строку 80 раз
=
:printf '=\e[80b\n'
Ограничения:
repeat_char
ANSI CSI.repeat_char
ANSI CSI до повторяющегося символа.источник
Вот что я использую для печати строки символов на экране в Linux (в зависимости от терминала / ширины экрана)
Выведите «=» на весь экран:
Объяснение:
Выведите знак равенства столько раз, сколько указано в данной последовательности:
Используйте вывод команды (это функция bash, называемая подстановкой команд):
Дайте последовательность, я использовал от 1 до 20 в качестве примера. В последней команде команда tput используется вместо 20:
Укажите количество столбцов, используемых в данный момент в терминале:
источник
источник
источник
Проще всего использовать эту однострочную строку в csh / tcsh:
источник
Более элегантная альтернатива предлагаемому решению Python может быть:
источник
В случае, если вы хотите повторить символ n раз, равный VARIABLE, в зависимости, скажем, от длины строки, которую вы можете сделать:
Он отображает:
источник
length
не будет работатьexpr
, вы, вероятно, имели в видуn=$(expr 10 - ${#vari})
; Однако, это проще и эффективнее использовать арифметическое расширение в Bash:n=$(( 10 - ${#vari} ))
. Кроме того, в основе вашего ответа лежит тот самый Perl-подход, которому OP ищет альтернативу Bash .Это более длинная версия того, что поддерживал Элия Каган:
Конечно, вы можете использовать printf и для этого, но не совсем так, как мне нравится:
Эта версия совместима с Dash:
где я - начальный номер.
источник
while (( i-- )); do echo -n " "; done
работает.Пробные прогоны
Ссылка на библиотеку по адресу: https://github.com/gdbtek/linux-cookbooks/blob/master/libraries/util.bash
источник
Вы можете сделать это,
echo
еслиecho
послеsed
:На самом деле, это
echo
не нужно там.источник
Мой ответ немного сложнее и, вероятно, не идеален, но для тех, кто хочет выводить большие числа, я смог сделать около 10 миллионов за 3 секунды.
источник
Проще всего использовать эту строку в bash:
источник
Другой вариант - использовать GNU seq и удалить все числа и новые строки, которые он генерирует:
Эта команда печатает
#
символ 100 раз.источник
Большинство существующих решений все зависит от
{1..10}
синтаксической поддержки оболочки, котораяbash
- иzsh
- специфический, и не работаетtcsh
или в OpenBSDksh
и большинство не-Башsh
.Следующее должно работать на OS X и всех системах * BSD в любой оболочке; фактически его можно использовать для создания целой матрицы различных видов декоративного пространства:
К сожалению, мы не получаем завершающий перевод строки; который может быть исправлен дополнительно
printf '\n'
после сгиба:Ссылки:
источник
Мое предложение (принимая значения переменных для n):
источник