no con eco, sino sobre el mismo tema ruby -e 'puts "=" * 100'opython -c 'print "=" * 100'
Evgeny
1
Gran pregunta Muy buenas respuestas. He usado una de las respuestas en un trabajo real aquí, que publicaré como ejemplo: github.com/drbeco/oldfiles/blob/master/oldfiles (usado printfcon seq)svrb=`printf '%.sv' $(seq $vrb)`
Dr Beco
Una solución genérica para imprimir lo que sea (1 o más caracteres, incluso las nuevas líneas): Repeat_this () {i = 1; mientras que ["$ i" -le "$ 2"]; hacer printf "% s" "$ 1"; i = $ (($ i + 1)); hecho ; printf '\ n';}. Use así: Repita_este "algo" Número_de_repeticiones. Por ejemplo, para mostrar la repetición de 5 veces algo que incluye 3 líneas nuevas: Repita_este "$ (printf '\ n \ n \ neste')" 5. La impresión final '\ n' puede ser eliminada (¡pero la puse para crear archivos de texto, y esos necesitan una nueva línea como su último personaje!)
Olivier Dulac
Respuestas:
396
Puedes usar:
printf '=%.0s'{1..100}
Cómo funciona esto:
Bash expande {1..100} por lo que el comando se convierte en:
printf '=%.0s'1234...100
He configurado el formato de printf, lo =%.0sque significa que siempre imprimirá uno =sin importar el argumento que se le dé. Por lo tanto, imprime 100 =s.
Gran solución que funciona razonablemente bien incluso con grandes recuentos repetidos. Aquí hay un contenedor de funciones con el que puede invocar repl = 100, por ejemplo ( evalse requiere truco, desafortunadamente, para basar la expansión de repl() { printf "$1"'%.s' $(eval "echo {1.."$(($2))"}"); }
llaves
77
¿Es posible establecer el límite superior usando una var? Lo he intentado y no puedo hacerlo funcionar.
Mike Purcell
70
No puede usar variables dentro de la expansión de llaves. Utilice en su seqlugar, por ejemplo $(seq 1 $limit).
dogbane
11
Si funcionaliza esto, es mejor reorganizarlo $s%.0spara %.0s$sque los guiones causen un printferror.
KomodoDave
55
Esto me hizo notar un comportamiento de Bash printf: continúa aplicando la cadena de formato hasta que no quedan argumentos. ¡Asumí que procesó la cadena de formato solo una vez!
Jeenu
89
No hay manera fácil. Pero por ejemplo:
seq -s=100|tr -d '[:digit:]'
O tal vez una forma de conformidad estándar:
printf %100s|tr " ""="
También hay un tput rep, pero en cuanto a mis terminales disponibles (xterm y linux) no parecen admitirlo :)
Tenga en cuenta que la primera opción con seq imprime uno menos que el número dado, por lo que ese ejemplo imprimirá 99 =caracteres.
Camilo Martin
13
printftres la única solución POSIX porque seq, yesy {1..3}no son POSIX.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
2
Para repetir una cadena en lugar de un solo carácter: printf %100s | sed 's/ /abc/g'- genera 'abcabcabc ...'
John Rix
3
+1 por no usar bucles y solo un comando externo ( tr). También podría extenderlo a algo así printf "%${COLUMNS}s\n" | tr " " "=".
musiphil
2
@ mklement0 Bueno, esperaba que estuvieras contando la última línea nueva por error wc. La única conclusión que puedo sacar de esto es " seqno se debe usar".
Nota: Esta respuesta no responde a la pregunta original, pero complementa las respuestas útiles existentes al comparar el rendimiento .
Las soluciones se comparan solo en términos de velocidad de ejecución : los requisitos de memoria no se tienen en cuenta (varían de una solución a otra y pueden tener un gran número de repeticiones).
Resumen:
Si su recuento de repeticiones es pequeño , digamos hasta alrededor de 100, vale la pena ir con las soluciones de solo Bash , ya que el costo inicial de las utilidades externas es importante, especialmente el de Perl.
Hablando pragmáticamente, sin embargo, si solo necesita una instancia de caracteres repetidos, todas las soluciones existentes pueden estar bien.
Con grandes recuentos repetidos , use utilidades externas , ya que serán mucho más rápidas.
En particular, evite el reemplazo de subcadena global de Bash con cadenas grandes
(por ejemplo, ${var// /=}), ya que es prohibitivamente lento.
Los siguientes son tiempos tomados en un iMac de finales de 2012 con una CPU Intel Core i5 de 3.2 GHz y una unidad Fusion, con OSX 10.10.4 y bash 3.2.57, y son el promedio de 1000 ejecuciones.
Las entradas son:
enumerado en orden ascendente de duración de la ejecución (el más rápido primero)
con el prefijo:
M... una solución potencialmente de múltiples caracteres
S... una solución de un solo personaje
P ... una solución compatible con POSIX
seguido de una breve descripción de la solución
Sufijo con el nombre del autor de la respuesta original.
Las soluciones de solo Bash lideran el paquete, ¡pero solo con un recuento de repetición tan pequeño! (vea abajo).
El costo inicial de las utilidades externas sí importa aquí, especialmente el de Perl. Si debe llamar a esto en un bucle, con pequeños recuentos de repeticiones en cada iteración, evite las soluciones awky la utilidad múltiple perl.
La solución Perl de la pregunta es, con mucho, la más rápida.
El reemplazo de cadena global de Bash ( ${foo// /=}) es inexplicablemente lento con cadenas grandes, y ha sido eliminado de la ejecución (tomó alrededor de 50 minutos (!) En Bash 4.3.30, e incluso más en Bash 3.2.57 - Nunca esperé para terminar).
Los bucles bash son lentos y los bucles aritméticos ( (( i= 0; ... ))) son más lentos que los expandidos con llaves ( {1..n}), aunque los bucles aritméticos son más eficientes en cuanto a memoria.
awkse refiere a BSDawk (como también se encuentra en OSX): es notablemente más lento que gawk(GNU Awk) y especialmentemawk .
Tenga en cuenta que con grandes recuentos y multi-char. cadenas, el consumo de memoria puede convertirse en una consideración: los enfoques difieren en ese sentido.
Aquí está el script Bash ( testrepeat) que produjo lo anterior. Se necesitan 2 argumentos:
el personaje repite cuenta
opcionalmente, el número de ejecuciones de prueba para realizar y calcular el tiempo promedio de
En otras palabras: los tiempos anteriores se obtuvieron con testrepeat 100 1000ytestrepeat 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
Es interesante ver la comparación de temporización, pero creo que en muchos programas la salida está almacenada en búfer, por lo que su sincronización puede modificarse si se desactiva el almacenamiento en búfer.
Sergiy Kolodyazhnyy
In order to use brace expansion with a variable, we must use `eval`👍
pyb
2
Entonces, la solución perl (sid_com) es básicamente la más rápida ... una vez que se alcanza la sobrecarga inicial de iniciar perl. (va de 59 ms para una pequeña repetición a 67 ms para un millón de repeticiones ... por lo que la bifurcación de perl tomó aproximadamente 59 ms en su sistema)
Olivier Dulac
46
Hay más de una forma de hacerlo.
Usando un bucle:
La expansión de llaves se puede usar con literales enteros:
Especificar una precisión aquí trunca la cadena para que se ajuste al ancho especificado ( 0). Como printfreutiliza la cadena de formato para consumir todos los argumentos, esto simplemente imprime "="100 veces.
++ para la solución head/ tr, que funciona bien incluso con recuentos altos de repetición (pequeña advertencia: head -cno es compatible con POSIX, pero BSD y GNU lo headimplementan); Si bien las otras dos soluciones serán lentas en ese caso, también tienen la ventaja de trabajar con cadenas de caracteres múltiples .
mklement0
1
El uso yesy head- útil si se desea un cierto número de saltos de línea: yes "" | head -n 100. trpuede hacer que imprima cualquier carácter:yes "" | head -n 100 | tr "\n" "="; echo
loxaxs
Algo sorprendente: dd if=/dev/zero count=1 bs=100000000 | tr '\0' '=' >/dev/nulles significativamente más lento que la head -c100000000 < /dev/zero | tr '\0' '=' >/dev/nullversión. Por supuesto, debe usar un tamaño de bloque de 100M + para medir la diferencia de tiempo razonablemente. 100M bytes toman 1.7 sy 1 s con las dos versiones respectivas mostradas. Me quité el tr y lo descargué /dev/nully obtuve 0.287 s para la headversión y 0.675 s para la ddversión por mil millones de bytes.
Ah, entonces estaba usando la versión BSD de seq que se encuentra en OS X. Actualizaré la respuesta. ¿Qué versión está utilizando?
Sam Salisbury
Estoy usando seq de GNU coreutils.
John B
1
@JohnB: BSD seqse está reutilizando inteligentemente aquí para replicar cadenas : la cadena de formato que se pasa a -f, normalmente utilizada para formatear los números que se generan, contiene solo la cadena para replicar aquí, de modo que la salida contiene copias de esa cadena solamente. Desafortunadamente, GNU seqinsiste en la presencia de un formato de número en la cadena de formato, que es el error que está viendo.
mklement0
1
Bien hecho; También funciona con cadenas de múltiples caracteres. Utilice "$1"(comillas dobles), para que también pueda pasar caracteres como '*'y cadenas con espacios en blanco incrustados. Finalmente, si desea poder usarlo %, debe duplicarlo (de lo contrario seq, pensará que es parte de una especificación de formato como %f); usando "${1//%/%%}"se encargaría de eso. Como (como mencionas) estás usando BSDseq , esto funcionará en sistemas operativos similares a BSD en general (por ejemplo, FreeBSD); por el contrario, no funcionará en Linux , donde se usa GNUseq .
Bien hecho; tenga en cuenta que BSDpaste requiere inexplicablemente -d '\0'para especificar un delimitador vacío, y falla con -d '': -d '\0'debería funcionar con todas las pasteimplementaciones compatibles con POSIX y, de hecho, también funciona con GNUpaste .
mklement0
Similar en espíritu, con menos herramientas fuera de borda:yes | mapfile -n 100 -C 'printf = \#' -c 1
Obispo
@bishop: Si bien su comando de hecho crea una subshell menos, aún es más lenta para conteos de repetición más altos, y para conteos de repetición más bajos, la diferencia probablemente no importa; el umbral exacto probablemente depende tanto del hardware como del sistema operativo, por ejemplo, en mi máquina OSX 10.11.5 esta respuesta ya es más rápida en 500; tratar time yes = | head -500 | paste -s -d '\0' -; time yes | mapfile -n 500 -C 'printf = \#' -c 1. Sin embargo, lo que es más importante: si está utilizando de printftodos modos, también puede printf '%.s=' $(seq 500)
optar
13
No hay manera simple. Evite el uso de bucles printfy la sustitución.
Agradable, pero solo funciona razonablemente con pequeños recuentos repetidos. Aquí hay un contenedor de funciones que se puede invocar como repl = 100, por ejemplo (no \nrepl() { local ts=$(printf "%${2}s"); printf %s "${ts// /$1}"; }
genera
1
@ mklement0 ¡Es amable de su parte proporcionar versiones funcionales de ambas soluciones, +1 en ambas!
Camilo Martin
8
Si quieres POSIX-cumplimiento y la coherencia entre las diferentes implementaciones de echoy printf, y / o conchas que no sea sólo 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)
... producirá la misma salida que perl -E 'say "=" x 100'casi en todas partes.
El problema es que seqno es una utilidad POSIX (aunque los sistemas BSD y Linux tienen implementaciones de la misma): en su lugar, puede hacer aritmética de shell POSIX con un whilebucle, como en la respuesta de @ Xennex81 (con printf "=", como sugiere correctamente, en lugar de echo -n).
mklement0
1
Vaya, tienes toda la razón. A veces me pasan cosas así, ya que ese estándar no tiene ningún sentido. cales POSIX seqno es. De todos modos, en lugar de reescribir la respuesta con un ciclo while (como dices, eso ya está en otras respuestas) agregaré una función RYO. Más educativo de esa manera ;-).
Geoff Nixon
8
La pregunta era sobre cómo hacerlo con echo:
echo -e ''$_{1..100}'\b='
Esto hará exactamente lo mismo perl -E 'say "=" x 100'pero echosolo con .
Una manera pura de Bash sin eval , sin subcapas, sin herramientas externas, sin expansiones de llaves (es decir, puede tener el número para repetir en una variable):
Si le dan una variable nque se expande a un número (no negativo) y una variable pattern, por ejemplo,
Para este pequeño truco estamos usando printfbastante con:
-v varname: en lugar de imprimir a la salida estándar, printfcolocará el contenido de la cadena formateada en variable varname.
'% * s': printfusará el argumento para imprimir el número correspondiente de espacios. Por ejemplo, printf '%*s' 42imprimirá 42 espacios.
Finalmente, cuando tenemos el número deseado de espacios en nuestra variable, usamos una expansión de parámetros para reemplazar todos los espacios por nuestro patrón: ${var// /$pattern}se expandirá a la expansión de varcon todos los espacios reemplazados por la expansión de $pattern.
También puede deshacerse de la tmpvariable en la repeatfunción utilizando la expansión indirecta:
repeat(){# $1=number of patterns to repeat# $2=pattern# $3=output variable name
printf -v "$3"'%*s'"$1"
printf -v "$3"'%s'"${!3// /$2}"}
Variación interesante para pasar el nombre de la variable. Si bien esta solución está bien para los recuentos repetidos de hasta alrededor de 1,000 (y, por lo tanto, probablemente esté bien para la mayoría de las aplicaciones de la vida real, si tuviera que adivinar), se vuelve muy lenta para los recuentos más altos (ver a continuación comentario).
mklement0
Parece que bashlas operaciones de reemplazo de cadenas globales en el contexto de la expansión de parámetros ( ${var//old/new}) son particularmente lentas: terriblemente lento en bash 3.2.57y lento en bash 4.3.30, al menos en mi sistema OSX 10.10.3 en una máquina Intel Core i5 de 3.2 Ghz: con un recuento de 1,000, las cosas son lentas ( 3.2.57) / rápidas ( 4.3.30): 0.1 / 0.004 segundos. Aumentar la cuenta a 10,000 produce números sorprendentemente diferentes: repeat 10000 = vartoma alrededor de 80 segundos (!) En bash 3.2.57, y alrededor de 0.3 segundos en bash 4.3.30(mucho más rápido que encendido 3.2.57, pero aún lento).
Bien hecho; Esto es compatible con POSIX y razonablemente rápido incluso con un alto número de repeticiones, al tiempo que admite cadenas de caracteres múltiples. Aquí está la versión de la shell: awk 'BEGIN { while (c++ < 100) printf "=" }'. Envuelta en una función de shell parametrizado (invoke como repeat 100 =, por ejemplo): repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { txt=substr(txt, 2); while (i++ < count) printf txt }'; }. (El .prefijo ficticio char y la substrllamada complementaria son necesarios para evitar un error en BSD awk, donde pasar un valor variable que comienza con =rompe el comando.)
mklement0
1
La NF = 100solución es muy inteligente (aunque para obtener 100 =, debe usar NF = 101). Las advertencias son que se estrella BSD awk(pero es muy rápido con gawky aún más rápido con mawk), y que se analizan en POSIX ni asignar a NF, ni el uso de los campos en BEGINlos bloques. Puede hacer que funcione también en BSD awkcon un ligero ajuste: awk 'BEGIN { OFS = "="; $101=""; print }'(pero curiosamente, en BSD awkeso no es más rápido que la solución de bucle). Como solución shell parametrizada: repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { OFS=substr(txt, 2); $(count+1)=""; print }'; }.
mklement0
Nota para los usuarios: el truco NF = 100 provoca un fallo de segmento en un awk anterior. El original-awkes el nombre bajo Linux del awk más antiguo similar al awk de BSD, que también se ha informado que se bloquea, si desea probar esto. Tenga en cuenta que el bloqueo suele ser el primer paso para encontrar un error explotable. Esta respuesta está promoviendo un código inseguro.
2
Nota para los usuarios - original-awkno es estándar y no se recomienda
Steven Penny
Una alternativa al primer fragmento de código puede ser awk NF=100 OFS='=' <<< ""(usando bashy gawk)
oliv
4
Supongo que el propósito original de la pregunta era hacer esto solo con los comandos integrados del shell. Así forbucles y printfs serían legítimos, mientras que rep, perly también jotmás adelante no lo haría. Aún así, el siguiente comando
jot -s "/" -b "\\" $((COLUMNS/2))
por ejemplo, imprime una línea de \/\/\/\/\/\/\/\/\/\/\/\/
Bien hecho; esto funciona bien incluso con recuentos altos de repetición (a la vez que admite cadenas de varios caracteres). Para ilustrar mejor el enfoque, esto es el equivalente a la orden del OP: jot -s '' -b '=' 100. La advertencia es que si bien las plataformas tipo BSD, incluido OSX, vienen con jot, las distribuciones de Linux no .
mklement0
1
Gracias, me gusta su uso de -s '' aún mejor. He cambiado mis guiones.
Como otros han dicho, en bash brace la expansión precede a la expansión de parámetros , por lo que los rangos solo pueden contener literales. y proporciona soluciones limpias, pero no son totalmente portátiles de un sistema a otro, incluso si está utilizando el mismo shell en cada uno. (Aunque está cada vez más disponible; por ejemplo, en FreeBSD 9.3 y superior ) y otras formas de indirección siempre funcionan, pero son algo poco elegantes.{m,n}seqjotseqeval
Afortunadamente, bash admite el estilo C para bucles (solo con expresiones aritméticas). Así que aquí hay una forma concisa de "puro golpe":
Esto toma el número de repeticiones como el primer argumento y la cadena que se repetirá (que puede ser un solo carácter, como en la descripción del problema) como el segundo argumento. repecho 7 bsalidas bbbbbbb(terminadas por una nueva línea).
Dado que el enfoque aquí está en repetir un solo carácter y el shell es bash, probablemente sea seguro usarlo en echolugar de hacerlo printf. Y leí la descripción del problema en esta pregunta como una preferencia por imprimir echo. La definición de función anterior funciona en bash y ksh93 . Aunque printfes más portátil (y generalmente debería usarse para este tipo de cosas), echola sintaxis de este es posiblemente más legible.
Las echofunciones integradas de algunos shells se interpretan -por sí mismas como una opción, aunque el significado habitual de -utilizar stdin para la entrada no tiene sentido echo. zsh hace esto. Y definitivamente existen correos electrónicos echoque no reconocen -n, ya que no es estándar . (Muchos proyectiles de estilo Bourne no aceptan el estilo C para los bucles, por lo tanto, echono es necesario considerar su comportamiento ...)
Aquí la tarea es imprimir la secuencia; allí , fue para asignarlo a una variable.
Si $nes el número deseado de repeticiones y no tiene que reutilizarlo, y desea algo aún más corto:
while((n--));do echo -n "$s";done; echo
ndebe ser una variable; de esta manera no funciona con parámetros posicionales. $ses el texto a repetir.
Evita hacer versiones de bucle. printf "%100s" | tr ' ' '='Es óptimo.
ocodo
Buena información de fondo y felicitaciones para empaquetar la funcionalidad como una función, que también funciona zsh, por cierto El enfoque echo-in-a-loop funciona bien para recuentos de repetición más pequeños, pero para los más grandes existen alternativas compatibles con POSIX basadas en utilidades , como lo demuestra el comentario de @ Slomojo.
mklement0
Agregar paréntesis alrededor de su ciclo más corto conserva el valor de n sin afectar los ecos:(while ((n--)); do echo -n "$s"; done; echo)
use printf en lugar de echo! es mucho más portátil (echo -n solo puede funcionar en algunos sistemas). ver unix.stackexchange.com/questions/65803/… (una de las increíbles respuestas de Stephane Chazelas)
Olivier Dulac
@OlivierDulac La pregunta aquí es sobre bash. No importa qué sistema operativo esté ejecutando, si está usando bash en él , bash tiene una función echointegrada que lo admite -n. El espíritu de lo que estás diciendo es absolutamente correcto. printfdebería preferirse casi siempre echo, al menos en uso no interactivo. Pero no creo que sea de ninguna manera inapropiado o engañoso dar una echorespuesta a una pregunta que hizo una y que proporcionó suficiente información para saber que funcionaría . Tenga en cuenta también que el soporte para ((n--))(sin a $) no está garantizado por POSIX.
Eliah Kagan
4
Python es omnipresente y funciona igual en todas partes.
No todas las terminales entienden repeat_char secuencia ANSI CSI.
Solo se pueden repetir los caracteres ISO-ASCII o de byte único.
Repita las paradas en la última columna, por lo que puede usar un valor grande para llenar una línea completa independientemente del ancho del terminal.
La repetición es solo para mostrar. La captura de resultados en una variable de shell no expandirá la repeat_charsecuencia ANSI CSI al carácter repetido.
En caso de que desee repetir un carácter n veces siendo un número VARIABLE de veces dependiendo de, por ejemplo, la longitud de una cadena que puede hacer:
#!/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)
Muestra:
vari equals.............................: AB
Up to 10 positions I must fill with.....:8 equal signs
AB========
lengthno funcionará expr, probablemente quisiste decir n=$(expr 10 - ${#vari}); Sin embargo, es más simple y más eficiente de utilizar expansión aritmética de Bash: n=$(( 10 - ${#vari} )). Además, en el centro de su respuesta está el enfoque de Perl en el que el OP está buscando una alternativa Bash .
mklement0
1
Esta es la versión más larga de lo que Eliah Kagan estaba defendiendo:
while[ $(( i--))-gt 0];do echo -n " ";done
Por supuesto, también puedes usar printf para eso, pero no es de mi agrado:
Mi respuesta es un poco más complicada, y probablemente no sea perfecta, pero para aquellos que buscan generar grandes números, pude hacer alrededor de 10 millones en 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}}
La mayoría de las soluciones existentes en todo dependen de {1..10}apoyo sintaxis de la cáscara, que es bash- y zsh- específica, y no funciona en tcshOpenBSD de kshy la mayoría no fiestash .
Lo siguiente debería funcionar en OS X y todos los sistemas * BSD en cualquier shell; de hecho, puede usarse para generar una matriz completa de varios tipos de espacio decorativo:
ruby -e 'puts "=" * 100'
opython -c 'print "=" * 100'
printf
conseq
)svrb=`printf '%.sv' $(seq $vrb)`
Respuestas:
Puedes usar:
Cómo funciona esto:
Bash expande {1..100} por lo que el comando se convierte en:
He configurado el formato de printf, lo
=%.0s
que significa que siempre imprimirá uno=
sin importar el argumento que se le dé. Por lo tanto, imprime 100=
s.fuente
repl = 100
, por ejemplo (eval
se requiere truco, desafortunadamente, para basar la expansión derepl() { printf "$1"'%.s' $(eval "echo {1.."$(($2))"}"); }
seq
lugar, por ejemplo$(seq 1 $limit)
.$s%.0s
para%.0s$s
que los guiones causen unprintf
error.printf
: continúa aplicando la cadena de formato hasta que no quedan argumentos. ¡Asumí que procesó la cadena de formato solo una vez!No hay manera fácil. Pero por ejemplo:
O tal vez una forma de conformidad estándar:
También hay un
tput rep
, pero en cuanto a mis terminales disponibles (xterm y linux) no parecen admitirlo :)fuente
=
caracteres.printf
tr
es la única solución POSIX porqueseq
,yes
y{1..3}
no son POSIX.printf %100s | sed 's/ /abc/g'
- genera 'abcabcabc ...'tr
). También podría extenderlo a algo asíprintf "%${COLUMNS}s\n" | tr " " "="
.wc
. La única conclusión que puedo sacar de esto es "seq
no se debe usar".Punta del sombrero a @ gniourf_gniourf por su aporte.
Nota: Esta respuesta no responde a la pregunta original, pero complementa las respuestas útiles existentes al comparar el rendimiento .
Las soluciones se comparan solo en términos de velocidad de ejecución : los requisitos de memoria no se tienen en cuenta (varían de una solución a otra y pueden tener un gran número de repeticiones).
Resumen:
(por ejemplo,
${var// /=}
), ya que es prohibitivamente lento.Los siguientes son tiempos tomados en un iMac de finales de 2012 con una CPU Intel Core i5 de 3.2 GHz y una unidad Fusion, con OSX 10.10.4 y bash 3.2.57, y son el promedio de 1000 ejecuciones.
Las entradas son:
M
... una solución potencialmente de múltiples caracteresS
... una solución de un solo personajeP
... una solución compatible con POSIXawk
y la utilidad múltipleperl
.${foo// /=}
) es inexplicablemente lento con cadenas grandes, y ha sido eliminado de la ejecución (tomó alrededor de 50 minutos (!) En Bash 4.3.30, e incluso más en Bash 3.2.57 - Nunca esperé para terminar).(( i= 0; ... ))
) son más lentos que los expandidos con llaves ({1..n}
), aunque los bucles aritméticos son más eficientes en cuanto a memoria.awk
se refiere a BSDawk
(como también se encuentra en OSX): es notablemente más lento quegawk
(GNU Awk) y especialmentemawk
.Aquí está el script Bash (
testrepeat
) que produjo lo anterior. Se necesitan 2 argumentos:En otras palabras: los tiempos anteriores se obtuvieron con
testrepeat 100 1000
ytestrepeat 1000000 1000
fuente
In order to use brace expansion with a variable, we must use `eval`
👍Hay más de una forma de hacerlo.
Usando un bucle:
La expansión de llaves se puede usar con literales enteros:
Un bucle tipo C permite el uso de variables:
Usando el
printf
incorporado:Especificar una precisión aquí trunca la cadena para que se ajuste al ancho especificado (
0
). Comoprintf
reutiliza la cadena de formato para consumir todos los argumentos, esto simplemente imprime"="
100 veces.Usando
head
(printf
, etc.) ytr
:fuente
head
/tr
, que funciona bien incluso con recuentos altos de repetición (pequeña advertencia:head -c
no es compatible con POSIX, pero BSD y GNU lohead
implementan); Si bien las otras dos soluciones serán lentas en ese caso, también tienen la ventaja de trabajar con cadenas de caracteres múltiples .yes
yhead
- útil si se desea un cierto número de saltos de línea:yes "" | head -n 100
.tr
puede hacer que imprima cualquier carácter:yes "" | head -n 100 | tr "\n" "="; echo
dd if=/dev/zero count=1 bs=100000000 | tr '\0' '=' >/dev/null
es significativamente más lento que lahead -c100000000 < /dev/zero | tr '\0' '=' >/dev/null
versión. Por supuesto, debe usar un tamaño de bloque de 100M + para medir la diferencia de tiempo razonablemente. 100M bytes toman 1.7 sy 1 s con las dos versiones respectivas mostradas. Me quité el tr y lo descargué/dev/null
y obtuve 0.287 s para lahead
versión y 0.675 s para ladd
versión por mil millones 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
;Acabo de encontrar una manera realmente fácil de hacer esto usando seq:
ACTUALIZACIÓN: Esto funciona en el BSD
seq
que viene con OS X. YMMV con otras versionesImprimirá '#' 10 veces, así:
-f "#"
establece la cadena de formato para ignorar los números y solo imprimir#
para cada uno.-s ''
establece el separador en una cadena vacía para eliminar las nuevas líneas que se insertan entre cada número-f
y-s
parecen ser importantes.EDITAR: Aquí está en una función útil ...
A lo que puedes llamar así ...
NOTA: Si estás repitiendo
#
, ¡las citas son importantes!fuente
seq: format ‘#’ has no % directive
.seq
es para números, no cadenas. Ver gnu.org/software/coreutils/manual/html_node/seq-invocation.htmlseq
se está reutilizando inteligentemente aquí para replicar cadenas : la cadena de formato que se pasa a-f
, normalmente utilizada para formatear los números que se generan, contiene solo la cadena para replicar aquí, de modo que la salida contiene copias de esa cadena solamente. Desafortunadamente, GNUseq
insiste en la presencia de un formato de número en la cadena de formato, que es el error que está viendo."$1"
(comillas dobles), para que también pueda pasar caracteres como'*'
y cadenas con espacios en blanco incrustados. Finalmente, si desea poder usarlo%
, debe duplicarlo (de lo contrarioseq
, pensará que es parte de una especificación de formato como%f
); usando"${1//%/%%}"
se encargaría de eso. Como (como mencionas) estás usando BSDseq
, esto funcionará en sistemas operativos similares a BSD en general (por ejemplo, FreeBSD); por el contrario, no funcionará en Linux , donde se usa GNUseq
.Aquí hay dos formas interesantes:
Tenga en cuenta que estos dos son sutilmente diferentes: el
paste
método termina en una nueva línea. Eltr
método no lo hace.fuente
paste
requiere inexplicablemente-d '\0'
para especificar un delimitador vacío, y falla con-d ''
:-d '\0'
debería funcionar con todas laspaste
implementaciones compatibles con POSIX y, de hecho, también funciona con 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
. Sin embargo, lo que es más importante: si está utilizando deprintf
todos modos, también puedeprintf '%.s=' $(seq 500)
No hay manera simple. Evite el uso de bucles
printf
y la sustitución.fuente
repl = 100
, por ejemplo (no\n
repl() { local ts=$(printf "%${2}s"); printf %s "${ts// /$1}"; }
Si quieres POSIX-cumplimiento y la coherencia entre las diferentes implementaciones de
echo
yprintf
, y / o conchas que no sea sólobash
:... producirá la misma salida que
perl -E 'say "=" x 100'
casi en todas partes.fuente
seq
no es una utilidad POSIX (aunque los sistemas BSD y Linux tienen implementaciones de la misma): en su lugar, puede hacer aritmética de shell POSIX con unwhile
bucle, como en la respuesta de @ Xennex81 (conprintf "="
, como sugiere correctamente, en lugar deecho -n
).cal
es POSIXseq
no es. De todos modos, en lugar de reescribir la respuesta con un ciclo while (como dices, eso ya está en otras respuestas) agregaré una función RYO. Más educativo de esa manera ;-).La pregunta era sobre cómo hacerlo con
echo
:Esto hará exactamente lo mismo
perl -E 'say "=" x 100'
peroecho
solo con .fuente
$_1
,$_2
o cualquier otra de las cien variables tienen valores.Una manera pura de Bash sin
eval
, sin subcapas, sin herramientas externas, sin expansiones de llaves (es decir, puede tener el número para repetir en una variable):Si le dan una variable
n
que se expande a un número (no negativo) y una variablepattern
, por ejemplo,Puedes hacer una función con esto:
Con este conjunto:
Para este pequeño truco estamos usando
printf
bastante con:-v varname
: en lugar de imprimir a la salida estándar,printf
colocará el contenido de la cadena formateada en variablevarname
.printf
usará el argumento para imprimir el número correspondiente de espacios. Por ejemplo,printf '%*s' 42
imprimirá 42 espacios.${var// /$pattern}
se expandirá a la expansión devar
con todos los espacios reemplazados por la expansión de$pattern
.También puede deshacerse de la
tmp
variable en larepeat
función utilizando la expansión indirecta:fuente
bash
las operaciones de reemplazo de cadenas globales en el contexto de la expansión de parámetros (${var//old/new}
) son particularmente lentas: terriblemente lento en bash3.2.57
y lento en bash4.3.30
, al menos en mi sistema OSX 10.10.3 en una máquina Intel Core i5 de 3.2 Ghz: con un recuento de 1,000, las cosas son lentas (3.2.57
) / rápidas (4.3.30
): 0.1 / 0.004 segundos. Aumentar la cuenta a 10,000 produce números sorprendentemente diferentes:repeat 10000 = var
toma alrededor de 80 segundos (!) En bash3.2.57
, y alrededor de 0.3 segundos en bash4.3.30
(mucho más rápido que encendido3.2.57
, pero aún lento).O
Ejemplo
fuente
awk 'BEGIN { while (c++ < 100) printf "=" }'
. Envuelta en una función de shell parametrizado (invoke comorepeat 100 =
, por ejemplo):repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { txt=substr(txt, 2); while (i++ < count) printf txt }'; }
. (El.
prefijo ficticio char y lasubstr
llamada complementaria son necesarios para evitar un error en BSDawk
, donde pasar un valor variable que comienza con=
rompe el comando.)NF = 100
solución es muy inteligente (aunque para obtener 100=
, debe usarNF = 101
). Las advertencias son que se estrella BSDawk
(pero es muy rápido congawk
y aún más rápido conmawk
), y que se analizan en POSIX ni asignar aNF
, ni el uso de los campos enBEGIN
los bloques. Puede hacer que funcione también en BSDawk
con un ligero ajuste:awk 'BEGIN { OFS = "="; $101=""; print }'
(pero curiosamente, en BSDawk
eso no es más rápido que la solución de bucle). Como solución shell parametrizada:repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { OFS=substr(txt, 2); $(count+1)=""; print }'; }
.original-awk
es el nombre bajo Linux del awk más antiguo similar al awk de BSD, que también se ha informado que se bloquea, si desea probar esto. Tenga en cuenta que el bloqueo suele ser el primer paso para encontrar un error explotable. Esta respuesta está promoviendo un código inseguro.original-awk
no es estándar y no se recomiendaawk NF=100 OFS='=' <<< ""
(usandobash
ygawk
)Supongo que el propósito original de la pregunta era hacer esto solo con los comandos integrados del shell. Así
for
bucles yprintf
s serían legítimos, mientras querep
,perl
y tambiénjot
más adelante no lo haría. Aún así, el siguiente comandojot -s "/" -b "\\" $((COLUMNS/2))
por ejemplo, imprime una línea de
\/\/\/\/\/\/\/\/\/\/\/\/
fuente
jot -s '' -b '=' 100
. La advertencia es que si bien las plataformas tipo BSD, incluido OSX, vienen conjot
, las distribuciones de Linux no .apt install athena-jot
proporcionaríajot
.Como otros han dicho, en bash brace la expansión precede a la expansión de parámetros , por lo que los rangos solo pueden contener literales. y proporciona soluciones limpias, pero no son totalmente portátiles de un sistema a otro, incluso si está utilizando el mismo shell en cada uno. (Aunque está cada vez más disponible; por ejemplo, en FreeBSD 9.3 y superior ) y otras formas de indirección siempre funcionan, pero son algo poco elegantes.
{m,n}
seq
jot
seq
eval
Afortunadamente, bash admite el estilo C para bucles (solo con expresiones aritméticas). Así que aquí hay una forma concisa de "puro golpe":
Esto toma el número de repeticiones como el primer argumento y la cadena que se repetirá (que puede ser un solo carácter, como en la descripción del problema) como el segundo argumento.
repecho 7 b
salidasbbbbbbb
(terminadas por una nueva línea).Dennis Williamson dio esencialmente esta solución hace cuatro años en su excelente respuesta a la creación de cadenas de caracteres repetidos en script de shell . El cuerpo de mi función difiere ligeramente del código allí:
Dado que el enfoque aquí está en repetir un solo carácter y el shell es bash, probablemente sea seguro usarlo en
echo
lugar de hacerloprintf
. Y leí la descripción del problema en esta pregunta como una preferencia por imprimirecho
. La definición de función anterior funciona en bash y ksh93 . Aunqueprintf
es más portátil (y generalmente debería usarse para este tipo de cosas),echo
la sintaxis de este es posiblemente más legible.Las
echo
funciones integradas de algunos shells se interpretan-
por sí mismas como una opción, aunque el significado habitual de-
utilizar stdin para la entrada no tiene sentidoecho
. zsh hace esto. Y definitivamente existen correos electrónicosecho
que no reconocen-n
, ya que no es estándar . (Muchos proyectiles de estilo Bourne no aceptan el estilo C para los bucles, por lo tanto,echo
no es necesario considerar su comportamiento ...)Aquí la tarea es imprimir la secuencia; allí , fue para asignarlo a una variable.
Si
$n
es el número deseado de repeticiones y no tiene que reutilizarlo, y desea algo aún más corto:n
debe ser una variable; de esta manera no funciona con parámetros posicionales.$s
es el texto a repetir.fuente
printf "%100s" | tr ' ' '='
Es óptimo.zsh
, por cierto El enfoque echo-in-a-loop funciona bien para recuentos de repetición más pequeños, pero para los más grandes existen alternativas compatibles con POSIX basadas en utilidades , como lo demuestra el comentario de @ Slomojo.(while ((n--)); do echo -n "$s"; done; echo)
echo
integrada que lo admite-n
. El espíritu de lo que estás diciendo es absolutamente correcto.printf
debería preferirse casi siempreecho
, al menos en uso no interactivo. Pero no creo que sea de ninguna manera inapropiado o engañoso dar unaecho
respuesta a una pregunta que hizo una y que proporcionó suficiente información para saber que funcionaría . Tenga en cuenta también que el soporte para((n--))
(sin a$
) no está garantizado por POSIX.Python es omnipresente y funciona igual en todas partes.
python -c "import sys; print('*' * int(sys.argv[1]))" "=" 100
El carácter y el recuento se pasan como parámetros separados.
fuente
python -c "import sys; print(sys.argv[1] * int(sys.argv[2]))" "=" 100
En bash 3.0 o superior
fuente
Otro medio para repetir una cadena arbitraria n veces:
Pros:
Contras:
yes
comando de Gnu Core Utils .Con un terminal ANSI y caracteres US-ASCII para repetir. Puede usar una secuencia de escape ANSI CSI. Es la forma más rápida de repetir un personaje.
O estáticamente:
Imprima una línea de 80 veces
=
:printf '=\e[80b\n'
Limitaciones:
repeat_char
secuencia ANSI CSI.repeat_char
secuencia ANSI CSI al carácter repetido.fuente
Esto es lo que uso para imprimir una línea de caracteres en la pantalla en Linux (según el ancho del terminal / pantalla)
Imprima "=" en la pantalla:
Explicación:
Imprima un signo igual tantas veces como la secuencia dada:
Use la salida de un comando (esta es una característica bash llamada Sustitución de comandos):
Da una secuencia, he usado del 1 al 20 como ejemplo. En el comando final se usa el comando tput en lugar de 20:
Indique el número de columnas que se usan actualmente en la terminal:
fuente
fuente
fuente
Lo más simple es usar este one-liner en csh / tcsh:
fuente
Una alternativa más elegante a la solución Python propuesta podría ser:
fuente
En caso de que desee repetir un carácter n veces siendo un número VARIABLE de veces dependiendo de, por ejemplo, la longitud de una cadena que puede hacer:
Muestra:
fuente
length
no funcionaráexpr
, probablemente quisiste decirn=$(expr 10 - ${#vari})
; Sin embargo, es más simple y más eficiente de utilizar expansión aritmética de Bash:n=$(( 10 - ${#vari} ))
. Además, en el centro de su respuesta está el enfoque de Perl en el que el OP está buscando una alternativa Bash .Esta es la versión más larga de lo que Eliah Kagan estaba defendiendo:
Por supuesto, también puedes usar printf para eso, pero no es de mi agrado:
Esta versión es compatible con Dash:
siendo yo el número inicial.
fuente
while (( i-- )); do echo -n " "; done
funciona.Ejecuciones de muestra
Referencia lib en: https://github.com/gdbtek/linux-cookbooks/blob/master/libraries/util.bash
fuente
Puede hacer esto
echo
si loecho
siguesed
:En realidad, eso
echo
es innecesario allí.fuente
Mi respuesta es un poco más complicada, y probablemente no sea perfecta, pero para aquellos que buscan generar grandes números, pude hacer alrededor de 10 millones en 3 segundos.
fuente
Lo más simple es usar este one-liner en bash:
fuente
Otra opción es usar GNU seq y eliminar todos los números y líneas nuevas que genera:
Este comando imprime el
#
personaje 100 veces.fuente
La mayoría de las soluciones existentes en todo dependen de
{1..10}
apoyo sintaxis de la cáscara, que esbash
- yzsh
- específica, y no funciona entcsh
OpenBSD deksh
y la mayoría no fiestash
.Lo siguiente debería funcionar en OS X y todos los sistemas * BSD en cualquier shell; de hecho, puede usarse para generar una matriz completa de varios tipos de espacio decorativo:
Lamentablemente, no tenemos una nueva línea final; que se puede arreglar con un extra
printf '\n'
después del doblez:Referencias
fuente
Mi propuesta (aceptando valores variables para n):
fuente