web-dev-qa-db-ja.com

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

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

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

たとえば、H5000回繰り返す必要があります。ここで%.0sはどういう意味ですか?

12
star

このコマンドは、シェルが5000個の引数を生成し、それらをprintfに渡して無視することに依存しています。それはかなり速く見えるかもしれません-そしていくつかのものに関連しています-シェルはまだそれらの文字列のすべてを引数として生成する必要があります(そしてそれらを区切る)などオン。

シェルが最初に5000に反復するまで生成されたHを印刷できないという事実に加えて、このコマンドは、数値文字列引数をprintfplusHs。単にあなたができるのと同じように:

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倍(ここでは科学的なものは何もない-逸話のみ)であり、ブレース拡張バージョンの平均処理時間は合計で.02秒強ですが、 trバージョンは平均で合計で約0.012秒で受信され、trバージョンは毎回それを上回っていました。驚いたとは言えません-{brace expansion}は便利な対話型シェルの省略機能ですが、通常、あらゆる種類のスクリプトが関係している場合は、かなり無駄な作業です。一般的な形式:

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

...考えてみると、本当にtwofor loops-最初のループは内部であり、シェルはループする必要があることを意味しますそれらをすべて保存して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]引数を使用すると、さらに効果的です。上記に,unblock cbs=1を追加し、そこからpaste/dev/nullを使用して行ごとに任意の文字列を注入できる場合、nullではなく64kの改行を取得できます。ただし、その場合、あなたが利用できるなら、あなたも使うでしょう:

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およびnoerror変換が指定されている場合に読み取りエラーが発生した場合に、少なくとも1つの完全なnullブロックを書き出すというsyncの仕様の動作を利用します(そして-count=なし-おそらくあなたが望むよりも長く続くでしょう)そして、意図的にddに書き込み専用ファイル記述子をリダイレクトしますstdin。

20
mikeserv

%.0sは、引数を文字列として変換し、精度をゼロにします。 man 3 printfによれば、そのような場合の精度値は

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

したがって、精度がゼロの場合、文字列引数はまったく出力されません。ただし、_man bashHセクションによれば、printf(フォーマット指定子の一部)は引数がある回数だけ出力されます。

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. 
8
steeldriver

この場合、 %.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

HHHHH-つまり、引数の数、この場合は5を出力します。

上記の最初の例の省略記号は文字どおりに挿入されていないことに注意してください。省略記号は、シーケンスまたは範囲を示すためにあります。

7
KM.