I try to understand this concept for years, with at this time some incomplete knowledge of this.

Is anyone here can make a kind of a tutorial or good explanations with examples on how to

  • open a new fd
  • read from fd
  • use fd
  • close fd

I have read https://www.gnu.org/software/bash/manual/html_node/Redirections.html and man bash, but it's not clear, there's no concrete and understandable examples at all.

If someone can explain it well, will be a valuable answer, not only for me. The syntax is most of the time a bit obfuscated.
Sometimes that require exec, sometimes not.


1 Answer 1


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.

  • I wonder if it is asking too much, but, that should be very great to have another column in your table that describe the behavior of the row (open, copy, close...) to people not knowing what are the syscalls in C. Feb 20, 2023 at 18:01

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .