re: performance: this is about as efficient as possible in the shell; just asking the kernel to truncate an existing file should be more efficient than unlinking the file and re-creating a new inode with the same name. Unless you want the file deleted, in which case rm
or unlink
it.
:
is a shell built-in so it avoids fork/exec. So is the equivalent true
in normal modern shells.
>foo
or true > foo
gets the shell to truncate the file by making an
open(path, O_WRONLY|O_TRUNC|O_CREAT, 0666)
system call.
Or in practice with DASH on Linux, from the strace sh
output:
openat(AT_FDCWD, "foo", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
which is equivalent.
Then it has to close()
that FD again. In fact, DASH doesn't special case this when you use :>
, it jumps through the following hoops:
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
Using > foo
in DASH leads to the same sequence of system calls, actually redirecting fd1 and then restoring it. I didn't check bash
or other shells.
But that's still significantly cheaper than creating a new process to run truncate -s 0 foo
which would (hopefully) make a single truncate("foo", 0)
system call which would presumably be even more efficient than an open
+ close
.
From a language like C (or anything with system call bindings), truncating a file you don't want open can be done most efficiently with a direct truncate
syscall.
In Dash, 3>foo
leads to this sequence of system calls:
openat(AT_FDCWD, "foo", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
close(3) = 0
Opening a new fd results in it already being fd 3, avoiding any duping. This is the most efficient way in dash, probably saving several microseconds vs. >foo
. If that matters, shell scripting is the wrong language for your task!! But you did ask.
With Spectre+Meltdown mitigation enabled, even a -ENOSYS
bad system call takes at least thousands of clock cycles, so microseconds, on modern Intel x86-64. Up from a couple hundred for the user->kernel->user round trip with just a syscall
instruction. Of course path lookups and so on take significant time and so does filesystem code. And going to kernel mode and back often evicts some cache making user-space run slower upon return.
Actual I/O cost once the metadata does write-back to disk depends on the filesystem. An FS like XFS or modern ext4 using only 1 or a few large extents for the whole file can easily free huge amounts of space in O(1) time. Or O(n) where n
is the number of extents (fragmentation) not the size in bytes.
Depending on the FS, if the extent info was stored right in the inode, instead of an indirect block, that's one less thing to add to the free list.
The I/O cost is similar to unlinking the file, but you don't have to free the inode or modify the directory entry. You do still have to update the mtime and ctime in the inode upon truncation, but you had to write it anyway if the size changed.
:
akatrue
command): What does >filename.txt do in shell script. Surely this is a duplicate but a search forcode:":>"
didn't find anything, not even this question.