não com eco, mas sobre o mesmo tema ruby -e 'puts "=" * 100'oupython -c 'print "=" * 100'
Evgeny
1
Ótima pergunta. Respostas muito boas. Eu usei uma das respostas em um trabalho real aqui, que vou postar como exemplo: github.com/drbeco/oldfiles/blob/master/oldfiles (usado printfcom seq)svrb=`printf '%.sv' $(seq $vrb)`
Dr. Beco
Uma solução genérica para imprimir qualquer que seja (1 ou mais caracteres, inclusive novas linhas): Repeat_this () {i = 1; enquanto ["$ i" -le "$ 2"]; imprimaf "% s" "$ 1"; i = $ (($ i + 1)); feito ; printf '\ n';}. Use desta forma: Repita_este "algo" Número_de_repetições. Por exemplo, para mostrar 5 vezes a repetição, incluindo 3 novas linhas: Repeat_this "$ (printf '\ n \ n \ n \ nthis')" 5. O printf final \ \ n 'pode ser retirado (mas eu o coloquei para criar arquivos de texto, e eles precisam de uma nova linha como último caractere!)
Olivier Dulac
Respostas:
396
Você pode usar:
printf '=%.0s'{1..100}
Como isso funciona:
O Bash se expande {1..100} para que o comando se torne:
printf '=%.0s'1234...100
Eu configurei o formato printf para o =%.0sque significa que ele sempre imprimirá um único, =independentemente do argumento que for fornecido. Portanto, imprime 100 =s.
Ótima solução que executa razoavelmente bem, mesmo com grandes contagens de repetição. Aqui está uma função wrapper você pode invocar com repl = 100, por exemplo ( evalé exigido truques, infelizmente, para basear a expansão cinta em uma variável):repl() { printf "$1"'%.s' $(eval "echo {1.."$(($2))"}"); }
mklement0
7
É possível definir o limite superior usando um var? Eu tentei e não consigo fazê-lo funcionar.
Mike Purcell
70
Você não pode usar variáveis na expansão de chaves. Use, seqpor exemplo, por exemplo $(seq 1 $limit).
dogbane
11
Se você funcionalizar isso, é melhor reorganizá-lo $s%.0spara %.0s$shífen, causando um printferro.
KomodoDave 30/07/2014
5
Isso me fez notar um comportamento do Bash printf: ele continua a aplicar a string de formato até que não haja mais argumentos. Eu tinha assumido que processou a string de formato apenas uma vez!
Jeenu
89
Não é fácil. Mas por exemplo:
seq -s=100|tr -d '[:digit:]'
Ou talvez uma maneira de conformidade padrão:
printf %100s|tr " ""="
Há também um tput rep, mas quanto aos meus terminais em mãos (xterm e linux), eles não parecem suportá-lo :)
Observe que a primeira opção com seq imprime um a menos que o número fornecido, portanto esse exemplo imprime 99 =caracteres.
Camilo Martin
13
printftré a única solução POSIX porque seq, yese {1..3}não é POSIX.
Ciro Santilli escreveu:
2
Para repetir uma seqüência de caracteres em vez de apenas um caractere: printf %100s | sed 's/ /abc/g'- gera 'abcabcabc ...'
John Rix
3
+1 por não usar loops e apenas um comando externo ( tr). Você também pode estendê-lo a algo como printf "%${COLUMNS}s\n" | tr " " "=".
musiphil
2
@ mklement0 Bem, eu esperava que você estivesse contando a última nova linha por engano wc. A única conclusão que posso tirar disso é " seqnão deve ser usada".
Camilo Martin
51
Ponta do chapéu para @ gniourf_gniourf por sua contribuição.
Nota: Esta resposta não responde à pergunta original, mas complementa as respostas úteis existentes comparando o desempenho .
As soluções são comparadas apenas em termos de velocidade de execução - os requisitos de memória não são levados em consideração (eles variam entre as soluções e podem importar com grandes contagens de repetição).
Resumo:
Se sua contagem de repetições for pequena , digamos até cerca de 100, vale a pena usar as soluções Bash-only , já que o custo de inicialização de utilitários externos é importante, principalmente do Perl.
Pragmaticamente falando, no entanto, se você precisar apenas de uma instância de caracteres repetidos, todas as soluções existentes podem ser boas.
Com grandes contagens de repetição , use utilitários externos , pois eles serão muito mais rápidos.
Em particular, evite a substituição global de substring do Bash por grandes cadeias
(por exemplo, ${var// /=}), pois é proibitivamente lento.
A seguir, são mostrados os tempos do iMac de final de 2012 com uma CPU Intel Core i5 de 3,2 GHz e uma unidade Fusion, executando o OSX 10.10.4 e o bash 3.2.57, e são a média de 1000 execuções.
As entradas são:
listado em ordem crescente de duração da execução (mais rápido primeiro)
prefixado com:
M... uma solução potencialmente com vários caracteres
As soluções exclusivas do Bash lideram o grupo - mas apenas com uma contagem repetida tão pequena! (ver abaixo).
O custo de inicialização de utilitários externos é importante aqui, especialmente o Perl. Se você precisar chamar isso em um loop - com pequenas contagens de repetição em cada iteração - evite as multi-utilidades awke perlsoluções.
A solução Perl da pergunta é de longe a mais rápida.
A substituição global de strings ( ${foo// /=}) do Bash é inexplicavelmente dolorosamente lenta com strings grandes e foi tirada da corrida (demorou cerca de 50 minutos (!) No Bash 4.3.30 e ainda mais no Bash 3.2.57 - nunca esperei para terminar).
Os loops de bash são lentos e os loops aritméticos ( (( i= 0; ... ))) são mais lentos que os com extensão de chaves ( {1..n}) - embora os loops aritméticos sejam mais eficientes em termos de memória.
awkrefere-se ao BSDawk (como também encontrado no OSX) - é visivelmente mais lento que o gawk(GNU Awk) e especialmente mawk.
Observe que com contagens grandes e vários caracteres. seqüências de caracteres, o consumo de memória pode se tornar uma consideração - as abordagens diferem nesse sentido.
Aqui está o script Bash ( testrepeat) que produziu o acima. São necessários 2 argumentos:
a contagem de repetição de caracteres
opcionalmente, o número de execuções de teste para executar e calcular o tempo médio de
Em outras palavras: os tempos acima foram obtidos com testrepeat 100 1000etestrepeat 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
É interessante ver a comparação do tempo, mas acho que em muitos programas a saída é armazenada em buffer, portanto, o tempo pode ser alterado se o buffer estiver desativado.
precisa saber é o seguinte
In order to use brace expansion with a variable, we must use `eval`Py
pyb 15/05/19
2
Portanto, a solução perl (sid_com) é basicamente a mais rápida ... quando a sobrecarga inicial do lançamento do perl for atingida. (passa de 59ms para uma pequena repetição a 67ms para um milhão de repetições ... então o forl forl levou aproximadamente 59ms no seu sistema)
Olivier Dulac
46
Há mais de uma maneira de fazer isso.
Usando um loop:
A expansão entre chaves pode ser usada com literais inteiros:
A especificação de uma precisão aqui trunca a string para caber na largura especificada ( 0). Como printfreutiliza a string de formato para consumir todos os argumentos, isso simplesmente imprime "="100 vezes.
++ para a solução head/ tr, que funciona bem mesmo com altas contagens de repetição (ressalva pequena: head -cnão é compatível com POSIX, mas o BSD e o GNU o headimplementam); embora as outras duas soluções sejam lentas nesse caso, elas também têm a vantagem de trabalhar com seqüências de vários caracteres.
usar o seguinte comando
1
Usando yese head- útil se você quiser um certo número de novas linhas: yes "" | head -n 100. trpode fazê-lo imprimir qualquer caractere:yes "" | head -n 100 | tr "\n" "="; echo
loxaxs 27/05
Surpreendentemente: dd if=/dev/zero count=1 bs=100000000 | tr '\0' '=' >/dev/nullé significativamente mais lento que a head -c100000000 < /dev/zero | tr '\0' '=' >/dev/nullversão. Claro que você precisa usar um tamanho de bloco de 100M + para medir a diferença de tempo razoavelmente. 100M bytes levam 1,7 se 1 s com as duas versões respectivas mostradas. Tirei o tr e apenas o joguei /dev/nulle obtive 0,287 s para a headversão e 0,675 s para a ddversão por um bilhão de bytes.
Ah, então eu estava usando a versão BSD do seq encontrada no OS X. Vou atualizar a resposta. Qual versão você está usando?
Sam Salisbury
Estou usando o seq do GNU coreutils.
John B
1
@ JohnB: O BSD seqestá sendo reutilizado de maneira inteligente aqui para replicar as strings : a string de formato passada para -f- normalmente usada para formatar os números que estão sendo gerados - contém apenas a string a ser replicada aqui, para que a saída contenha apenas cópias dessa string. Infelizmente, o GNU seqinsiste na presença de um formato numérico na string de formato, que é o erro que você está vendo.
usar o seguinte comando
1
Bem feito; também funciona com cadeias de caracteres múltiplos . Use "$1"(aspas duplas), para que você também possa passar caracteres como '*'e strings com espaço em branco incorporado. Finalmente, se você quiser poder usá- %lo, precisará dobrá- lo (caso contrário, seqvocê achará que faz parte de uma especificação de formato como %f); usando "${1//%/%%}"iria cuidar disso. Como (como você mencionou) você está usando o BSDseq , isso funcionará em sistemas operacionais semelhantes ao BSD em geral (por exemplo, FreeBSD) - por outro lado, não funcionará no Linux , onde o GNUseq é usado.
Bem feito; observe que o BSDpaste inexplicavelmente requer -d '\0'a especificação de um delimitador vazio e falha com -d ''- -d '\0'deve funcionar com todas as pasteimplementações compatíveis com POSIX e de fato também funciona com o GNUpaste .
usar o seguinte comando
De espírito semelhante, com menos ferramentas externas:yes | mapfile -n 100 -C 'printf = \#' -c 1
bispo
@ bispo: Embora seu comando realmente crie menos um subshell, ainda é mais lento para contagens mais altas de repetições e, para contagens mais baixas, a diferença provavelmente não importa; o limite exato provavelmente depende do hardware e do sistema operacional, por exemplo, na minha máquina OSX 10.11.5, essa resposta já é mais rápida em 500; tente time yes = | head -500 | paste -s -d '\0' -; time yes | mapfile -n 500 -C 'printf = \#' -c 1. Mais importante, porém: se você estiver usando printfqualquer maneira, assim como você pode ir com a abordagem mais simples e mais eficiente da resposta aceita:printf '%.s=' $(seq 500)
mklement0
13
Não existe uma maneira simples. Evite loops usando printfe substituindo.
Bom, mas só funciona razoavelmente com pequenas contagens de repetição. Aqui está uma função de invólucro que pode ser invocado como repl = 100, por exemplo (não emite um à direita \n):repl() { local ts=$(printf "%${2}s"); printf %s "${ts// /$1}"; }
mklement0
1
@ mklement0 Que bom que você forneceu versões funcionais das duas soluções, +1 nas duas!
Camilo Martin
8
Se você deseja conformidade e consistência POSIX em diferentes implementações de echoe printf, e / ou shells, além de 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)
... produzirá a mesma saída que perl -E 'say "=" x 100'praticamente em qualquer lugar.
O problema é que seqnão é um utilitário POSIX (embora os sistemas BSD e Linux tenham implementações dele) - você pode fazer a aritmética do shell POSIX com um whileloop, como na resposta do @ Xennex81 (com printf "=", como você sugere corretamente, em vez de echo -n).
usar o seguinte comando
1
Opa, você está certo. Às vezes, coisas assim passam despercebidas, pois esse padrão não faz nenhum sentido. calé POSIX. seqnão é. De qualquer forma, em vez de reescrever a resposta com um loop while (como você diz, isso já está em outras respostas), adicionarei uma função RYO. Mais educativo assim ;-).
Geoff Nixon
8
A pergunta era sobre como fazer isso com echo:
echo -e ''$_{1..100}'\b='
Isso fará exatamente o mesmo que perl -E 'say "=" x 100'com echoapenas.
Uma maneira pura do Bash sem eval, sem subcascas, sem ferramentas externas, sem expansões de chaves (ou seja, você pode ter o número para repetir em uma variável):
Se você receber uma variável nque se expande para um número (não negativo) e uma variável pattern, por exemplo,
Para este pequeno truque, estamos usando printfbastante:
-v varname: em vez de imprimir na saída padrão, printfcolocará o conteúdo da sequência formatada em variável varname.
'% * s': printfusará o argumento para imprimir o número correspondente de espaços. Por exemplo, printf '%*s' 42imprimirá 42 espaços.
Finalmente, quando tivermos o número desejado de espaços em nossa variável, usamos um parâmetro de expansão para substituir todos os espaços pelo nosso padrão: ${var// /$pattern}expandiremos para a expansão de varcom todos os espaços substituídos pela expansão de $pattern.
Você também pode se livrar da tmpvariável na repeatfunção usando expansão indireta:
repeat(){# $1=number of patterns to repeat# $2=pattern# $3=output variable name
printf -v "$3"'%*s'"$1"
printf -v "$3"'%s'"${!3// /$2}"}
Variação interessante para passar o nome da variável. Embora esta solução seja adequada para contagens repetidas de até 1.000 (e, portanto, provavelmente adequada para a maioria das aplicações da vida real, se eu adivinhar), ela fica muito lenta para contagens mais altas (veja a próxima Comente).
usar o seguinte comando
Parece que bashas operações globais de substituição de cadeia de caracteres no contexto da expansão de parâmetros ( ${var//old/new}) são particularmente lentas: extremamente lenta no bash 3.2.57e lenta no bash 4.3.30, pelo menos no meu sistema OSX 10.10.3 em uma máquina Intel Core i5 de 3,2 Ghz: com uma contagem de 1.000, as coisas são lentas ( 3.2.57) / rápidas ( 4.3.30): 0.1 / 0.004 segundos. Aumentar a contagem para 10.000 gera números surpreendentemente diferentes: repeat 10000 = varleva cerca de 80 segundos (!) No bash 3.2.57e cerca de 0,3 segundos no bash 4.3.30(muito mais rápido que no 3.2.57, mas ainda lento).
Bem feito; isso é compatível com POSIX e razoavelmente rápido, mesmo com altas contagens de repetição, além de suportar seqüências de vários caracteres. Aqui está a versão shell: awk 'BEGIN { while (c++ < 100) printf "=" }'. Envolto em uma função parametrizada concha (invocação como repeat 100 =, por exemplo): repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { txt=substr(txt, 2); while (i++ < count) printf txt }'; }. (O manequim .prefixo caractere e complementar substrchamada são necessários para o trabalho em torno de um bug no BSD awk, onde passando um valor variável que começa com =quebras de comando.)
mklement0
1
A NF = 100solução é muito inteligente (apesar de obter 100 =, você deve usar NF = 101). As advertências são de que ele trava BSD awk(mas é muito rápido com gawke até mesmo mais rápido com mawk), e que discute POSIX nem atribuir a NF, nem o uso de campos em BEGINblocos. Você pode fazê-lo funcionar no BSD awktambém com um pequeno ajuste: awk 'BEGIN { OFS = "="; $101=""; print }'(mas curiosamente, no BSD awkque não é mais rápido que a solução de loop). Como uma solução de shell parametrizado: repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { OFS=substr(txt, 2); $(count+1)=""; print }'; }.
mklement0
Nota para os usuários - O truque NF = 100 causa uma falha de segmento no awk mais antigo. O original-awké o nome no Linux do awk mais antigo, semelhante ao awk do BSD, que também foi relatado como travado, se você quiser tentar isso. Observe que travar é geralmente o primeiro passo para encontrar um bug explorável. Esta resposta está promovendo códigos inseguros.
2
Nota aos usuários - original-awknão é padrão e não é recomendado #
Steven Penny
Uma alternativa para o primeiro snippet de código pode ser awk NF=100 OFS='=' <<< ""(using bashand gawk) #
1119
4
Eu acho que o objetivo original da pergunta era fazer isso apenas com os comandos internos do shell. Então, forloops e printfs seria legítimo, enquanto rep, perle também jotabaixo não faria. Ainda assim, o seguinte comando
jot -s "/" -b "\\" $((COLUMNS/2))
por exemplo, imprime uma linha de janelas inteira \/\/\/\/\/\/\/\/\/\/\/\/
Bem feito; isso funciona bem mesmo com altas contagens de repetição (enquanto também suporta seqüências de caracteres múltiplos). Para melhor ilustrar a abordagem, aqui é o equivalente a ordem do OP: jot -s '' -b '=' 100. A ressalva é que, embora as plataformas semelhantes a BSD, incluindo OSX, venham jot, as distribuições Linux não .
usar o seguinte comando
1
Obrigado, eu gosto mais do seu uso de -s. Eu mudei meus scripts.
Stefan Ludwig
Em sistemas recentes baseados no Debian , apt install athena-jotseria fornecido jot.
agc
4
Como outros já disseram, na expansão do bash brace precede a expansão do parâmetro , portanto, os intervalos podem conter apenas literais. e forneça soluções limpas, mas não totalmente portáteis de um sistema para outro, mesmo se você estiver usando o mesmo shell em cada um. (Embora esteja cada vez mais disponível; por exemplo, no FreeBSD 9.3 e superior .) E outras formas de indireção sempre funcionam, mas são um tanto deselegantes.{m,n}seqjotseqeval
Felizmente, o bash suporta o estilo C para loops (apenas com expressões aritméticas). Então, aqui está uma maneira concisa de "pure bash":
Isso leva o número de repetições como o primeiro argumento e a sequência a ser repetida (que pode ser um caractere único, como na descrição do problema) como o segundo argumento. repecho 7 bsaídas bbbbbbb(terminadas por uma nova linha).
Como o foco aqui é repetir um único caractere e o shell é bash, provavelmente é seguro usá-lo em echovez de printf. E li a descrição do problema nesta pergunta como expressando uma preferência para imprimir echo. A definição da função acima funciona no bash e no ksh93 . Embora printfseja mais portátil (e geralmente deva ser usado para esse tipo de coisa), echoa sintaxe é indiscutivelmente mais legível.
Os echocomponentes de alguns shells interpretam -por si só como uma opção - mesmo que o significado usual de -, usar stdin para entrada, seja sem sentido echo. O zsh faz isso. E definitivamente existem echos que não reconhecem -n, pois isso não é padrão . (Muitas conchas no estilo Bourne não aceitam o estilo C para loops, portanto, seu echocomportamento não precisa ser considerado ..)
Aqui, a tarefa é imprimir a sequência; lá , era para atribuí-lo a uma variável.
Se $né o número desejado de repetições e você não precisa reutilizá-lo, e deseja algo ainda mais curto:
while((n--));do echo -n "$s";done; echo
ndeve ser uma variável - dessa maneira não funciona com parâmetros posicionais. $sé o texto a ser repetido.
Evite fortemente fazer versões de loop. printf "%100s" | tr ' ' '='é ótimo.
Ocodo
Boas informações de base e elogios por empacotar a funcionalidade como uma função, que também funciona zsh, aliás. A abordagem echo-in-a-loop funciona bem para contagens menores de repetição, mas para as maiores existem alternativas compatíveis com POSIX baseadas em utilitários , conforme evidenciado pelo comentário do @ Slomojo.
precisa saber é o seguinte
Adicionando parênteses em torno de suas conservas de loop mais curtos o valor de n, sem afetar os ecos:(while ((n--)); do echo -n "$s"; done; echo)
use printf em vez de eco! é muito mais portátil (o echo -n pode funcionar apenas em alguns sistemas). veja unix.stackexchange.com/questions/65803/… (uma das respostas impressionantes de Stephane Chazelas)
Olivier Dulac
@OlivierDulac A questão aqui é sobre o bash. Não importa qual sistema operacional você esteja executando, se estiver usando o bash , o bash possui um echosuporte interno -n. O espírito do que você está dizendo é absolutamente correto. printfquase sempre deve ser preferido echo, pelo menos em uso não interativo. Mas não acho que seja inapropriado ou enganoso dar uma echoresposta a uma pergunta que pediu uma e que forneceu informações suficientes para saber que funcionaria . Observe também que o suporte para ((n--))(sem a $) não é garantido pelo POSIX.
Eliah Kagan em 14/02
4
Python é onipresente e funciona da mesma maneira em todos os lugares.
Com um terminal ANSI e caracteres US-ASCII para repetir. Você pode usar uma sequência de escape ANSI CSI. É a maneira mais rápida de repetir um personagem.
Nem todos os terminais compreendem a repeat_charsequência ANSI CSI.
Somente caracteres US-ASCII ou ISO de byte único podem ser repetidos.
Repita as paradas na última coluna, para que você possa usar um valor grande para preencher uma linha inteira, independentemente da largura do terminal.
A repetição é apenas para exibição. Capturar a saída em uma variável de shell não expandirá a repeat_charsequência ANSI CSI no caractere repetido.
lengthnão vai funcionar expr, você provavelmente quis dizer n=$(expr 10 - ${#vari}); no entanto, é mais simples e mais eficiente usar a expansão aritmética do Bash: n=$(( 10 - ${#vari} )). Além disso, no centro da sua resposta está a própria abordagem Perl para a qual o OP está procurando uma alternativa do Bash .
precisa saber é o seguinte
1
Esta é a versão mais longa do que Eliah Kagan estava defendendo:
while[ $(( i--))-gt 0];do echo -n " ";done
Claro que você também pode usar o printf para isso, mas não do meu jeito:
Minha resposta é um pouco mais complicada e, provavelmente, não é perfeita, mas para aqueles que desejam gerar grandes números, consegui fazer cerca de 10 milhões em 3 segundos.
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}}
A maioria das soluções existentes depende do {1..10}suporte à sintaxe do shell, que é bash- e zsh- específico, e não funciona no tcshOpenBSD kshe na maioria dos não-bash sh.
O seguinte deve funcionar no OS X e em todos os sistemas * BSD em qualquer shell; de fato, ele pode ser usado para gerar uma matriz inteira de vários tipos de espaço decorativo:
ruby -e 'puts "=" * 100'
oupython -c 'print "=" * 100'
printf
comseq
)svrb=`printf '%.sv' $(seq $vrb)`
Respostas:
Você pode usar:
Como isso funciona:
O Bash se expande {1..100} para que o comando se torne:
Eu configurei o formato printf para o
=%.0s
que significa que ele sempre imprimirá um único,=
independentemente do argumento que for fornecido. Portanto, imprime 100=
s.fonte
repl = 100
, por exemplo (eval
é exigido truques, infelizmente, para basear a expansão cinta em uma variável):repl() { printf "$1"'%.s' $(eval "echo {1.."$(($2))"}"); }
seq
por exemplo, por exemplo$(seq 1 $limit)
.$s%.0s
para%.0s$s
hífen, causando umprintf
erro.printf
: ele continua a aplicar a string de formato até que não haja mais argumentos. Eu tinha assumido que processou a string de formato apenas uma vez!Não é fácil. Mas por exemplo:
Ou talvez uma maneira de conformidade padrão:
Há também um
tput rep
, mas quanto aos meus terminais em mãos (xterm e linux), eles não parecem suportá-lo :)fonte
=
caracteres.printf
tr
é a única solução POSIX porqueseq
,yes
e{1..3}
não é POSIX.printf %100s | sed 's/ /abc/g'
- gera 'abcabcabc ...'tr
). Você também pode estendê-lo a algo comoprintf "%${COLUMNS}s\n" | tr " " "="
.wc
. A única conclusão que posso tirar disso é "seq
não deve ser usada".Ponta do chapéu para @ gniourf_gniourf por sua contribuição.
Nota: Esta resposta não responde à pergunta original, mas complementa as respostas úteis existentes comparando o desempenho .
As soluções são comparadas apenas em termos de velocidade de execução - os requisitos de memória não são levados em consideração (eles variam entre as soluções e podem importar com grandes contagens de repetição).
Resumo:
(por exemplo,
${var// /=}
), pois é proibitivamente lento.A seguir, são mostrados os tempos do iMac de final de 2012 com uma CPU Intel Core i5 de 3,2 GHz e uma unidade Fusion, executando o OSX 10.10.4 e o bash 3.2.57, e são a média de 1000 execuções.
As entradas são:
M
... uma solução potencialmente com vários caracteresS
... uma solução única para caracteresP
... uma solução compatível com POSIXawk
eperl
soluções.${foo// /=}
) do Bash é inexplicavelmente dolorosamente lenta com strings grandes e foi tirada da corrida (demorou cerca de 50 minutos (!) No Bash 4.3.30 e ainda mais no Bash 3.2.57 - nunca esperei para terminar).(( i= 0; ... ))
) são mais lentos que os com extensão de chaves ({1..n}
) - embora os loops aritméticos sejam mais eficientes em termos de memória.awk
refere-se ao BSDawk
(como também encontrado no OSX) - é visivelmente mais lento que ogawk
(GNU Awk) e especialmentemawk
.Aqui está o script Bash (
testrepeat
) que produziu o acima. São necessários 2 argumentos:Em outras palavras: os tempos acima foram obtidos com
testrepeat 100 1000
etestrepeat 1000000 1000
fonte
In order to use brace expansion with a variable, we must use `eval`
PyHá mais de uma maneira de fazer isso.
Usando um loop:
A expansão entre chaves pode ser usada com literais inteiros:
Um loop tipo C permite o uso de variáveis:
Usando o
printf
builtin:A especificação de uma precisão aqui trunca a string para caber na largura especificada (
0
). Comoprintf
reutiliza a string de formato para consumir todos os argumentos, isso simplesmente imprime"="
100 vezes.Usando
head
(printf
, etc) etr
:fonte
head
/tr
, que funciona bem mesmo com altas contagens de repetição (ressalva pequena:head -c
não é compatível com POSIX, mas o BSD e o GNU ohead
implementam); embora as outras duas soluções sejam lentas nesse caso, elas também têm a vantagem de trabalhar com seqüências de vários caracteres.yes
ehead
- útil se você quiser um certo número de novas linhas:yes "" | head -n 100
.tr
pode fazê-lo imprimir qualquer caractere:yes "" | head -n 100 | tr "\n" "="; echo
dd if=/dev/zero count=1 bs=100000000 | tr '\0' '=' >/dev/null
é significativamente mais lento que ahead -c100000000 < /dev/zero | tr '\0' '=' >/dev/null
versão. Claro que você precisa usar um tamanho de bloco de 100M + para medir a diferença de tempo razoavelmente. 100M bytes levam 1,7 se 1 s com as duas versões respectivas mostradas. Tirei o tr e apenas o joguei/dev/null
e obtive 0,287 s para ahead
versão e 0,675 s para add
versão por um bilhão de bytes.dd if=/dev/zero count=1 bs=100000000 | tr '\0' '=' >/dev/null
=>0,21332 s, 469 MB/s
; Para:dd if=/dev/zero count=100 bs=1000000| tr '\0' '=' >/dev/null
=>0,161579 s, 619 MB/s
;Acabei de encontrar uma maneira muito fácil de fazer isso usando seq:
UPDATE: Funciona no BSD
seq
que acompanha o OS X. YMMV com outras versõesImprimirá '#' 10 vezes, assim:
-f "#"
define a string de formato para ignorar os números e apenas imprimir#
para cada um.-s ''
define o separador como uma sequência vazia para remover as novas linhas que seq insere entre cada número-f
e-s
parecem ser importantes.EDIT: Aqui está em uma função útil ...
Que você pode chamar assim ...
NOTA: Se você estiver repetindo
#
, as aspas são importantes!fonte
seq: format ‘#’ has no % directive
.seq
é para números, não para strings. Veja gnu.org/software/coreutils/manual/html_node/seq-invocation.htmlseq
está sendo reutilizado de maneira inteligente aqui para replicar as strings : a string de formato passada para-f
- normalmente usada para formatar os números que estão sendo gerados - contém apenas a string a ser replicada aqui, para que a saída contenha apenas cópias dessa string. Infelizmente, o GNUseq
insiste na presença de um formato numérico na string de formato, que é o erro que você está vendo."$1"
(aspas duplas), para que você também possa passar caracteres como'*'
e strings com espaço em branco incorporado. Finalmente, se você quiser poder usá-%
lo, precisará dobrá- lo (caso contrário,seq
você achará que faz parte de uma especificação de formato como%f
); usando"${1//%/%%}"
iria cuidar disso. Como (como você mencionou) você está usando o BSDseq
, isso funcionará em sistemas operacionais semelhantes ao BSD em geral (por exemplo, FreeBSD) - por outro lado, não funcionará no Linux , onde o GNUseq
é usado.Aqui estão duas maneiras interessantes:
Observe que esses dois são sutilmente diferentes - O
paste
método termina em uma nova linha. Otr
método não.fonte
paste
inexplicavelmente requer-d '\0'
a especificação de um delimitador vazio e falha com-d ''
--d '\0'
deve funcionar com todas aspaste
implementações compatíveis com POSIX e de fato também funciona com o 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
. Mais importante, porém: se você estiver usandoprintf
qualquer maneira, assim como você pode ir com a abordagem mais simples e mais eficiente da resposta aceita:printf '%.s=' $(seq 500)
Não existe uma maneira simples. Evite loops usando
printf
e substituindo.fonte
repl = 100
, por exemplo (não emite um à direita\n
):repl() { local ts=$(printf "%${2}s"); printf %s "${ts// /$1}"; }
Se você deseja conformidade e consistência POSIX em diferentes implementações de
echo
eprintf
, e / ou shells, além debash
:... produzirá a mesma saída que
perl -E 'say "=" x 100'
praticamente em qualquer lugar.fonte
seq
não é um utilitário POSIX (embora os sistemas BSD e Linux tenham implementações dele) - você pode fazer a aritmética do shell POSIX com umwhile
loop, como na resposta do @ Xennex81 (comprintf "="
, como você sugere corretamente, em vez deecho -n
).cal
é POSIX.seq
não é. De qualquer forma, em vez de reescrever a resposta com um loop while (como você diz, isso já está em outras respostas), adicionarei uma função RYO. Mais educativo assim ;-).A pergunta era sobre como fazer isso com
echo
:Isso fará exatamente o mesmo que
perl -E 'say "=" x 100'
comecho
apenas.fonte
$_1
,$_2
ou qualquer outra das centenas de variáveis tiver valores.Uma maneira pura do Bash sem
eval
, sem subcascas, sem ferramentas externas, sem expansões de chaves (ou seja, você pode ter o número para repetir em uma variável):Se você receber uma variável
n
que se expande para um número (não negativo) e uma variávelpattern
, por exemplo,Você pode fazer uma função com isso:
Com este conjunto:
Para este pequeno truque, estamos usando
printf
bastante:-v varname
: em vez de imprimir na saída padrão,printf
colocará o conteúdo da sequência formatada em variávelvarname
.printf
usará o argumento para imprimir o número correspondente de espaços. Por exemplo,printf '%*s' 42
imprimirá 42 espaços.${var// /$pattern}
expandiremos para a expansão devar
com todos os espaços substituídos pela expansão de$pattern
.Você também pode se livrar da
tmp
variável narepeat
função usando expansão indireta:fonte
bash
as operações globais de substituição de cadeia de caracteres no contexto da expansão de parâmetros (${var//old/new}
) são particularmente lentas: extremamente lenta no bash3.2.57
e lenta no bash4.3.30
, pelo menos no meu sistema OSX 10.10.3 em uma máquina Intel Core i5 de 3,2 Ghz: com uma contagem de 1.000, as coisas são lentas (3.2.57
) / rápidas (4.3.30
): 0.1 / 0.004 segundos. Aumentar a contagem para 10.000 gera números surpreendentemente diferentes:repeat 10000 = var
leva cerca de 80 segundos (!) No bash3.2.57
e cerca de 0,3 segundos no bash4.3.30
(muito mais rápido que no3.2.57
, mas ainda lento).Ou
Exemplo
fonte
awk 'BEGIN { while (c++ < 100) printf "=" }'
. Envolto em uma função parametrizada concha (invocação comorepeat 100 =
, por exemplo):repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { txt=substr(txt, 2); while (i++ < count) printf txt }'; }
. (O manequim.
prefixo caractere e complementarsubstr
chamada são necessários para o trabalho em torno de um bug no BSDawk
, onde passando um valor variável que começa com=
quebras de comando.)NF = 100
solução é muito inteligente (apesar de obter 100=
, você deve usarNF = 101
). As advertências são de que ele trava BSDawk
(mas é muito rápido comgawk
e até mesmo mais rápido commawk
), e que discute POSIX nem atribuir aNF
, nem o uso de campos emBEGIN
blocos. Você pode fazê-lo funcionar no BSDawk
também com um pequeno ajuste:awk 'BEGIN { OFS = "="; $101=""; print }'
(mas curiosamente, no BSDawk
que não é mais rápido que a solução de loop). Como uma solução de shell parametrizado:repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { OFS=substr(txt, 2); $(count+1)=""; print }'; }
.original-awk
é o nome no Linux do awk mais antigo, semelhante ao awk do BSD, que também foi relatado como travado, se você quiser tentar isso. Observe que travar é geralmente o primeiro passo para encontrar um bug explorável. Esta resposta está promovendo códigos inseguros.original-awk
não é padrão e não é recomendado #awk NF=100 OFS='=' <<< ""
(usingbash
andgawk
) #Eu acho que o objetivo original da pergunta era fazer isso apenas com os comandos internos do shell. Então,
for
loops eprintf
s seria legítimo, enquantorep
,perl
e tambémjot
abaixo não faria. Ainda assim, o seguinte comandojot -s "/" -b "\\" $((COLUMNS/2))
por exemplo, imprime uma linha de janelas inteira
\/\/\/\/\/\/\/\/\/\/\/\/
fonte
jot -s '' -b '=' 100
. A ressalva é que, embora as plataformas semelhantes a BSD, incluindo OSX, venhamjot
, as distribuições Linux não .apt install athena-jot
seria fornecidojot
.Como outros já disseram, na expansão do bash brace precede a expansão do parâmetro , portanto, os intervalos podem conter apenas literais. e forneça soluções limpas, mas não totalmente portáteis de um sistema para outro, mesmo se você estiver usando o mesmo shell em cada um. (Embora esteja cada vez mais disponível; por exemplo, no FreeBSD 9.3 e superior .) E outras formas de indireção sempre funcionam, mas são um tanto deselegantes.
{m,n}
seq
jot
seq
eval
Felizmente, o bash suporta o estilo C para loops (apenas com expressões aritméticas). Então, aqui está uma maneira concisa de "pure bash":
Isso leva o número de repetições como o primeiro argumento e a sequência a ser repetida (que pode ser um caractere único, como na descrição do problema) como o segundo argumento.
repecho 7 b
saídasbbbbbbb
(terminadas por uma nova linha).Dennis Williamson deu essencialmente essa solução há quatro anos em sua excelente resposta para Criando sequência de caracteres repetidos no shell script . Meu corpo da função difere um pouco do código lá:
Como o foco aqui é repetir um único caractere e o shell é bash, provavelmente é seguro usá-lo em
echo
vez deprintf
. E li a descrição do problema nesta pergunta como expressando uma preferência para imprimirecho
. A definição da função acima funciona no bash e no ksh93 . Emboraprintf
seja mais portátil (e geralmente deva ser usado para esse tipo de coisa),echo
a sintaxe é indiscutivelmente mais legível.Os
echo
componentes de alguns shells interpretam-
por si só como uma opção - mesmo que o significado usual de-
, usar stdin para entrada, seja sem sentidoecho
. O zsh faz isso. E definitivamente existemecho
s que não reconhecem-n
, pois isso não é padrão . (Muitas conchas no estilo Bourne não aceitam o estilo C para loops, portanto, seuecho
comportamento não precisa ser considerado ..)Aqui, a tarefa é imprimir a sequência; lá , era para atribuí-lo a uma variável.
Se
$n
é o número desejado de repetições e você não precisa reutilizá-lo, e deseja algo ainda mais curto:n
deve ser uma variável - dessa maneira não funciona com parâmetros posicionais.$s
é o texto a ser repetido.fonte
printf "%100s" | tr ' ' '='
é ótimo.zsh
, aliás. A abordagem echo-in-a-loop funciona bem para contagens menores de repetição, mas para as maiores existem alternativas compatíveis com POSIX baseadas em utilitários , conforme evidenciado pelo comentário do @ Slomojo.(while ((n--)); do echo -n "$s"; done; echo)
echo
suporte interno-n
. O espírito do que você está dizendo é absolutamente correto.printf
quase sempre deve ser preferidoecho
, pelo menos em uso não interativo. Mas não acho que seja inapropriado ou enganoso dar umaecho
resposta a uma pergunta que pediu uma e que forneceu informações suficientes para saber que funcionaria . Observe também que o suporte para((n--))
(sem a$
) não é garantido pelo POSIX.Python é onipresente e funciona da mesma maneira em todos os lugares.
python -c "import sys; print('*' * int(sys.argv[1]))" "=" 100
Caractere e contagem são passados como parâmetros separados.
fonte
python -c "import sys; print(sys.argv[1] * int(sys.argv[2]))" "=" 100
No bash 3.0 ou superior
fonte
Outra forma de repetir uma sequência arbitrária n vezes:
Prós:
Contras:
yes
comando do Gnu Core Utils .Com um terminal ANSI e caracteres US-ASCII para repetir. Você pode usar uma sequência de escape ANSI CSI. É a maneira mais rápida de repetir um personagem.
Ou estaticamente:
Imprima uma linha de 80 vezes
=
:printf '=\e[80b\n'
Limitações:
repeat_char
sequência ANSI CSI.repeat_char
sequência ANSI CSI no caractere repetido.fonte
Aqui está o que eu uso para imprimir uma linha de caracteres na tela no Linux (com base na largura do terminal / tela)
Imprimir "=" na tela:
Explicação:
Imprima um sinal de igual quantas vezes a sequência fornecida:
Use a saída de um comando (esse é um recurso do bash chamado Substituição de Comando):
Dê uma sequência, usei 1 a 20 como exemplo. No comando final, o comando tput é usado em vez de 20:
Forneça o número de colunas atualmente usadas no terminal:
fonte
fonte
fonte
O mais simples é usar esta linha em csh / tcsh:
fonte
Uma alternativa mais elegante à solução Python proposta poderia ser:
fonte
Caso deseje repetir um caractere n vezes, sendo um número VARIÁVEL de vezes, dependendo, digamos, do comprimento de uma string que você possa fazer:
Exibe:
fonte
length
não vai funcionarexpr
, você provavelmente quis dizern=$(expr 10 - ${#vari})
; no entanto, é mais simples e mais eficiente usar a expansão aritmética do Bash:n=$(( 10 - ${#vari} ))
. Além disso, no centro da sua resposta está a própria abordagem Perl para a qual o OP está procurando uma alternativa do Bash .Esta é a versão mais longa do que Eliah Kagan estava defendendo:
Claro que você também pode usar o printf para isso, mas não do meu jeito:
Esta versão é compatível com Dash:
sendo eu o número inicial.
fonte
while (( i-- )); do echo -n " "; done
.Execuções de amostra
Referência lib em: https://github.com/gdbtek/linux-cookbooks/blob/master/libraries/util.bash
fonte
Você pode fazer isso com
echo
se oecho
for seguido porsed
:Na verdade, isso
echo
é desnecessário lá.fonte
Minha resposta é um pouco mais complicada e, provavelmente, não é perfeita, mas para aqueles que desejam gerar grandes números, consegui fazer cerca de 10 milhões em 3 segundos.
fonte
O mais simples é usar este one-liner no bash:
fonte
Outra opção é usar o GNU seq e remover todos os números e novas linhas que ele gerar:
Este comando imprime o
#
caractere 100 vezes.fonte
A maioria das soluções existentes depende do
{1..10}
suporte à sintaxe do shell, que ébash
- ezsh
- específico, e não funciona notcsh
OpenBSDksh
e na maioria dos não-bashsh
.O seguinte deve funcionar no OS X e em todos os sistemas * BSD em qualquer shell; de fato, ele pode ser usado para gerar uma matriz inteira de vários tipos de espaço decorativo:
Infelizmente, não temos uma nova linha à direita; que pode ser corrigido com um extra
printf '\n'
após a dobra:Referências:
fonte
Minha proposta (aceitando valores variáveis para n):
fonte