printfコマンドを使用して文字をN回書き込む


12

Linuxで文字を繰り返す次のコマンドを見つけました。

printf 'H%.0s' {1..5000} > H.txt

たとえば、時間Hを繰り返したい5000。何がない%.0sここに意味ですか?


tcshzshrepeat 5000 printf H理解しやすいです。有するperlprint "H" x 5000(ことに留意されたい{1..5000}zshのオペレータが触発さperl1..5000一方と後は、ksh93およびBashでコピー)
ステファンChazelas

はい、動作しますが、より大きなリピートのために多くのリソースを使用します。ステファンシャゼラス
スカペレンの

1
私はこのコマンドを実行しますyes H|head -5000|tr -d '\012'
スカペレン

dd if=/dev/zero bs=5000 count=1 | tr '\0' H
小次郎

@Skaepren:yes H| head -n 2500| tr \\n H
mikeserv

回答:


20

そのコマンドは、5000個の引数を生成し、それらに引数を渡してからprintf無視するシェルに依存します。それは非常に速いように思えるかもしれませんが、いくつかの事柄に関連していますが、シェルはこれらの文字列をすべて引数として(およびそれらを区切って)生成する必要があります。

生成さHsは5000にシェル最初の反復処理するまで印刷することができないという事実のほかに、そのコマンドはまた、格納するために、数値文字列引数を区切るのにかかるすべてのことをメモリにコストprintf プラス Hsが。簡単にできること:

printf %05000s|tr \  H

...これは5000個のスペースの文字列を生成します-少なくとも、通常は1バイトにつき1バイトであり、区切られていないため、区切るのに費用はかかりません。いくつかのテストでは、わずか5000バイトtrでさえ、この場合でも必要なフォークとパイプのコストが価値があることを示しており、ほとんどの場合、数字が大きくなるとコストが高くなります。

走った...

time bash -c 'printf H%.0s {1..5000}' >/dev/null

...そして...

time bash -c 'printf %05000s|tr \  H' >/dev/null

それぞれ約5回(ここでは科学的ではありません-逸話のみ)、ブレース拡張バージョンは合計処理時間で平均0.02秒強でしたが、trバージョンは平均で約.012秒で入っており、trバージョンはそれに勝っています毎回。驚いたとは言えません- {brace expansion}便利なインタラクティブシェルの略記機能ですが、通常、あらゆる種類のスクリプトが関係する場所で行うのはかなり無駄です。一般的な形式:

for i in {[num]..[num]}; do ...

...あなたがそれについて考えるとき、実際には2つの forループです-最初のものは内部であり、シェルはそれらをすべて保存してforループのために再度反復する前にそれらのイテレータを生成するために何らかの方法でループする必要があることを暗示しています。そのようなことは通常、次のように行う方が適切です。

iterator=$start
until [ "$((iterator+=interval))" -gt "$end" ]; do ...

...非常に少数の値のみを保存し、イテレート可能オブジェクトの生成中に繰り返しを行うのと同様に、それらを上書きするためです。

とにかく、前述のスペースパディングと同様に、printfもちろん次のように任意の桁数をゼロパッドすることもできます。

printf %05000d

私は引数なしで両方を行います。なぜならprintf、引数が見つからないときにの書式文字列で指定されたすべての引数に対してnull文字列が使用されているためです。これは数字引数の場合はゼロとして、文字列の場合は空の文字列として解釈されます。

これは、問題のコマンドと比較した場合のコインのもう一方の(そして、私の意見では-より効率的な)側面です-あなたがするとき、あなたは何かから何も得ることができませんprintf %.0、各引数の文字列を長く何もないところから何かを得ることができます。

次のddように使用できる、生成された大量のバイトに対してさらに高速です。

printf \\0| dd bs=64k conv=sync 

...そして、通常のファイルddseek=[num]引数を使用して、より大きな利点を得ることができます。あなたが追加する場合は、64kの改行ではなくヌルを得ることができます,unblock cbs=1行ごとに任意の文字列とそこに注入でき、上記とからにpasteして/dev/null-しかし、その場合には、それが使用可能かどうかは、あなたにも使用することがあります:

yes 'output string forever'

ddとにかく、さらにいくつかの例を示します。

dd bs=5000 seek=1 if=/dev/null of=./H.txt

... サイズが5000バイトのH.txtという名前の現在のディレクトリにファイルを作成(または切り捨て)\0NULます。ddオフセットをまっすぐシークし、その背後のすべてをNULで埋めます。

<&1 dd bs=5000 conv=sync,noerror count=1 | tr \\0 H >./H.txt

...これは同じ名前とサイズのファイルを作成しますが、H文字で埋められます。それはを活用するdd際に、読み取りエラーの場合には少なくとも一つの完全なヌル・ブロックを書き出すのspec'd行動noerrorsync変換が指定されている( -なしとcount=おそらくあなたがしたいことよりも長い上に行くだろう- )リダイレクトを、意図的ddの標準入力の書き込み専用ファイル記述子。


8

%.0s以下のように引数を変換することを意味する文字列で、精度ゼロ。によればman 3 printf、そのような場合の精度値は

   [ ... ] the  maximum  number  of characters to be printed from a
   string for s and S conversions.

したがって、精度がゼロの場合、文字列引数はまったく出力されません。ただし、H(書式指定子の一部である)は、引数の数だけ印刷されます。なぜならprintfman bash

The format is reused as necessary to consume all  of  the  argu
ments.  If the format requires more arguments than are supplied,
the extra format specifications behave as if  a  zero  value  or
null  string,  as  appropriate,  had  been supplied. 

7

この場合、%.0s常に1つ前の文字のインスタンス(この場合はH)を印刷します。{1..5000}を使用すると、シェルはそれを展開し、次のようになります。

printf 'H%.0s' 1 2 3 4 ... 5000 > H.txt

つまり、printfコマンドには現在5000個の引数があり、各引数に対して1つのHを取得します。これらは連続的または数値である必要はありません。

printf 'H%.0s' a bc fg 12 34

print- HHHHHすなわち、引数の数、この場合は5。

上記の最初の例の楕円は文字通りに挿入されず、シーケンスまたは範囲を示すためにあります。

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