Bashでキャラクターを繰り返すにはどうすればよいですか?


240

どうすればこれを行うことができechoますか?

perl -E 'say "=" x 100'

悲しいことに、これはバッシュではありません。
solidsnack 2016

1
エコーではなく、同じトピックに関するruby -e 'puts "=" * 100'ものpython -c 'print "=" * 100'
Evgeny

1
すばらしい質問です。とても良い答えです。私は例として投稿しますことを、ここで本当の仕事での答えのいずれかを使用しました:github.com/drbeco/oldfiles/blob/master/oldfiles(使用printfしてseqsvrb=`printf '%.sv' $(seq $vrb)`
博士ベコ

何でも印刷するための一般的なソリューション(1行以上の文字、改行を含む):Repeat_this(){i = 1; while ["$ i" -le "$ 2"]; printf "%s" "$ 1"を実行します。i = $(($ i + 1)); 完了しました。printf '\ n';}。次のように使用します:Repeat_this "something" Number_of_repetitions。たとえば、3つの改行を含む何かを5回繰り返すことを示すには、Repeat_this "$(printf '\ n \ n \ nthis')" 5とします。最後のprintf '\ n'が取り出される場合があります(ただし、テキストファイルを作成するために挿入しましたが、最後の文字として改行が必要です!)
Olivier Dulac

回答:


396

以下を使用できます。

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

仕組み:

Bashは{1..100}を展開するため、コマンドは次のようになります。

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

私はprintfのフォーマットを設定しました。これは、与えられた引数に関係なく=%.0s、常に1つを出力することを意味し=ます。したがって、100 =秒印刷されます。


14
繰り返し回数が多くても適度に機能する優れたソリューション。ここであなたが呼び出すことができるラッパー関数のrepl = 100インスタンスのためには、(eval策略は変数のブレース展開を基づかため、残念ながら、必要とされる):repl() { printf "$1"'%.s' $(eval "echo {1.."$(($2))"}"); }
mklement0

7
変数を使用して上限を設定することは可能ですか?試してみましたが動作しません。
Mike Purcell、2014年

70
ブレース展開内では変数を使用できません。seq代わりに使用してください$(seq 1 $limit)
dogbane 2014年

11
これを機能化$s%.0sする%.0s$s場合、それをからに再配置することをお勧めしprintfます。そうしないと、ダッシュがエラーの原因になります。
KomodoDave 14

5
これにより、Bashの動作に気づきましたprintf。引数がなくなるまで、フォーマット文字列を適用し続けます。私はそれがフォーマット文字列を一度だけ処理すると仮定していました!
Jeenu 2015年

89

簡単な方法はありません。しかし、例えば:

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

または、おそらく標準に準拠した方法:

printf %100s |tr " " "="

もありますがtput rep、私の手元の端末(xtermとlinux)はサポートしていないようです:)


3
seqを使用した最初のオプションは、指定された数より1つ少ない数を印刷するため、この例では99 =文字を印刷します。
Camilo Martin

13
printf trとはPOSIX ではないためseq、これが唯一のPOSIXソリューションです。yes{1..3}
Ciro Santilli郝海东冠状病六四事件法轮功

2
単一の文字だけではなく文字列を繰り返すには:printf %100s | sed 's/ /abc/g'-「abcabcabc ...」を出力します
John Rix

3
ループなしで1つの外部コマンド(tr)のみを使用する場合は+1 。また、に拡張することもできprintf "%${COLUMNS}s\n" | tr " " "="ます。
ムシフィル2015年

2
@ mklement0まあ、私はあなたが誤って最後の改行を数えていたことを望んでいましたwc。私がこれから得ることができる唯一の結論は「seq使用されるべきではない」です。
Camilo Martin、

51

彼の入力のための@ gniourf_gniourfへの帽子の先端。

注:この回答は元の質問には答えませが、パフォーマンス比較することにより、既存の役立つ回答を補足します。

