Generic File Descriptors - STDIN, STDOUT, STDERR

When I talk about generic file descriptors I'm referring to standard input, output and error. For any application in operating systems based on linux kernel the following is defined:

One of the most common use cases with these file descriptors is to use redirect ">" symbol one or more of these streams towards files, null or from one another. As a first example I will redirect STDOUT of echo towards a file and then use cat to read that file but the third time I shall mispell it and redirect STDERR towards cat_error

╰─ echo "Some content" > myfile cat myfile Some content cat myfiles cat myfile 2>cat_error cat cat_error cat: myfiles: No such file or directory

Another common use case is to reduce the noise of a command and redirect STDERR towards /dev/null

╰─ find / -name "cat_error" find: ‘/proc/2942/fd’: Permission denied find: ‘/proc/2942/map_files’: Permission denied find: ‘/proc/2942/fdinfo’: Permission denied find: ‘/proc/2942/ns’: Permission denied find: ‘/home/tony/.cache/pacaur/icecat/pkg’: Permission denied /home/tony/Documents/cat_error find: ‘/home/tony/AUR/icecat/pkg’: Permission denied find: ‘/boot/efi’: Permission denied find: ‘/root’: Permission denied

I issued find command to search for the file in which I have just redirected STDERR and as you can see I have a lot of permission denied and I'm displaying only a few rows, there are so many that it's hard to even see the path of the actual file I'm searching for. So I'm gonna redirect STDERR to null device

╰─ ╰─ find / -name "cat_error" 2> /dev/null /home/tony/Documents/cat_error

As you can see now the output is only useful information and please note that there must not be any blank character between file descritpor number and redirect operator, "2>" is a totally different operation in comparisson to "2 >"

You can redirect both STDOUT and STDERR using syntax "&>"

╰─ ╰─ find / -name "cat_error" &> /dev/null

As we can see both stdout and stderr have been redirected

An important thing that I would like to mention is that these 3 generic file descriptors are present for each and every PID on the system. What was done above is for the PID of the shell on which commands were run but it can be done for example also for a script.

╰─ #!/bin/bash #STDOUT echo "This is output of script which appears on screen" # Redirecting stdout towards output_file exec 1> output_file echo "This is the output of script redirected towards output_file"

In this small script the first command is redirected to the screen which is the default and the second line is redirected towards file

╰─ ./myscript.sh This is output of script which appears on screen ╰─ cat output_file This is the output of script redirected towards output_file

The following example is for STDERR

╰─ #!/bin/bash #STDERR #error printed on screen unknown_command #Redirect stderr towards error_file exec 2> error_file unknown_command
╰─ ./myscript.sh ./myscript.sh: line 16: unknown_command: command not found ╰─ cat error_file ./myscript.sh: line 21: unknown_command: command not found

As we can see when I execute the above script I see on terminal screen the error for unknown command and the second time it is redirected to file. In the next example I will redirect stdin towards a custom file descriptor and then I link it back to file descriptor number 0.

╰─ #!/bin/bash #STDIN #Redirect stdin from FD 0 to FD 4 exec 4<&0 #> exec 0 while read line; do echo "$line" done #Following line will be ignored as STDIN is still linked to input_file read -p "Test data: " variable #Redirect stdin from FD 4 back to FD 0 exec 0<&4 #> read -p "Test data that works: " variable
╰─ ./myscript.sh This is just some data to read Test data that works: some data

The first read input prompt is ignored as STDIN is linked to input_file and the next one is not

To check what's happening on OS level when we perform these operations we can use lsof like this:

#!/bin/bash exec 3stdout_file exec 5>stderr_file #Redirect stdin to fd 4, stdout to fd 5 and stderr to fd 6 #Check open files for shell running this script lsof -a -p $$ -d 0,1,2,3,4,5
╰─ ./myscript.sh COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME myscript. 4402 tony 0u CHR 136,1 0t0 4 /dev/pts/1 myscript. 4402 tony 1u CHR 136,1 0t0 4 /dev/pts/1 myscript. 4402 tony 2u CHR 136,1 0t0 4 /dev/pts/1 myscript. 4402 tony 3r REG 254,0 31 10750881 /home/tony/Documents/input_file myscript. 4402 tony 4w REG 254,0 0 10750562 /home/tony/Documents/stdout_file myscript. 4402 tony 5w REG 254,0 0 10751053 /home/tony/Documents/stderr_file

We can see that FD 3 is open for read and is linked to regular file input_file, FD 4 and 5 are open for writing and are linked to stdout and stderr regular files. For a better understanding of lsof command please check lsof

That's about it, a lot more can be done using these descriptors but these are the basics