Some translations for Bourne-like shells:
system call |
shell interface |
shells |
comment |
open("file", O_RDONLY) |
exec 3< file |
all |
here open on fd 3¹ |
open("file", O_RDONLY) |
exec {fd}< file |
zsh ksh93 bash |
fd returned in $fd ² |
open("file", O_WRONLY|O_TRUNC|O_CREAT) |
exec 3> file |
all |
³ |
open("file", O_WRONLY|O_APPEND|O_CREAT) |
exec 3>> file |
all |
4 |
open("file", O_RDWR|O_CREAT) |
exec 3<> file |
all |
|
open("file", ...) |
sysopen ... |
zsh |
in zsh/system module |
dup2(4, 5) |
exec 5>&4 |
all |
same as exec 5<&4 |
dup(4) |
exec {fd}>&4 |
zsh ksh93 bash |
fd returned in $fd |
write(4, data, length) |
print -rnu4 -- "$data" |
ksh zsh |
bash has an example loadable print builtin as well5 |
write(...) |
syswrite ... |
zsh |
in zsh/system module |
read(4, var...) |
sysread -i 4 ... var |
zsh |
in zsh/system module |
read(4, var...) |
IFS= read -ru4 ... var |
ksh zsh bash |
reads a line. Beware of limitations.5 |
close(4) |
exec 4>&- |
all |
or exec 4<&- |
lseek(4, 123, SEEK_SET) |
exec 4>#((123)) |
ksh93 |
|
lseek(4, 123, SEEK_CUR) |
exec 4>#((CUR + 123)) |
ksh93 |
|
lseek(4, 123, SEEK_END) |
exec 4>#((EOF + 123)) |
ksh93 |
|
lseek(...) |
sysseek ... |
zsh |
in zsh/system module6 |
pipe() |
exec 4>>|5 |
yash |
fd 4 the writing end, 5 the reading end |
fstat(4, var) |
zstat -f 4 -H var |
zsh |
in zsh/stat module |
select(...) |
zselect ... |
zsh |
in zsh/zselect module |
zsh also has some zsh/net/socket
, zsh/net/tcp
, zsh/zpty
modules to create an manipulate other sorts of fds.
Using exec
changes the file descriptors of the shell process. Most of the times however, you only want to change file descriptors of some other command, like ls -l dir/ > file
(short for ls -l dir/ 1> file
) to open file
on fd 1 (stdout) for ls
only, or a section of code:
{
echo Some Header
ls -l dir/
} 1> file 2>> error.log
foo | bar
does a pipe()
7 followed by some dup2()
s or equivalent and close()
s in concurrent processes so fd 1 of the process that will run foo
goes to the writing end of the pipe and fd 0 or bar
goes to the reading end. Other shell constructs that use or may use pipes include command substitution (var=$(cmd)
, var=`cmd`
), Korn-like shell process substitution (foo <(bar) >(baz)
), co-processes, yash's process redirection.
¹ in ksh93 and bash, if file
is /dev/tcp|udp/host/port
, it doesn't do a real open but creates a network socket instead.
² Same can be done for the other >
, >>
, <>
, >
operators
³ If the noclobber
option is on, a O_EXCL
is added if the destination is a regular file, which can be bypassed with the >|
or >!
operators depending on the shell
4 In the Bourne shell, there was no O_APPEND
, but the shell seeked to the end after open
5 In other shells, you can always do printf %s "$var" >&4
. That doesn't write to fd 4, but writes to fd 1, after a temporary dup2(4, 1)
which in effect achieves the same goal. Same for IFS= read -r line <&4
.
6 See also the systell(fd)
math function to return a fd's position which is another interface to lseek()
7 Except with ksh93 on some systems that uses socketpair()
s instead of pipes. Same goes for some other constructs that use pipes.