Comment pourrais-je faire ça avec echo
?
perl -E 'say "=" x 100'
ruby -e 'puts "=" * 100'
oupython -c 'print "=" * 100'
printf
avec seq
)svrb=`printf '%.sv' $(seq $vrb)`
Comment pourrais-je faire ça avec echo
?
perl -E 'say "=" x 100'
ruby -e 'puts "=" * 100'
oupython -c 'print "=" * 100'
printf
avec seq
)svrb=`printf '%.sv' $(seq $vrb)`
Réponses:
Vous pouvez utiliser:
printf '=%.0s' {1..100}
Comment cela fonctionne:
Bash développe {1..100} de sorte que la commande devient:
printf '=%.0s' 1 2 3 4 ... 100
J'ai défini le format de printf, =%.0s
ce qui signifie qu'il imprimera toujours un seul, =
quel que soit l'argument qui lui est donné. Il imprime donc 100 =
s.
repl = 100
, par exemple (une eval
astuce est malheureusement nécessaire pour baser l'expansion d'accolade sur une variable):repl() { printf "$1"'%.s' $(eval "echo {1.."$(($2))"}"); }
seq
plutôt par exemple $(seq 1 $limit)
.
$s%.0s
à %.0s$s
sinon les tirets provoquent une printf
erreur.
printf
: il continue d'appliquer la chaîne de format jusqu'à ce qu'il n'y ait plus d'arguments. J'avais supposé qu'il n'avait traité la chaîne de format qu'une seule fois!
Pas facile. Mais par exemple:
seq -s= 100|tr -d '[:digit:]'
Ou peut-être d'une manière conforme aux normes:
printf %100s |tr " " "="
Il y en a aussi un tput rep
, mais quant à mes terminaux à portée de main (xterm et linux) ils ne semblent pas le supporter :)
=
caractères.
printf
tr
est la seule solution POSIX car seq
, yes
et {1..3}
ne sont pas POSIX.
printf %100s | sed 's/ /abc/g'
- affiche 'abcabcabc ...'
tr
). Vous pouvez également l'étendre à quelque chose comme printf "%${COLUMNS}s\n" | tr " " "="
.
wc
. La seule conclusion que je peux en tirer est " seq
ne devrait pas être utilisée".
Pointe du chapeau à @ gniourf_gniourf pour sa contribution.
Remarque: Cette réponse ne répond pas à la question d'origine, mais complète les réponses utiles existantes en comparant les performances .
Les solutions sont comparées uniquement en termes de vitesse d'exécution - les besoins en mémoire ne sont pas pris en compte (ils varient selon les solutions et peuvent être importants avec de grands nombres de répétitions).
Résumé:
${var// /=}
), car il est extrêmement lent.Les temporisations suivantes sont prises sur un iMac fin 2012 avec un processeur Intel Core i5 à 3,2 GHz et un Fusion Drive, exécutant OSX 10.10.4 et bash 3.2.57, et représentent la moyenne de 1000 exécutions.
Les entrées sont:
M
... une solution potentiellement multi- caractèresS
... une seule solution -character seuleP
... une solution compatible POSIX[M, P] printf %.s= [dogbane]: 0.0002
[M ] printf + bash global substr. replacement [Tim]: 0.0005
[M ] echo -n - brace expansion loop [eugene y]: 0.0007
[M ] echo -n - arithmetic loop [Eliah Kagan]: 0.0013
[M ] seq -f [Sam Salisbury]: 0.0016
[M ] jot -b [Stefan Ludwig]: 0.0016
[M ] awk - $(count+1)="=" [Steven Penny (variant)]: 0.0019
[M, P] awk - while loop [Steven Penny]: 0.0019
[S ] printf + tr [user332325]: 0.0021
[S ] head + tr [eugene y]: 0.0021
[S, P] dd + tr [mklement0]: 0.0021
[M ] printf + sed [user332325 (comment)]: 0.0021
[M ] mawk - $(count+1)="=" [Steven Penny (variant)]: 0.0025
[M, P] mawk - while loop [Steven Penny]: 0.0026
[M ] gawk - $(count+1)="=" [Steven Penny (variant)]: 0.0028
[M, P] gawk - while loop [Steven Penny]: 0.0028
[M ] yes + head + tr [Digital Trauma]: 0.0029
[M ] Perl [sid_com]: 0.0059
awk
et les perl
solutions.[M ] Perl [sid_com]: 0.0067
[M ] mawk - $(count+1)="=" [Steven Penny (variant)]: 0.0254
[M ] gawk - $(count+1)="=" [Steven Penny (variant)]: 0.0599
[S ] head + tr [eugene y]: 0.1143
[S, P] dd + tr [mklement0]: 0.1144
[S ] printf + tr [user332325]: 0.1164
[M, P] mawk - while loop [Steven Penny]: 0.1434
[M ] seq -f [Sam Salisbury]: 0.1452
[M ] jot -b [Stefan Ludwig]: 0.1690
[M ] printf + sed [user332325 (comment)]: 0.1735
[M ] yes + head + tr [Digital Trauma]: 0.1883
[M, P] gawk - while loop [Steven Penny]: 0.2493
[M ] awk - $(count+1)="=" [Steven Penny (variant)]: 0.2614
[M, P] awk - while loop [Steven Penny]: 0.3211
[M, P] printf %.s= [dogbane]: 2.4565
[M ] echo -n - brace expansion loop [eugene y]: 7.5877
[M ] echo -n - arithmetic loop [Eliah Kagan]: 13.5426
[M ] printf + bash global substr. replacement [Tim]: n/a
${foo// /=}
) est inexplicablement lent avec des chaînes de grande taille, et a été retiré du jeu (a pris environ 50 minutes (!) Dans Bash 4.3.30, et encore plus longtemps dans Bash 3.2.57 - je n'ai jamais attendu pour finir).(( i= 0; ... ))
) sont plus lentes que celles à croisillons ( {1..n}
) - bien que les boucles arithmétiques soient plus efficaces en mémoire.awk
fait référence à BSD awk
(comme on le trouve également sur OSX) - il est sensiblement plus lent que gawk
(GNU Awk) et surtout mawk
.Voici le script Bash ( testrepeat
) qui a produit ce qui précède. Il faut 2 arguments:
En d'autres termes: les timings ci-dessus ont été obtenus avec testrepeat 100 1000
ettestrepeat 1000000 1000
#!/usr/bin/env bash
title() { printf '%s:\t' "$1"; }
TIMEFORMAT=$'%6Rs'
# The number of repetitions of the input chars. to produce
COUNT_REPETITIONS=${1?Arguments: <charRepeatCount> [<testRunCount>]}
# The number of test runs to perform to derive the average timing from.
COUNT_RUNS=${2:-1}
# Discard the (stdout) output generated by default.
# If you want to check the results, replace '/dev/null' on the following
# line with a prefix path to which a running index starting with 1 will
# be appended for each test run; e.g., outFilePrefix='outfile', which
# will produce outfile1, outfile2, ...
outFilePrefix=/dev/null
{
outFile=$outFilePrefix
ndx=0
title '[M, P] printf %.s= [dogbane]'
[[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
# !! In order to use brace expansion with a variable, we must use `eval`.
eval "
time for (( n = 0; n < COUNT_RUNS; n++ )); do
printf '%.s=' {1..$COUNT_REPETITIONS} >"$outFile"
done"
title '[M ] echo -n - arithmetic loop [Eliah Kagan]'
[[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
time for (( n = 0; n < COUNT_RUNS; n++ )); do
for ((i=0; i<COUNT_REPETITIONS; ++i)); do echo -n =; done >"$outFile"
done
title '[M ] echo -n - brace expansion loop [eugene y]'
[[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
# !! In order to use brace expansion with a variable, we must use `eval`.
eval "
time for (( n = 0; n < COUNT_RUNS; n++ )); do
for i in {1..$COUNT_REPETITIONS}; do echo -n =; done >"$outFile"
done
"
title '[M ] printf + sed [user332325 (comment)]'
[[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
time for (( n = 0; n < COUNT_RUNS; n++ )); do
printf "%${COUNT_REPETITIONS}s" | sed 's/ /=/g' >"$outFile"
done
title '[S ] printf + tr [user332325]'
[[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
time for (( n = 0; n < COUNT_RUNS; n++ )); do
printf "%${COUNT_REPETITIONS}s" | tr ' ' '=' >"$outFile"
done
title '[S ] head + tr [eugene y]'
[[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
time for (( n = 0; n < COUNT_RUNS; n++ )); do
head -c $COUNT_REPETITIONS < /dev/zero | tr '\0' '=' >"$outFile"
done
title '[M ] seq -f [Sam Salisbury]'
[[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
time for (( n = 0; n < COUNT_RUNS; n++ )); do
seq -f '=' -s '' $COUNT_REPETITIONS >"$outFile"
done
title '[M ] jot -b [Stefan Ludwig]'
[[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
time for (( n = 0; n < COUNT_RUNS; n++ )); do
jot -s '' -b '=' $COUNT_REPETITIONS >"$outFile"
done
title '[M ] yes + head + tr [Digital Trauma]'
[[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
time for (( n = 0; n < COUNT_RUNS; n++ )); do
yes = | head -$COUNT_REPETITIONS | tr -d '\n' >"$outFile"
done
title '[M ] Perl [sid_com]'
[[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
time for (( n = 0; n < COUNT_RUNS; n++ )); do
perl -e "print \"=\" x $COUNT_REPETITIONS" >"$outFile"
done
title '[S, P] dd + tr [mklement0]'
[[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
time for (( n = 0; n < COUNT_RUNS; n++ )); do
dd if=/dev/zero bs=$COUNT_REPETITIONS count=1 2>/dev/null | tr '\0' "=" >"$outFile"
done
# !! On OSX, awk is BSD awk, and mawk and gawk were installed later.
# !! On Linux systems, awk may refer to either mawk or gawk.
for awkBin in awk mawk gawk; do
if [[ -x $(command -v $awkBin) ]]; then
title "[M ] $awkBin"' - $(count+1)="=" [Steven Penny (variant)]'
[[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
time for (( n = 0; n < COUNT_RUNS; n++ )); do
$awkBin -v count=$COUNT_REPETITIONS 'BEGIN { OFS="="; $(count+1)=""; print }' >"$outFile"
done
title "[M, P] $awkBin"' - while loop [Steven Penny]'
[[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
time for (( n = 0; n < COUNT_RUNS; n++ )); do
$awkBin -v count=$COUNT_REPETITIONS 'BEGIN { while (i++ < count) printf "=" }' >"$outFile"
done
fi
done
title '[M ] printf + bash global substr. replacement [Tim]'
[[ $outFile != '/dev/null' ]] && outFile="$outFilePrefix$((++ndx))"
# !! In Bash 4.3.30 a single run with repeat count of 1 million took almost
# !! 50 *minutes*(!) to complete; n Bash 3.2.57 it'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=1
if (( skip )); then
echo 'n/a' >&2
else
time for (( n = 0; n < COUNT_RUNS; n++ )); do
{ printf -v t "%${COUNT_REPETITIONS}s" '='; printf %s "${t// /=}"; } >"$outFile"
done
fi
} 2>&1 |
sort -t$'\t' -k2,2n |
awk -F $'\t' -v count=$COUNT_RUNS '{
printf "%s\t", $1;
if ($2 ~ "^n/a") { print $2 } else { printf "%.4f\n", $2 / count }}' |
column -s$'\t' -t
In order to use brace expansion with a variable, we must use `eval`
👍
Il y a plus d'une façon de le faire.
Utiliser une boucle:
L'expansion d'accolade peut être utilisée avec des littéraux entiers:
for i in {1..100}; do echo -n =; done
Une boucle de type C permet l'utilisation de variables:
start=1
end=100
for ((i=$start; i<=$end; i++)); do echo -n =; done
Utilisation de la fonction printf
intégrée:
printf '=%.0s' {1..100}
La spécification d'une précision ici tronque la chaîne pour l'adapter à la largeur spécifiée ( 0
). Comme printf
réutilise la chaîne de format pour consommer tous les arguments, cela imprime simplement "="
100 fois.
Utilisation de head
( printf
, etc) et tr
:
head -c 100 < /dev/zero | tr '\0' '='
printf %100s | tr " " "="
head
/ tr
, qui fonctionne bien même avec un nombre de répétitions élevé (petite mise en garde: head -c
n'est pas compatible POSIX, mais BSD et GNU l' head
implémentent); alors que les deux autres solutions seront lentes dans ce cas, elles ont également l'avantage de fonctionner avec des chaînes multi- caractères.
yes
et head
- utile si vous voulez un certain nombre de nouvelles lignes: yes "" | head -n 100
. tr
peut faire imprimer n'importe quel caractère:yes "" | head -n 100 | tr "\n" "="; echo
dd if=/dev/zero count=1 bs=100000000 | tr '\0' '=' >/dev/null
est beaucoup plus lent que la head -c100000000 < /dev/zero | tr '\0' '=' >/dev/null
version. Bien sûr, vous devez utiliser une taille de bloc de 100M + pour mesurer la différence de temps de manière raisonnable. 100M octets prend 1,7 s et 1 s avec les deux versions respectives illustrées. J'ai enlevé le tr et je l'ai simplement vidé /dev/null
et j'ai obtenu 0,287 s pour la head
version et 0,675 s pour la dd
version pour un milliard d'octets.
dd if=/dev/zero count=1 bs=100000000 | tr '\0' '=' >/dev/null
=> 0,21332 s, 469 MB/s
; Pour: dd if=/dev/zero count=100 bs=1000000| tr '\0' '=' >/dev/null
=> 0,161579 s, 619 MB/s
;
Je viens de trouver un moyen très simple de le faire en utilisant seq:
MISE À JOUR: Cela fonctionne sur le BSD seq
fourni avec OS X. YMMV avec d'autres versions
seq -f "#" -s '' 10
Imprime '#' 10 fois, comme ceci:
##########
-f "#"
définit la chaîne de format pour ignorer les nombres et simplement imprimer #
pour chacun.-s ''
définit le séparateur sur une chaîne vide pour supprimer les sauts de ligne qui seq insère entre chaque numéro-f
et -s
semblent importants.EDIT: Ici, c'est dans une fonction pratique ...
repeat () {
seq -f $1 -s '' $2; echo
}
Que vous pouvez appeler comme ça ...
repeat "#" 10
REMARQUE: si vous répétez, #
les citations sont importantes!
seq: format ‘#’ has no % directive
. seq
est pour les nombres, pas pour les chaînes. Voir gnu.org/software/coreutils/manual/html_node/seq-invocation.html
seq
est intelligemment réutilisé ici pour répliquer les chaînes : la chaîne de format transmise à -f
- normalement utilisée pour formater les nombres générés - contient uniquement la chaîne à répliquer ici afin que la sortie contienne uniquement des copies de cette chaîne. Malheureusement, GNU seq
insiste sur la présence d'un format numérique dans la chaîne de format, ce qui est l'erreur que vous voyez.
"$1"
(guillemets doubles), afin que vous puissiez également passer des caractères tels que '*'
et des chaînes avec des espaces blancs intégrés. Enfin, si vous voulez pouvoir l'utiliser %
, vous devez le doubler (sinon vous seq
penserez que cela fait partie d'une spécification de format telle que %f
); utiliser s'en "${1//%/%%}"
occuperait. Puisque (comme vous le mentionnez) vous utilisez BSD seq
, cela fonctionnera sur les systèmes d'exploitation de type BSD en général (par exemple, FreeBSD) - en revanche, cela ne fonctionnera pas sur Linux , où GNU seq
est utilisé.
Voici deux façons intéressantes:
ubuntu @ ubuntu: ~ $ yes = | tête -10 | coller -s -d '' - ========== ubuntu @ ubuntu: ~ $ yes = | tête -10 | tr -d "\ n" ========== ubuntu @ ubuntu: ~ $
Notez que ces deux sont subtilement différents - La paste
méthode se termine par une nouvelle ligne. La tr
méthode ne fonctionne pas.
paste
nécessite inexplicablement -d '\0'
de spécifier un délimiteur vide, et échoue avec -d ''
- -d '\0'
devrait fonctionner avec toutes les paste
implémentations compatibles POSIX et fonctionne également avec GNU paste
.
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
. Plus important encore, cependant: si vous utilisez de printf
toute façon, vous pouvez tout aussi bien adopter l'approche à la fois plus simple et plus efficace de la réponse acceptée:printf '%.s=' $(seq 500)
Il n'y a pas de moyen simple. Évitez l'utilisation printf
et la substitution des boucles .
str=$(printf "%40s")
echo ${str// /rep}
# echoes "rep" 40 times.
repl = 100
, par exemple (ne produit pas de fin \n
):repl() { local ts=$(printf "%${2}s"); printf %s "${ts// /$1}"; }
Si vous souhaitez la conformité POSIX et la cohérence entre les différentes implémentations de echo
et printf
, et / ou des shells autres que simplement bash
:
seq(){ n=$1; while [ $n -le $2 ]; do echo $n; n=$((n+1)); done ;} # If you don't have it.
echo $(for each in $(seq 1 100); do printf "="; done)
... produira la même sortie que perl -E 'say "=" x 100'
presque partout.
seq
n'est pas un utilitaire POSIX (bien que les systèmes BSD et Linux en aient des implémentations) - vous pouvez faire de l'arithmétique shell POSIX avec une while
boucle à la place, comme dans la réponse de @ Xennex81 (avec printf "="
, comme vous le suggérez correctement, plutôt que echo -n
).
cal
est POSIX. seq
n'est pas. Quoi qu'il en soit, plutôt que de réécrire la réponse avec une boucle while (comme vous le dites, c'est déjà dans d'autres réponses), j'ajouterai une fonction RYO. Plus pédagogique de cette façon ;-).
La question était de savoir comment le faire avec echo
:
echo -e ''$_{1..100}'\b='
Cela fera exactement la même chose perl -E 'say "=" x 100'
mais avec echo
seulement.
Une manière pure de Bash sans eval
, aucun sous-shell, aucun outil externe, aucune extension d'accolade (c.-à-d., Vous pouvez avoir le nombre à répéter dans une variable):
Si vous recevez une variable n
qui se développe en un nombre (non négatif) et une variable pattern
, par exemple,
$ n=5
$ pattern=hello
$ printf -v output '%*s' "$n"
$ output=${output// /$pattern}
$ echo "$output"
hellohellohellohellohello
Vous pouvez créer une fonction avec ceci:
repeat() {
# $1=number of patterns to repeat
# $2=pattern
# $3=output variable name
local tmp
printf -v tmp '%*s' "$1"
printf -v "$3" '%s' "${tmp// /$2}"
}
Avec cet ensemble:
$ repeat 5 hello output
$ echo "$output"
hellohellohellohellohello
Pour cette petite astuce, nous utilisons printf
beaucoup avec:
-v varname
: au lieu d'imprimer sur la sortie standard, printf
mettra le contenu de la chaîne formatée en variable varname
.printf
utilisera l'argument pour imprimer le nombre d'espaces correspondant. Par exemple, printf '%*s' 42
imprimera 42 espaces.${var// /$pattern}
va s'étendre à l'expansion de var
avec tous les espaces remplacés par l'expansion de $pattern
.Vous pouvez également vous débarrasser de la tmp
variable dans la repeat
fonction en utilisant l'expansion indirecte:
repeat() {
# $1=number of patterns to repeat
# $2=pattern
# $3=output variable name
printf -v "$3" '%*s' "$1"
printf -v "$3" '%s' "${!3// /$2}"
}
bash
les opérations globales de remplacement de chaîne dans le contexte de l'expansion des paramètres ( ${var//old/new}
) soient particulièrement lentes: atrocement lent en bash 3.2.57
, et lent en bash 4.3.30
, au moins sur mon système OSX 10.10.3 sur une machine Intel Core i5 à 3,2 Ghz: avec un compte de 1000, les choses sont lentes ( 3.2.57
) / rapides ( 4.3.30
): 0,1 / 0,004 secondes. Augmenter le nombre à 10 000 donne des nombres étonnamment différents: repeat 10000 = var
prend environ 80 secondes (!) En bash 3.2.57
et environ 0,3 seconde en bash 4.3.30
(beaucoup plus rapide qu'activé 3.2.57
, mais toujours lent).
#!/usr/bin/awk -f
BEGIN {
OFS = "="
NF = 100
print
}
Ou
#!/usr/bin/awk -f
BEGIN {
while (z++ < 100) printf "="
}
awk 'BEGIN { while (c++ < 100) printf "=" }'
. Enveloppé dans une fonction d'enveloppe paramétrée (invoquer comme repeat 100 =
, par exemple): repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { txt=substr(txt, 2); while (i++ < count) printf txt }'; }
. (Le .
préfixe factice et l' substr
appel complémentaire sont nécessaires pour contourner un bogue dans BSD awk
, où le passage d'une valeur de variable qui commence par =
rompt la commande.)
NF = 100
solution est très intelligente (mais pour obtenir 100 =
, vous devez utiliser NF = 101
). Les mises en garde sont qu'il se bloque BSD awk
(mais il est très rapide avec gawk
et encore plus rapide avec mawk
), et qui traite ni POSIX assigner à NF
, ni l' utilisation de champs dans les BEGIN
blocs. Vous pouvez également le faire fonctionner dans BSD awk
avec un léger ajustement: awk 'BEGIN { OFS = "="; $101=""; print }'
(mais curieusement, dans BSD awk
ce n'est pas plus rapide que la solution de boucle). En tant que solution shell paramétrées: repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { OFS=substr(txt, 2); $(count+1)=""; print }'; }
.
original-awk
le nom sous Linux de l'ancien awk similaire à awk de BSD, qui a également été signalé en panne, si vous voulez essayer cela. Notez que le plantage est généralement la première étape vers la recherche d'un bogue exploitable. Cette réponse est donc la promotion du code non sécurisé.
original-awk
n'est pas standard et n'est pas recommandée
awk NF=100 OFS='=' <<< ""
(en utilisant bash
et gawk
)
Je suppose que le but initial de la question était de le faire uniquement avec les commandes intégrées du shell. Ainsi , les for
boucles et printf
s seraient légitimes, alors que rep
, perl
et aussi jot
ci - dessous ne serait pas. Pourtant, la commande suivante
jot -s "/" -b "\\" $((COLUMNS/2))
par exemple, imprime une ligne de \/\/\/\/\/\/\/\/\/\/\/\/
jot -s '' -b '=' 100
. La mise en garde est que, bien que les plates-formes de type BSD, y compris OSX, soient fourniesjot
, les distributions Linux ne le sont pas .
apt install athena-jot
fournirait jot
.
Comme d'autres l'ont dit, l' expansion des accolades en bash précède l' expansion des paramètres , les plages ne peuvent donc contenir que des littéraux. et fournir des solutions propres mais ne sont pas entièrement portables d'un système à l'autre, même si vous utilisez le même shell sur chacun. (Bien qu'il soit de plus en plus disponible; par exemple, dans FreeBSD 9.3 et supérieur .) Et d'autres formes d'indirection fonctionnent toujours mais sont quelque peu inélégantes.{m,n}
seq
jot
seq
eval
Heureusement, bash prend en charge le style C pour les boucles (avec des expressions arithmétiques uniquement). Voici donc une méthode concise de "pure bash":
repecho() { for ((i=0; i<$1; ++i)); do echo -n "$2"; done; echo; }
Cela prend le nombre de répétitions comme premier argument et la chaîne à répéter (qui peut être un seul caractère, comme dans la description du problème) comme deuxième argument. repecho 7 b
sorties bbbbbbb
(terminées par une nouvelle ligne).
Dennis Williamson a donné essentiellement cette solution il y a quatre ans dans son excellente réponse à la création d'une chaîne de caractères répétés dans un script shell . Mon corps de fonction diffère légèrement du code là-bas:
Puisque l'accent est mis ici sur la répétition d'un seul caractère et que le shell est bash, il est probablement sûr d'utiliser à la echo
place de printf
. Et j'ai lu la description du problème dans cette question comme exprimant une préférence pour imprimer avec echo
. La définition de fonction ci-dessus fonctionne en bash et ksh93 . Bien qu'elle printf
soit plus portable (et devrait généralement être utilisée pour ce genre de choses), echo
la syntaxe de est sans doute plus lisible.
Certains echo
composants internes de shells s'interprètent -
par eux-mêmes comme une option - même si la signification habituelle de -
, pour utiliser stdin comme entrée, n'a pas de sens pour echo
. zsh fait cela. Et il existe certainement des echo
s qui ne reconnaissent pas -n
, car ce n'est pas standard . (De nombreux shells de style Bourne n'acceptent pas du tout le style C pour les boucles, donc leur echo
comportement n'a pas besoin d'être pris en compte ..)
Ici, la tâche consiste à imprimer la séquence; là , c'était pour l'assigner à une variable.
Si $n
est le nombre de répétitions souhaité et que vous n'avez pas à le réutiliser, et que vous voulez quelque chose d'encore plus court:
while ((n--)); do echo -n "$s"; done; echo
n
doit être une variable - cette méthode ne fonctionne pas avec les paramètres de position. $s
est le texte à répéter.
printf "%100s" | tr ' ' '='
est optimal.
zsh
également également. L'approche de l'écho en boucle fonctionne bien pour les petits nombres de répétitions, mais pour les plus grands, il existe des alternatives compatibles POSIX basées sur les utilitaires , comme en témoigne le commentaire de @ Slomojo.
(while ((n--)); do echo -n "$s"; done; echo)
echo
intégrée qui prend en charge -n
. L'esprit de ce que vous dites est absolument correct. printf
devrait presque toujours être préféré à echo
, au moins dans une utilisation non interactive. Mais je ne pense pas qu'il soit en aucune façon inapproprié ou trompeur de donner une echo
réponse à une question qui en a demandé une et qui a donné suffisamment d'informations pour savoir que cela fonctionnerait . Veuillez également noter que la prise en charge de ((n--))
(sans a $
) n'est pas garantie par POSIX.
Python est omniprésent et fonctionne de la même manière partout.
python -c "import sys; print('*' * int(sys.argv[1]))" "=" 100
Le caractère et le nombre sont passés en tant que paramètres séparés.
python -c "import sys; print(sys.argv[1] * int(sys.argv[2]))" "=" 100
Un autre moyen de répéter une chaîne arbitraire n fois:
Avantages:
Les inconvénients:
yes
commande de Gnu Core Utils .#!/usr/bin/sh
to_repeat='='
repeat_count=80
yes "$to_repeat" | tr -d '\n' | head -c "$repeat_count"
Avec un terminal ANSI et des caractères US-ASCII à répéter. Vous pouvez utiliser une séquence d'échappement ANSI CSI. C'est le moyen le plus rapide de répéter un caractère.
#!/usr/bin/env bash
char='='
repeat_count=80
printf '%c\e[%db' "$char" "$repeat_count"
Ou statiquement:
Imprimez une ligne de 80 fois =
:
printf '=\e[80b\n'
Limites:
repeat_char
séquence ANSI CSI.repeat_char
séquence CSI ANSI en caractère répété.Voici ce que j'utilise pour imprimer une ligne de caractères sur l'écran sous Linux (en fonction de la largeur du terminal / de l'écran)
printf '=%.0s' $(seq 1 $(tput cols))
Explication:
Imprimez un signe égal autant de fois que la séquence donnée:
printf '=%.0s' #sequence
Utilisez la sortie d'une commande (il s'agit d'une fonction bash appelée Substitution de commande):
$(example_command)
Donnez une séquence, j'ai utilisé 1 à 20 comme exemple. Dans la commande finale, la commande tput est utilisée au lieu de 20:
seq 1 20
Indiquez le nombre de colonnes actuellement utilisées dans le terminal:
tput cols
for i in {1..100}
do
echo -n '='
done
echo
Le plus simple est d'utiliser ce one-liner dans csh / tcsh:
printf "%50s\n" '' | tr '[:blank:]' '[=]'
Une alternative plus élégante à la solution Python proposée pourrait être:
python -c 'print "="*(1000)'
Dans le cas où vous voulez répéter un caractère n fois étant na nombre VARIABLE de fois en fonction, par exemple, de la longueur d'une chaîne, vous pouvez faire:
#!/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)
Il affiche:
vari equals.............................: AB
Up to 10 positions I must fill with.....: 8 equal signs
AB========
length
ne fonctionnera pas expr
, vous vouliez probablement dire n=$(expr 10 - ${#vari})
; cependant, il est plus simple et plus efficace d'utiliser l' évaluation arithmétique de Bash: n=$(( 10 - ${#vari} ))
. De plus, au cœur de votre réponse se trouve l'approche très Perl à laquelle l'OP cherche une alternative à Bash .
Voici la version plus longue de ce qu'Eliah Kagan épousait:
while [ $(( i-- )) -gt 0 ]; do echo -n " "; done
Bien sûr, vous pouvez également utiliser printf pour cela, mais pas vraiment à mon goût:
printf "%$(( i*2 ))s"
Cette version est compatible Dash:
until [ $(( i=i-1 )) -lt 0 ]; do echo -n " "; done
avec i étant le nombre initial.
function repeatString()
{
local -r string="${1}"
local -r numberToRepeat="${2}"
if [[ "${string}" != '' && "${numberToRepeat}" =~ ^[1-9][0-9]*$ ]]
then
local -r result="$(printf "%${numberToRepeat}s")"
echo -e "${result// /${string}}"
fi
}
Exemples de parcours
$ repeatString 'a1' 10
a1a1a1a1a1a1a1a1a1a1
$ repeatString 'a1' 0
$ repeatString '' 10
Bibliothèque de référence sur: https://github.com/gdbtek/linux-cookbooks/blob/master/libraries/util.bash
Ma réponse est un peu plus compliquée, et probablement pas parfaite, mais pour ceux qui cherchent à produire de grands nombres, j'ai pu faire environ 10 millions en 3 secondes.
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 x
for 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}
}
Le plus simple est d'utiliser ce one-liner en bash:
seq 10 | xargs -n 1 | xargs -I {} echo -n ===\>;echo
La plupart des solutions existantes dépendent toutes de la prise en {1..10}
charge de la syntaxe du shell, qui est bash
- et zsh
- spécifique, et ne fonctionne pas dans tcsh
ou OpenBSD ksh
et la plupart non bashsh
.
Les éléments suivants devraient fonctionner sur OS X et tous les systèmes * BSD dans n'importe quel shell; en fait, il peut être utilisé pour générer toute une matrice de différents types d'espace décoratif:
$ printf '=%.0s' `jot 64` | fold -16
================
================
================
================$
Malheureusement, nous n'obtenons pas de nouvelle ligne de fin; qui peut être fixé par un supplément printf '\n'
après le pli:
$ printf "=%.0s" `jot 64` | fold -16 ; printf "\n"
================
================
================
================
$
Références:
Ma proposition (accepter des valeurs variables pour n):
n=100
seq 1 $n | xargs -I {} printf =