ソリューションは実行速度のみで比較されます-メモリー要件は考慮されません(ソリューションによって異なり、繰り返し回数が多い場合に問題になることがあります)。

概要:

  • 繰り返し回数が少ない場合、たとえば100程度までであればBashのみのソリューションを使用する価値があります、外部ユーティリティ、特にPerlの起動コストが問題になるます。
    • ただし、実際的に言えば、繰り返し文字のインスタンスが1つだけ必要な場合は、既存のすべての解決策が適切な場合があります。
  • 大きな繰り返し回数使用外部ユーティリティ、彼らははるかに高速になるだろうとして、。
    • 特に、Bashのグローバルな部分文字列を大きな文字列
      (たとえば${var// /=})で置き換えることは、非常に遅いため避けてください。

以下はタイミングです、OSX 10.10.4とbash 3.2.57を実行し、3.2 GHz Intel Core i5 CPUとFusion Driveを搭載した2012年後半のiMacでのであり、1000回の実行の平均です。

エントリは次のとおりです。

  • 実行時間の昇順でリストされています(最初に速い)
  • 接頭辞:
    • M...潜在的に複数文字のソリューション
    • S... 単一文字のみのソリューション
    • P ... POSIX準拠のソリューション
  • 続いてソリューションの簡単な説明
  • 元の回答の作成者の名前がサフィックス

  • 小さな繰り返し数:100
[M, P] printf %.s= [dogbane]:                           0.0002
[M   ] printf + bash global substr. replacement [Tim]:  0.0005
[M   ] echo -n - brace expansion loop [eugene y]:       0.0007
[M   ] echo -n - arithmetic loop [Eliah Kagan]:         0.0013
[M   ] seq -f [Sam Salisbury]:                          0.0016
[M   ] jot -b [Stefan Ludwig]:                          0.0016
[M   ] awk - $(count+1)="=" [Steven Penny (variant)]:   0.0019
[M, P] awk - while loop [Steven Penny]:                 0.0019
[S   ] printf + tr [user332325]:                        0.0021
[S   ] head + tr [eugene y]:                            0.0021
[S, P] dd + tr [mklement0]:                             0.0021
[M   ] printf + sed [user332325 (comment)]:             0.0021
[M   ] mawk - $(count+1)="=" [Steven Penny (variant)]:  0.0025
[M, P] mawk - while loop [Steven Penny]:                0.0026
[M   ] gawk - $(count+1)="=" [Steven Penny (variant)]:  0.0028
[M, P] gawk - while loop [Steven Penny]:                0.0028
[M   ] yes + head + tr [Digital Trauma]:                0.0029
[M   ] Perl [sid_com]:                                  0.0059
  • Bashのみのソリューションが主導権を握りますが、繰り返し数がこれだけ少ない場合のみです。(下記参照)。
  • 外部ユーティリティ、特にPerlの起動コストは重要です。これをループで呼び出す必要がある場合- 各反復の繰り返し回数が少ない場合は、マルチユーティリティ、、awkおよびperlソリューションを避けてください。

  • 大きな繰り返し数:1000000(100万)
[M   ] Perl [sid_com]:                                  0.0067
[M   ] mawk - $(count+1)="=" [Steven Penny (variant)]:  0.0254
[M   ] gawk - $(count+1)="=" [Steven Penny (variant)]:  0.0599
[S   ] head + tr [eugene y]:                            0.1143
[S, P] dd + tr [mklement0]:                             0.1144
[S   ] printf + tr [user332325]:                        0.1164
[M, P] mawk - while loop [Steven Penny]:                0.1434
[M   ] seq -f [Sam Salisbury]:                          0.1452
[M   ] jot -b [Stefan Ludwig]:                          0.1690
[M   ] printf + sed [user332325 (comment)]:             0.1735
[M   ] yes + head + tr [Digital Trauma]:                0.1883
[M, P] gawk - while loop [Steven Penny]:                0.2493
[M   ] awk - $(count+1)="=" [Steven Penny (variant)]:   0.2614
[M, P] awk - while loop [Steven Penny]:                 0.3211
[M, P] printf %.s= [dogbane]:                           2.4565
[M   ] echo -n - brace expansion loop [eugene y]:       7.5877
[M   ] echo -n - arithmetic loop [Eliah Kagan]:         13.5426
[M   ] printf + bash global substr. replacement [Tim]:  n/a
  • 問題からのPerlソリューションは、断然最速です。
  • Bashのグローバル文字列置換(${foo// /=})は、大きな文字列では説明のつかないほど遅く、実行から外されました(Bash 4.3.30では約50分(!)、Bash 3.2.57ではさらに長くなりました-私は待ちませんでした終了します)。
  • Bashループは低速で、算術ループ((( i= 0; ... )))はブレース展開されたもの({1..n})よりも低速ですが、算術ループの方がメモリ効率が高くなります。
  • awkBSD awk(OSXにもあります)を指します- gawk(GNU Awk)よりも著しく遅く、特にmawkです。
  • 大きなカウントと複数文字であることに注意してください。文字列、メモリ消費は考慮事項になる可能性があります-アプローチはその点で異なります。

上記を生成したBashスクリプトtestrepeat)を次に示します。2つの引数を取ります。

  • 文字繰り返し数
  • オプションで、実行して平均タイミングを計算するテスト実行の数

つまり、上記のタイミングはtestrepeat 100 1000testrepeat 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

タイミングの比較を見るのは興味深いですが、多くのプログラムでは出力がバッファリングされていると思うので、バッファリングがオフになっている場合、それらのタイミングを変更できます。
Sergiy Kolodyazhnyy 2017年

In order to use brace expansion with a variable, we must use `eval`👍
PYB

2
そのため、perlソリューション(sid_com)は基本的に最速です... perlの起動の初期オーバーヘッドに達した時点で。(小さなリピートの59ミリ秒から100万回のリピートの67ミリ秒に...なので、perlフォークはシステムで約59ミリ秒かかりました)
Olivier Dulac

46

それを行うには複数の方法があります。

ループを使用する:

  • ブレース展開は整数リテラルで使用できます。

    for i in {1..100}; do echo -n =; done    
  • Cのようなループでは、変数を使用できます。

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

printf組み込みを使用する:

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

ここで精度を指定すると、指定した幅(0)に合わせて文字列が切り捨てられます。printfすべての引数を消費するフォーマット文字列を再利用し、これは単純に印刷し"="100回。

使用headprintfなど)とtr

head -c 100 < /dev/zero | tr '\0' '='
printf %100s | tr " " "="

2
head/ trソリューションの++は、繰り返し回数が多い場合でもうまく機能します(小さな注意:head -cPOSIXに準拠していませんが、BSDとGNUの両方でhead実装されています)。その場合、他の2つのソリューションは遅くなりますが、複数文字の文字列を処理するという利点もあります。
mklement0

1
and-を使用するyeshead、特定の数の改行が必要な場合に役立ちますyes "" | head -n 100tr任意の文字を印刷できます:yes "" | head -n 100 | tr "\n" "="; echo
loxaxs '27年

やや意外:バージョンdd if=/dev/zero count=1 bs=100000000 | tr '\0' '=' >/dev/nullよりもかなり遅いですhead -c100000000 < /dev/zero | tr '\0' '=' >/dev/null。もちろん、時間差を合理的に測定するには、100M +のブロックサイズを使用する必要があります。100Mバイトには1.7秒と1秒かかり、2つのバージョンが示されています。私はtrを外してダンプした/dev/nullところ、headバージョンが0.287秒、バージョンが0.675秒dd、10億バイトでした。
Michael Goldshteyn

対象:dd if=/dev/zero count=1 bs=100000000 | tr '\0' '=' >/dev/null=> 0,21332 s, 469 MB/s; 対象:dd if=/dev/zero count=100 bs=1000000| tr '\0' '=' >/dev/null=> 0,161579 s, 619 MB/s;
2018

31

seqを使用してこれを行う非常に簡単な方法を見つけました。

更新:これseqはOS Xに付属するBSD で動作します。他のバージョンのYMMV

seq  -f "#" -s '' 10

次のように、「#」を10回出力します。

##########
  • -f "#"数字を無視して、#それぞれについて出力するだけのフォーマット文字列を設定します。
  • -s '' 区切り文字を空の文字列に設定して、seqが各数値の間に挿入する改行を削除します
  • -fと後のスペースは-s重要なようです。

編集:これは便利な機能です...

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

あなたはこのように呼ぶことができます...

repeat "#" 10

注:繰り返す#場合は、引用符が重要です!


7
これは私に与えますseq: format ‘#’ has no % directiveseq文字列ではなく数値用です。gnu.org/software/coreutils/manual/html_node/seq-invocation.html
John B

ああ、それで私はOS XにあるseqのBSDバージョンを使っていました。答えを更新します。どのバージョンを使用していますか?
サムソールズベリー

GNU coreutilsのseqを使用しています。
ジョンB

1
@JohnB:BSD seq文字列を複製するためにここで巧妙に転用されています:渡されるフォーマット文字列-f-通常は生成される数値をフォーマットするために使用されます-ここに複製する文字列のみが含まれるため、出力にはその文字列のコピーのみが含まれます。残念ながら、GNU は、表示されているエラーであるフォーマット文字列に数値フォーマットseq存在することを要求しています。
mklement0

1
よくできました。複数文字の文字列でも機能します。"$1"(二重引用符)を使用してください'*'。空白文字が埋め込まれた文字列やなどの文字を渡すこともできます。最後に、を使用できるようにするには%、それを2倍にする必要があります(それ以外の場合seqは、のような形式仕様の一部と見なされます%f)。使用"${1//%/%%}"することでそれを処理できます。あなたが使っている(あなたが言及したように)のでBSDを seq、これは一般的にはBSDライクなOS上で動作します(例えば、FreeBSDの) -対照的に、それはLinux上で動作しませんGNUが seq使用されています。
mklement0

18

ここに2つの興味深い方法があります。

ubuntu @ ubuntu:〜$はい= | 頭-10 | 貼り付け-s -d ''-
==========
ubuntu @ ubuntu:〜$はい= | 頭-10 | tr -d "\ n"
========== ubuntu @ ubuntu:〜$ 

これら2つは微妙に異なることに注意してください- pasteメソッドは新しい行で終わります。trこの方法にはありません。


1
よくできました。空の区切り文字を指定するためにBSDが paste不可解に必要-d '\0'であり、失敗することに注意してください-d ''- -d '\0'すべてのPOSIX互換のpaste実装で動作し、GNU pasteでも動作するはずです。
mklement0 2015

船外機の数が少ない、同様の精神:yes | mapfile -n 100 -C 'printf = \#' -c 1
ビショップ

@bishop:コマンドが実際に作成するサブシェルの数は1つ少なくなりますが、繰り返し数が多い場合はさらに遅くなり、繰り返し数が少ない場合は違いはおそらく問題になりません。正確なしきい値は、おそらくハードウェアとOSの両方に依存します。たとえば、私のOSX 10.11.5マシンでは、この回答はすでに500で高速です。試してみてくださいtime yes = | head -500 | paste -s -d '\0' -; time yes | mapfile -n 500 -C 'printf = \#' -c 1。さらに重要なことは、しかし:あなたが使用している場合はprintf、とにかく、あなたにも受け入れ答えから両方のシンプルでより効率的なアプローチで行くことがあります。printf '%.s=' $(seq 500)
mklement0

13

簡単な方法はありません。ループの使用printfと置換を避けます。

str=$(printf "%40s")
echo ${str// /rep}
# echoes "rep" 40 times.

2
いいですが、リピートカウントが少ない場合にしか実行できません。ここのように呼び出すことができる関数のラッパーですrepl = 100(出力は、後続しないたとえば、\n:)repl() { local ts=$(printf "%${2}s"); printf %s "${ts// /$1}"; }
mklement0

1
@ mklement0両方のソリューションの関数バージョンを提供できることを嬉しく思います。両方に+1します。
Camilo Martin

8

あなたはの異なる実装間POSIX準拠性と一貫性が必要な場合echoprintf、および/またはちょうど以外のシェルbash

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

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

... perl -E 'say "=" x 100'ほぼどこでも同じ出力を生成します。


1
問題はseq、POSIXユーティリティではないことです(BSDおよびLinuxシステムには実装されていますが)- while@ Xennex81の回答のように、代わりにループを使用してPOSIXシェル演算を実行できます(ではなく、printf "="正しく提案されているecho -n)。
mklement0 2015

1
おっと、あなたはまったく正しい。そのような基準は意味をなさないので、そのようなことは時々私を通り過ぎます。calPOSIXです。seqではありません。とにかく、whileループで答えを書き直すのではなく(言ったように、それは他の答えに既にあります)、RYO関数を追加します。その方法でより教育的です;-)。
ジェフニクソン

8

問題はそれをどのように行うかについてでしたecho

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

これはまったく同じことを行いますperl -E 'say "=" x 100'が、それechoだけです。


今では珍しいですが、余分なスペースとバックスペースを入れないでください。または、次のコマンドを使用してクリーンアップしてください:echo -e $ _ {1..100} '\ b =' | col
アンソニー

1
悪いアイデア。、、または100の他の変数のいずれかに値がある場合$_1、これは失敗し$_2ます。
John Kugelman

@JohnKugelman echo $(set-; eval echo -e \ $ {{1..100}} '\\ b =')
mug896

これはです。大好きです:D
dimo414

6

なしeval、サブシェルなし、外部ツールなし、ブレース展開なしの純粋なBash方法(つまり、変数内で繰り返す数を指定できます):

n(負ではない)数に展開する変数と変数が与えられた場合pattern、例えば、

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

あなたはこれで関数を作ることができます:

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

このセットで:

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

この小さなトリックのために、私たちは次のものでprintfかなり使用しています:

  • -v varname:標準出力に出力する代わりにprintf、フォーマットされた文字列の内容を変数に入れますvarname
  • '%* s':printf引数を使用して、対応する数のスペースを出力します。たとえば、printf '%*s' 4242スペースを印刷します。
  • 最後に、必要な数のスペースが変数にある場合、パラメーター展開を使用して、すべてのスペースをパターンに置き換えます。${var// /$pattern}var、すべてのスペースをの展開に置き換えて、の展開に展開し$patternます。

間接展開を使用tmpして、repeat関数内の変数を取り除くこともできます。

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

変数名を渡すための興味深いバリエーションです。このソリューションは、繰り返し数が最大で約1,000の場合は問題ありません(したがって、私が推測した場合、ほとんどの実際のアプリケーションでは問題ありません)。コメント)。
mklement0

それはそのようでbashパラメータ展開(の文脈でのグローバルな文字列置換操作${var//old/new}耐え難いほど遅いのbashで:特に遅い)3.2.57、およびbashで遅く4.3.30少なくとも3.2 GHzのインテルCore i5のマシン上で私のOSX 10.10.3システム上で、:でカウントが1,000、物事が遅い(3.2.57)/速い(4.3.30):0.1 / 0.004秒。カウントを10,000に増やすと、著しく異なる数値が生成されます。bashで repeat 10000 = var約80秒(!)かかり、bash 3.2.57で約0.3秒かかります4.3.30(onよりはるかに高速ですが3.2.57、それでも低速です)。
mklement0

6
#!/usr/bin/awk -f
BEGIN {
  OFS = "="
  NF = 100
  print
}

または

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


3
よくできました。これはPOSIXに準拠しており、繰り返し回数が多い場合でもかなり高速ですが、複数文字の文字列もサポートしています。シェルのバージョンは次のとおりawk 'BEGIN { while (c++ < 100) printf "=" }'です。(呼出しとしてパラメータシェル関数にラップrepeat 100 =例えば、) repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { txt=substr(txt, 2); while (i++ < count) printf txt }'; }。(BSDのバグを回避するには.、ダミーのプレフィックスcharと補完的なsubstr呼び出しが必要ですawk=
。BSD

1
NF = 100解決策は、(100を取得するためにかかわらず、非常に賢いです=、あなたが使用する必要がありますNF = 101)。注意点は、BSDをクラッシュさせることですawk(ただし、を使用すると非常に高速にgawkなり、さらに高速になりますmawk)。POSIXでは、ブロック内のフィールドへの割り当てNFや使用については説明されていませんBEGIN。BSD awkでも同様に微調整することで機能させることができますawk 'BEGIN { OFS = "="; $101=""; print }'(ただし、奇妙なことに、BSD awkではループソリューションよりも高速ではありません)。パラメータ化されたシェルの解決策として:repeat() { awk -v count="$1" -v txt=".$2" 'BEGIN { OFS=substr(txt, 2); $(count+1)=""; print }'; }
mklement0

ユーザーへの注意-NF = 100トリックは、古いawkでセグメント障害を引き起こします。これoriginal-awkは、BSDのawkに似た古いawkのLinuxでの名前であり、これを試すとクラッシュすることが報告されています。通常、クラッシュは悪用可能なバグを見つけるための最初のステップです。この答えは、安全でないコードを非常に促進しています。

2
ユーザーへの注意- original-awk非標準であり、推奨されません
Steven Penny

最初のコードスニペットの代わりにawk NF=100 OFS='=' <<< ""bashおよびを使用gawk)を使用できます
oliv

4

質問の元々の目的は、シェルの組み込みコマンドだけでこれを行うことだったと思います。だからfor、ループおよびprintfsは、正当であろうがrepperlとにもjot以下ではないだろう。それでも、次のコマンド

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

たとえば、ウィンドウ全体の行を出力します \/\/\/\/\/\/\/\/\/\/\/\/


2
よくできました。これは、繰り返し数が多い場合でもうまく機能します(複数文字の文字列もサポートします)。アプローチをわかりやすく説明するために、OPのコマンドと同等のものを次に示しますjot -s '' -b '=' 100。OSXを含むBSD風のプラットフォームにはが付属しているのjotに対し、Linuxディストリビューションには付属していないことに注意してください
mklement0

1
おかげで、私は-s ''の使用がさらに良いと思います。スクリプトを変更しました。
Stefan Ludwig

最近のDebianベースのシステムでapt install athena-jotは、を提供しますjot
agc

4

他の人が言ったように、bashでは中括弧の展開パラメータの展開の前に行われるため、範囲にはリテラルのみを含めることができます。また、クリーンなソリューションを提供しますが、それぞれに同じシェルを使用している場合でも、システム間での完全な移植性はありません。(ただし、FreeBSD 9.3以降などでますます利用可能になります。)およびその他の間接的な形式は常に機能しますが、やや洗練されていません。{m,n}seqjotseqeval

幸い、bash はCスタイルのforループをサポートしています(算術式のみ)。したがって、ここに簡潔な「純粋なbash」方法があります。

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

これは、最初の引数として繰り返しの数を取り、2番目の引数として繰り返される文字列(問題の説明のように、単一の文字である場合があります)を取ります。repecho 7 b出力bbbbbbb(改行で終了)。

デニスウィリアムソンは、基本的にこの解決策を4年前にシェルスクリプトで繰り返し文字列作成することに対する彼の優れた答えで与えました。私の関数本体はそこでのコードと少し異なります:

  • ここでの焦点は単一の文字を繰り返すことにあり、シェルはbashであるため、のecho代わりに使用することはおそらく安全ですprintf。そして、私はこの質問の問題の説明を、を使用して印刷することを好むことを表現していると読みましたecho。上記の関数定義は、bashおよびksh93で機能します。printfより移植性がありますが(通常、この種のものに使用する必要があります)、echoの構文は間違いなく読みやすくなっています。

    一部のシェルのecho組み込みコマンドは-、それ自体をオプションとして解釈します。ただし、-入力にstdinを使用するというの通常の意味は、には意味がありませんechozshはこれを行います。そして、それは標準ではないのでそれをecho認識しないものは確かに存在します。(多くのBourneスタイルのシェルは、Cスタイルのforループをまったく受け入れないため、その動作を考慮する必要はありません。)-necho

  • ここでのタスクはシーケンスを印刷することです。そこで、それを変数に割り当てることでした。

もしは$n繰り返し、所望の数であり、あなたはそれを再利用する必要はありません、あなたも短く何かをしたいです:

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

n変数でなければなりません-この方法は位置パラメータでは機能しません。$s繰り返されるテキストです。


2
ループバージョンを実行しないでください。printf "%100s" | tr ' ' '='最適です。
ocodo 2014年

偶然にも機能する機能を機能としてパッケージ化するための優れた背景情報と称賛zsh。echo-in-a-loopアプローチは、繰り返し回数が少ない場合にうまく機能しますが、@ Slomojoのコメントで証明されているように、繰り返し回数が多い場合は、ユーティリティに基づくPOSIX準拠の代替手段があります
mklement0

エコーに影響を与えることなく、あなたの短いループジャムnの値の周りに括弧を追加:(while ((n--)); do echo -n "$s"; done; echo)

echoの代わりにprintfを使用してください!移植性が大幅に向上します(echo -nは一部のシステムでのみ機能します)。参照unix.stackexchange.com/questions/65803/...(ステファンChazelasの素晴らしい答えの一つ)
オリヴィエ・デュラック

@OlivierDulacここでの質問はbashについてです。実行しているオペレーティングシステムに関係なく、bash を使用している場合、bashにはechoをサポートするビルトインがあります-n。あなたの言っている精神は完全に正しいです。printfほとんどの場合echo、少なくとも非インタラクティブな使用では、が優先されます。しかしecho、質問をし、それが機能することを知るのに十分な情報を提供してくれた質問に回答するのは、決して不適切でも誤解を招くものでもないと思います。((n--))(なしの$)のサポート自体は、POSIXでは保証されないことにも注意してください。
Eliah Kagan

4

Pythonはユビキタスであり、どこでも同じように動作します。

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

文字とカウントは別々のパラメーターとして渡されます。


これはここでの意図だったと思いますpython -c "import sys; print(sys.argv[1] * int(sys.argv[2]))" "=" 100
gazhay

@loevborgは少し遠くまで行きませんか?
Sapphire_Brick


3

任意の文字列をn回繰り返す別の方法:

長所:

  • POSIXシェルで動作します。
  • 出力は変数に割り当てることができます。
  • 任意の文字列を繰り返します。
  • 非常に大きな繰り返しでも非常に高速です。

短所:

  • Gnu Core Utilsのyesコマンドが必要です。
#!/usr/bin/sh
to_repeat='='
repeat_count=80
yes "$to_repeat" | tr -d '\n' | head -c "$repeat_count"

ANSI端末とUS-ASCII文字を繰り返します。ANSI CSIエスケープシーケンスを使用できます。これは、文字を繰り返す最も速い方法です。

#!/usr/bin/env bash

char='='
repeat_count=80
printf '%c\e[%db' "$char" "$repeat_count"

または静的に:

80回の行を印刷します=

printf '=\e[80b\n'

制限:

  • すべての端末がrepeat_charANSI CSIシーケンスを理解するわけではありません。
  • US-ASCIIまたはシングルバイトISO文字のみを繰り返すことができます。
  • 最後の列で停止を繰り返すので、端子の幅に関係なく、大きな値を使用して行全体を埋めることができます。
  • リピートは表示のみです。出力をシェル変数にキャプチャrepeat_charしても、ANSI CSIシーケンスは繰り返される文字に展開されません。

1
細かい注意-端末が折り返しモードの場合、REP(CSI b)は通常通り折り返す必要があります。
1

3

これは、Linuxの画面全体に文字の行を印刷するために使用するものです(ターミナル/画面の幅に基づく)。

画面全体に「=」を印刷します。

printf '=%.0s' $(seq 1 $(tput cols))

説明:

指定されたシーケンスと同じ回数だけ等号を出力します。

printf '=%.0s' #sequence

コマンドの出力を使用します(これはコマンド置換と呼ばれるbash機能です):

$(example_command)

シーケンスを示します。例として1〜20を使用しました。最後のコマンドでは、20の代わりにtputコマンドが使用されています。

seq 1 20

端末で現在使用されている列の数を指定します。

tput cols


2
repeat() {
    # $1=number of patterns to repeat
    # $2=pattern
    printf -v "TEMP" '%*s' "$1"
    echo ${TEMP// /$2}
}


2

提案されたPythonソリューションのよりエレガントな代替案は次のとおりです。

python -c 'print "="*(1000)'

1

文字をn回繰り返す場合は、たとえば、実行できる文字列の長さに応じてVARIABLE回数を繰り返します。

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

表示されます:

vari equals.............................: AB  
Up to 10 positions I must fill with.....: 8 equal signs  
AB========  

lengthで動作しませんexpr、あなたはおそらく意味しましたn=$(expr 10 - ${#vari}); ただし、Bashの算術展開を使用する方が簡単で効率的ですn=$(( 10 - ${#vari} ))。また、あなたの答えの中核にあるのは、OPがBashの代替を探しているまさにPerlのアプローチです 。
mklement0 2015

1

これは、Eliah Kaganが支持していたものの長いバージョンです。

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

もちろん、printfを使用することもできますが、私の好みではありません。

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

このバージョンはDash互換です:

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

最初の番号はiです。


bashでは正のn:でwhile (( i-- )); do echo -n " "; done機能します。


1

エコーでこれをどのように行うことができますか?

これが後に続くecho場合、これを行うことができます:echosed

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

実際には、それechoは必要ありません。


1

私の答えはもう少し複雑で、おそらく完璧ではありませんが、大きな数を出力したい人にとっては、3秒で約1000万回実行できました。

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}
}

1

最も簡単なのは、このワンライナーをbashで使用することです。

seq 10 | xargs -n 1 | xargs -I {} echo -n  ===\>;echo


1

別のオプションは、GNU seqを使用して、それが生成するすべての番号と改行を削除することです。

seq -f'#%.0f' 100 | tr -d '\n0123456789'

このコマンドは、#文字を100回印刷します。


1

ほとんどの既存のソリューションはすべて{1..10}、シェルの構文サポートに依存しています。これはbash-およびzsh-固有のものでありtcsh、OpenBSD kshやほとんどの非bash では機能しません。sh

以下は、OS Xおよびすべての* BSDシステムで動作します。実際、さまざまなタイプの装飾スペースのマトリックス全体を生成するために使用できます。

$ printf '=%.0s' `jot 64` | fold -16
================
================
================
================$ 

悲しいことに、末尾の改行はありません。printf '\n'折りたたみ後にエキストラで修正できます:

$ printf "=%.0s" `jot 64` | fold -16 ; printf "\n"
================
================
================
================
$ 

参照:


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.