:>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
?ファイルを空にしたり削除したりする他の同様の方法よりも速く実行されますか、それとも遅く実行されますか?
答え1
rm
ご存知のように、これは単にファイルの内容を空にするだけです(ファイルを切り捨てます)。これは実際にrm
ファイルを完全に削除するのとは異なります。しかも:>file.txt
実際には作るまだ存在しない場合は、このファイルです。
:
正常に終了して出力が生成されない「何もしないコマンド」なので、ファイルを空にする簡単な方法にすぎません。ほとんどのシェルでは、>file.txt
単にこれを行うと同じ結果が得られます。また、外部コマンドなどの他の方法よりも少し速い場合がecho >file.txt
ありますecho
。
echo >file.txt
また、内部に空白行が配置され、ファイルに内容がありません。file.txt
:>file.txt
答え2
はい、違いますrm
。
rm
ファイルが削除されます。
:>filename.txt
ファイルはまだ存在しますが、サイズが0バイトになるようにファイルを空にします。
答え3
シェル呼び出しは、>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
ファイルにゼロバイトを含めるのではなく、ファイルを消去するからです。
ファイルを空にしたり削除したりする他の同様の方法よりも速く実行されますか、それとも遅く実行されますか?
繰り返しますが、ファイルは削除されず(与えられたリストから消えますls
)、空のファイル(0バイトを含む)に変換されます。
ファイルを空にするために外部コマンドを呼び出すよりも高速です。実行可能ファイルとコマンドをロードするにはサブシェルを作成する必要があるため、exec
外部コマンドは組み込みコマンドよりも遅くなります:
。
同様の解決策は次のとおりです(一部は$?
1に設定されています)。
[ ] > filename.txt
builtin > filename.txt
command > filename.txt
printf '' > filename.txt
答え4
A:パフォーマンス:これはシェルでできるだけ効率的です。単にカーネルに既存のファイルを切り捨てるように要求するほうが、ファイルのリンクを解除し、同じ名前で新しい inode を再生成するよりも効率的です。そうでなければ考えるファイルが削除されました。この場合、rm
まだunlink
残っています。
:
内蔵シェルなのでフォーク/実行は不要です。true
一般的な現代の殻の場合も同様です。
>foo
または、システムコールを介してシェルがtrue > foo
ファイルを切り捨てるようにします。
open(path, O_WRONLY|O_TRUNC|O_CREAT, 0666)
strace sh
または、出力を判断してLinuxでDASHを使用して練習してみてください。
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 -s 0 foo
(希望的に)単一のプロセスを作成するよりもはるかに安いです。truncate("foo", 0)
システムコールopen
これは+より効率的ですclose
。
C(またはシステムコールバインディングを持つすべての言語)では、開いたくないファイルは直接システムコールを介して最も効率的に切り捨てられますtruncate
。
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で誤ったシステムコールにも最小数千のクロックサイクル(マイクロ秒など)がかかります。 1つのコマンドで、syscall
数百のユーザー - >カーネル - >ユーザーの往復を達成できます。もちろん、パスルックアップなどはファイルシステムコードと同様に時間がかかります。カーネルモードに入って戻ってくると、通常は一部のキャッシュが削除され、戻り時にユーザースペースの実行が遅くなります。
メタデータをディスクに書き戻した後の実際のI / Oコストは、ファイルシステムによって異なります。 XFS や最新の ext4 などの FS は、ファイル全体で 1 つまたは複数の大きな範囲のみを使用して、O(1) 時間に多くのスペースを簡単に確保できます。またはO(n)、ここではn
バイトサイズではなく範囲(断片)の数です。
FSによると、エクステント情報が間接ブロックではなくiノードに直接保存されている場合は、空きリストに追加することが1つ減ります。
I / Oコストはファイルのリンク解除と似ていますが、inodeを解放したりディレクトリエントリを変更したりする必要はありません。切り取るときにinodeでmtimeとctimeを更新する必要がありますが、サイズが変更された場合はとにかくそれを作成する必要があります。