:>filename.txt
例えば:
root@box$ dd if=/dev/zero of=file.txt count=1024 bs=1024
1024+0 records in
1024+0 records out
1048576 bytes (1.0 MB, 1.0 MiB) copied, 0.00536175 s, 196 MB/s
root@box$ ll
total 1024
-rw-r--r-- 1 root root 1048576 Nov 15 14:40 file.txt
root@box$ :>file.txt
root@box$ ll
total 0
-rw-r--r-- 1 root root 0 Nov 15 14:40 file.txt
これはrm
とは異なりますか?ファイルをゼロ化したり削除したりする他の同様の方法よりも速く動作しますか?
ご存じのとおり、これはファイルの内容を空にするだけです(ファイルを切り捨てます)。 rm
は実際にファイルを完全に削除するため、rm
とは異なります。さらに、:>file.txt
は実際にはファイルが存在しない場合はcreateになります。
:
は、成功して終了し、出力を生成しない「何もしないコマンド」であるため、ファイルを空にする短い方法です。ほとんどのシェルでは、>file.txt
を実行するだけで同じ結果を得ることができます。また、echo
が外部コマンドになる可能性があるため、echo >file.txt
などの他のメソッドよりもわずかに高速になる可能性があります。
さらに、echo >file.txt
はfile.txt
に空白行を入れ、:>file.txt
はファイルに内容をまったく含まないようにします。
はい、rm
とは異なります。
rm
はファイルを削除します。
:>filename.txt
はファイルを空にしてそのままにしますが、サイズは0バイトです。
シェルが>filename.txt
を呼び出すと、一部の出力がファイル「filename.txt」にリダイレクトされ、完全に置き換えられます。したがって、シェルは出力を書き込む前に、指定されたファイルのすべてのコンテンツをクリアする必要があります。
リダイレクトされる出力は、実行されたコマンドの出力です。お気に入り:
$ echo Hello >filename.txt
Filename.txtという名前のファイルに、文字列Hello
を正確に(かつ唯一)含めるようにします。
実行中:
$ echo "New Value" >filename.txt
$ cat filename.txt
New Value
ファイル内のすべてを消去し、New Value
を書き込みます。
コマンドtrue
のようにコマンドに出力がない場合、ファイルは空のままになります(切り捨てられます)。
$ true >filename.txt
$ cat filename.txt
シェルの組み込みでもあるコマンドは:
(二重ドット(コロン)のみ)であり、出力はありません。組み込みであるため、true
などの外部コマンド(出力もありません)よりも高速になります。だから、どちらか:
$ : > filename.txt
$ : >filename.txt
$ :>filename.txt
filename.txt
という名前のファイルからすべてのコンテンツを削除するか、存在しない場合は空のファイルとして作成します。
これはrmとは異なりますか?
これはrmとは異なります。rmはファイルをls
から消去し、ファイルに0バイトが含まれないようにするためです。
ファイルをゼロにする、またはファイルを削除する他の同様の方法よりも速くまたは遅く動作しますか?
この場合も、ファイルは削除されていません(ls
で指定されたリストから消えます)。空のファイル(0バイトを含む)に変換されます。
ファイルを空にするために外部コマンドを呼び出すよりも高速でなければなりません。実行可能ファイルとexec
をロードするために子シェルを作成する必要があるため、外部コマンドは組み込みの:
よりも遅くなります。
同様の解決策は次のとおりです(一部は$?
を1に設定):
[ ] > filename.txt
builtin > filename.txt
command > filename.txt
printf '' > filename.txt
re:パフォーマンス:これはシェルで可能な限り効率的です。カーネルに既存のファイルを切り捨てるように要求することは、ファイルのリンクを解除して同じ名前で新しいiノードを再作成するよりも効率的です。あなたがファイルを削除したい場合を除き、その場合はrm
またはunlink
です。
_:
_はシェル組み込みなので、fork/execを回避できます。同様に、通常の最新のシェルにおける同等のtrue
も同様です。
_>foo
_または_true > foo
_は、シェルを取得してファイルを切り捨てます。open(path, O_WRONLY|O_TRUNC|O_CREAT, 0666)
システムコール。
または実際にLinuxのDASHで、_strace sh
_出力から:openat(AT_FDCWD, "foo", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
これは同等です。
次に、そのFDをclose()
する必要があります。実際、DASHは、_:>
_を使用する場合の特別なケースではなく、次のフープをジャンプします。
_openat(AT_FDCWD, "foo", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
fcntl(1, F_GETFD) = 0
fcntl(1, F_DUPFD, 10) = 10 # save original stdout
fcntl(1, F_GETFD) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 1) = 1 # redirect stdout to foo
close(3) = 0 # then close 3
dup2(10, 1) = 1 # then restore original stdout
fcntl(10, F_GETFD) = 0x1 (flags FD_CLOEXEC)
close(10) = 0
_
DASHで_> foo
_を使用すると、同じシステムコールシーケンスが発生し、実際にはfd1をリダイレクトしてから復元します。 bash
や他のシェルはチェックしませんでした。
しかし、それは(うまくいけば)単一の truncate("foo", 0)
システムコール を実行する_truncate -s 0 foo
_を実行する新しいプロセスを作成するよりもはるかに安価です。 open
+ close
よりも。
Cなどの言語(またはシステムコールバインディングのあるもの)からは、直接開かないtruncate
syscallを使用して、開いたくないファイルの切り捨てを最も効率的に行うことができます。
Dashでは、_3>foo
_がこのシステムコールのシーケンスにつながります:
_openat(AT_FDCWD, "foo", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
close(3) = 0
_
新しいfdを開くと、すでにfd 3になっており、重複を回避できます。これはダッシュで最も効率的な方法であり、おそらく_>foo
_と比較して数マイクロ秒を節約します。それが問題なら、シェルスクリプトはあなたの仕事にとって間違った言語です!!しかし、あなたは尋ねました。
Spectre + Meltdownの緩和機能が有効になっていると、_-ENOSYS
_の悪いシステムコールでも、最新のIntel x86-64では少なくとも数千クロックサイクル(マイクロ秒)かかります。 syscall
命令だけで、ユーザー->カーネル->ユーザーの往復の数百から。もちろん、パスの検索などにはかなりの時間がかかり、ファイルシステムのコードにも時間がかかります。また、カーネルモードに戻って戻ると、キャッシュが削除され、ユーザー空間の実行が遅くなります。
メタデータがディスクに書き戻した後の実際のI/Oコストは、ファイルシステムによって異なります。 FS XFSまたは最新のext4のように、ファイル全体に1つまたはいくつかの大きなエクステントのみを使用すると、O(1)時間で大量のスペースを簡単に解放できますまたはO(n)ここで、n
は、バイト単位のサイズではなく、エクステント(フラグメンテーション)の数です。
FSによっては、エクステント情報が間接ブロックではなくiノードに直接格納されている場合、フリーリストに追加する必要のある項目が1つ少なくなります。
I/Oコストはファイルのリンク解除に似ていますが、iノードを解放したり、ディレクトリエントリを変更したりする必要はありません。切り捨ての際にiノードのmtimeとctimeを更新する必要がありますが、サイズが変更された場合はとにかくそれを書かなければなりませんでした。