Is there a way to close all the open file descriptors, without having an explicit list of them beforehand?
-
4All of which file descriptors? Every process has some open.Warren Young– Warren Young2014-04-06 17:54:01 +00:00Commented Apr 6, 2014 at 17:54
-
What are "all the open file descriptors"? 0, 1, and 2? Or do you have many more? If so, where did they come from?U. Windl– U. Windl2019-02-06 15:00:25 +00:00Commented Feb 6, 2019 at 15:00
-
Aren't the file descriptors closed automatically when the script finishes?jarno– jarno2019-10-27 12:27:39 +00:00Commented Oct 27, 2019 at 12:27
-
This is useful to know for the situation when you fork a shell-script, and thus need to close all the parents sockets.Samuel Åslund– Samuel Åslund2021-04-07 08:13:43 +00:00Commented Apr 7, 2021 at 8:13
7 Answers
To answer literally, to close all open file descriptors for bash:
for fd in $(ls /proc/$$/fd); do
eval "exec $fd>&-"
done
However this really isn't a good idea since it will close the basic file descriptors the shell needs for input and output. If you do this, none of the programs you run will have their output displayed on the terminal (unless they write to the tty device directly). If fact in my tests closing stdin (exec 0>&-) just causes an interactive shell to exit.
What you may actually be looking to do is rather to close all file descriptors that are not part of the shell's basic operation. These are 0 for stdin, 1 for stdout and 2 for stderr. On top of this some shells also seem to have other file descriptors open by default. In bash, for example, you have 255 (also for terminal I/O) and in dash I have 10, which points to /dev/tty rather than the specific tty/pts device the terminal is using. To close everything apart from 0, 1, 2 and 255 in bash:
for fd in $(ls /proc/$$/fd); do
case "$fd" in
0|1|2|255)
;;
*)
eval "exec $fd>&-"
;;
esac
done
Note also that eval is required when redirecting the file descriptor contained in a variable, if not bash will expand the variable but consider it part of the command (in this case it would try to exec the command 0 or 1 or whichever file descriptor you are trying to close).
NOTE: Also using a glob instead of ls (eg /proc/$$/fd/*) seems to open an extra file descriptor for the glob, so ls seems the best solution here.
Update
For further information on the portability of /proc/$$/fd, please see Portability of file descriptor links. If /proc/$$/fd is unavailable, then a drop in replacement for the $(ls /proc/$$/fd), using lsof (if that is available) would be $(lsof -p $$ -Ff | grep f[0-9] | cut -c 2-).
-
/procis only available under Linux.chepner– chepner2014-04-07 19:30:41 +00:00Commented Apr 7, 2014 at 19:30 -
5@chepner you would be right if you said that
/proc/PID/fdis not very portable. But saying that/procis only available under Linux is not a correct statement.Graeme– Graeme2014-04-07 19:35:29 +00:00Commented Apr 7, 2014 at 19:35 -
Some websites use the
<&-form, is it any different/needed?Lorenzo Pistone– Lorenzo Pistone2014-04-07 21:56:17 +00:00Commented Apr 7, 2014 at 21:56 -
@LorenzoPistone, the direction makes no difference when closing a descriptor, so either form can be used.Graeme– Graeme2014-04-07 22:03:36 +00:00Commented Apr 7, 2014 at 22:03
-
for fd in $(ls /proc/$$/fd| grep -v [012]); do exec {fd}>&-; done# Filtering out std streamsTinmarino– Tinmarino2023-04-15 03:59:50 +00:00Commented Apr 15, 2023 at 3:59
In recent versions of Bash (4.1 and onward, year 2009 and later) you can specify the file descriptor to close using a shell variable:
for fd in $(ls /proc/$$/fd/); do
[ $fd -gt 2 ] && exec {fd}<&-
done
The feature had been in Korn shell already (since 1993?) but apparently took some time to make its way into Bash.
Simple way
eval exec {0..255}">&-"
exec {0..255}">&-" expands to the exec 0>&- 1>&- ... 255>&- literal string, according to Brace Expansion (see Brace Expansion chapter in man bash for more info), then eval actually evaluates the command
Clear all file descriptors except i/o/e of the current shell, but also excludes the ones provided as arguments
clear_fds() {
for fd in $(compgen -G "/proc/$BASHPID/fd/*"); do
fd=${fd/*\/}
if [[ ! " $* " =~ " ${fd} " ]]; then
case "$fd" in
0|1|2|255)
;;
*)
eval "exec $fd>&-"
;;
esac
fi
done
}
No. The kernel can close only one FD at a time, and bash does not have "group commands" for FDs.
for fd in $(ls -1 /proc/27343/fd); do echo exec $fd">&"-; done
Remove the echo and the " after testing.
If this is not for the shell itself but for a command to be run then you can use nohup.
-
2The file descriptor used by
>&-cannot be a parameter; the redirection is parsed prior to parameter expansion.chepner– chepner2014-04-07 19:35:33 +00:00Commented Apr 7, 2014 at 19:35 -
1@chepner nowadays
exec {fd}>&-works.jarno– jarno2019-10-27 20:42:09 +00:00Commented Oct 27, 2019 at 20:42
You can do it without subshell $(....) and without referring parent process,
furthermore, if you are using Bash 4.1+, you can do it without eval.
for path in /proc/self/fd/*; do
fd=${path##*/} # get name part
exec {fd}>&-
done
More resonable implementation should exclude the stdin,stdout,stderr, left them to the caller side.
for path in /proc/self/fd/*; do
fd=${path##*/} # get name part
((fd < 3)) || exec {fd}>&-
done
-
1This answer is the fastest in general case. In special case if you want to close specific descriptors, that you know beforehand, then this is faster:
eval exec {3..9}">&-"oreval exec {0,1,2,9,11,255}">&-"Alek– Alek2023-04-12 17:31:28 +00:00Commented Apr 12, 2023 at 17:31
Another way without "eval" at all, is to use the form:
$ exec {var_a}>> file.txt
$ echo $var_a
10
$ ls -l /proc/self/fd/10
l-wx------ 1 0 0 64 Dec 11 18:32 /proc/self/fd/10 -> /run/user/0/tmp/file.txt
$ echo "aaaaa" >&$var_a
$ cat file.txt
aaaaa
$ exec {var_a}>&-
$ ls /proc/self/fd/10
ls: cannot access '/proc/self/fd/10': No such file or directory
-
But this doesn't close all file descriptors.G-Man Says 'Reinstate Monica'– G-Man Says 'Reinstate Monica'2018-12-11 18:04:14 +00:00Commented Dec 11, 2018 at 18:04
-
Indeed. You'll have to exec in a loop like explained in the previous replies... It was just to point the fact that "eval" is not mandatory here and that we can keep the same logic to close it than to open it... Man pages are really not clear about this...David DG– David DG2018-12-11 18:11:03 +00:00Commented Dec 11, 2018 at 18:11