Redirection
In this lab, we will review redirection operators encountered during bash scripting and their specific meaning.
1. What is a File descriptor?
When you open a file in Linux, each file will be assigned with an Integer and this information is stored in the kernel. This way the kernel knows what files are opened and which process has opened the files. The assigned Integer number is what we call file descriptor (shortly FD).
By default, every program will start with three file descriptors.
- FD 0 -> Standard Input(stdin) -> Keyboard
- FD 1 -> Standard Output(Stdout) -> Display(Terminal)
- FD 2 -> Standard Error(Stderr) -> Display(Terminal)
You can see the file descriptors in /dev
directory:
-
Type the following:
$ ls -l /dev/std*
-
Ouput:
lrwxrwxrwx 1 root root 15 Mar 5 20:19 /dev/stderr -> /proc/self/fd/2 lrwxrwxrwx 1 root root 15 Mar 5 20:19 /dev/stdin -> /proc/self/fd/0 lrwxrwxrwx 1 root root 15 Mar 5 20:19 /dev/stdout -> /proc/self/fd/1
Stdin (FD0) gets input from the keyboard. stdout (FD1) and stderr (FD2) are sent to the terminal. When using Pipes and Redirections, you can change the way where input is passed from and output and errors are sent to.
The
proc
filesystem is a pseudo-filesystem which provides an interface to kernel data structures. It is commonly mounted at/proc.
2. Redirecting output to a file
As stated earlier, output (stdout
) and error (stderr
) of any program is sent to the terminal. You can use the redirection operator >
to write the stdout
and stderr
to a file.
Take a look at the below example. I am running the uname -mrs
command and redirecting the output to a file named uname.log
.
Type the following:
$ uname -mrs > uname.log
Then type:
$ cat uname.log
Linux 5.10.0-8-amd64 x86_64
And then empty the file by using the echo
command with no arguments or flags and >
:
echo > uname.log
Heads Up: When using
>
operator, if the file is not available, it will be created. And if the file exists already, it will be overridden with new contents.
You can also use the file descriptor number for stdout(1) before the redirection operator to redirect output to a file.
Try this too:
$ uname -mrs 1> uname.log
A single redirection operator (
>
) will only override the contents of a file, if the file already exists. However, if you want to append the contents instead of overwriting to the same file, use double redirection operator (i.e.>>
). You can also use stdout file descriptor(1) here too.
Next type:
$ whoami >> uname.log
and:
$ echo $SHELL 1>> uname.log
Finally:
$ cat uname.log
Output:
Linux 5.4.0-1091-azure x86_64
jovyan
/bin/bash
3. How to handle Stderr
Every program generates both output and error and both are sent to the terminal. In many cases, you may wish to either ignore the error or isolate the errors and redirect it to a separate file for troubleshooting.
Let's take a simple example of running the ls
command to see how this works.Say you are trying to list two files out of which only one is present.
Target:
$ ls -l uname.log u1.log
and you will see:
ls: cannot access 'u1.log': No such file or directory
-rw-r--r-- 1 jovyan users 1 Mar 6 14:30 uname.log
Here you are getting both error and output in the terminal.
Now you will use the same ls
command again but this time redirecting the output to a file.
$ ls -l uname.log u1.log > ls.op
ls: cannot access 'u1.log': No such file or directory
As you can see from the output >
operator redirects stdout(1) to a file but stderr(2) is sent to the terminal.
To redirect stderr to a file use the 2>
operator. It is mandatory to use the file descriptor for stderr(2) before the redirection operator >
that will send the error alone to a file.
ls -l uname.log u1.log > ls.op 2> ls.err
Run the following command to see the contents of ls.op
and ls.err
$ cat < ls.err
ls: cannot access 'u1.log': No such file or directory
Now, stdout and stderr are written to separate files. You can also send stdout and stderr to a single file too.
ls -l uname.log u1.log 1> ls.op 2> ls.op
From bash 4.4, you can also use &> sign to redirect both stdout and stderr to a file.
4. What is /dev/null?
Null is a character special file that accepts input and discards the input and produces no output. To put this simply, null discards whatever you redirect to it.
Try:
$ ls -l /dev/null
Output:
crw-rw-rw- 1 root root 1, 3 Mar 5 20:19 /dev/null
Why does the null matter in redirection? You might wonder. In some cases, you may wish not to print or store stdout or stderr. In that case, you can redirect either stdout or stderr to /dev/null
which will discard the stream of input.
c
character device based file Within Linux devices such as hardware are characterised in two ways: Character Devices (c
) which are devices which transfer data in characters also known as bytes or bits such as mice, speaker etc.The file type is one of the following characters: ?-
-
regular file
b
block special filec
character special fileC
high performance ("contiguous data") filed
directoryD
door (Solaris 2.5 and up)l
symbolic linkM
off-line ("migrated") file (Cray DMF)n
network special file (HP-UX)p
FIFO (named pipe)P
port (Solaris 10 and up)s
socket?
some other file type
Try the following commands:
-
Stdout and Stderr to Null
$ date > /dev/null
-
Stdout to Null
$ date 1> /dev/null
-
WRONG COMMAND, Stderr to Null
$ dateee 2> /dev/null
-
Stdout/Stderr to Null
$ date &> /dev/null
5. Input Redirection in Bash
Similar to how you are redirecting the output and error to a file, you can also pass inputs to a command using input redirection operator (<
).
Let’s start with a simple word count program:
$ wc -l < uname.log
Here, you are redirecting the contents of uname.log
file to the word count to find the number of lines.
Output:
1
You can also pass stdin file descriptor (0) when redirecting the input.
Try this:
$ wc -l 0< uname.log
1
You can combine the input and output redirection operators as shown below.
wc -l < uname.log &> wc.op
Output:
$ cat wc.op
1
Input redirection will be used along with a while loop to read the content of a file line by line.
6. Input redirection will be used along with a while loop to read the content of a file line by line.
Take a look at the below example. I am passing the /etc/passwd
file as the input to the while command. Here the read command will read line by line and store it in the variable VAL
and further down in the loop condition is written to check if the user is available.
Create a bash script called alertUser.sh
nano alertUser.sh
and fill with the following code:
#! /usr/bin/env bash
while read VAL; do
NAME=$(echo $VAL | awk -F ":" '/1/ {print $1}' )
if [[ $NAME -eq $(cat < uname.log | awk 'NR==2') ]];then
echo "User spotted"
break
fi
done < /etc/passwd
If you have followed this lab exactly, $(cat < uname.log | awk 'NR==2')
should contain your system username.
This is not an optimal way to achieve the task of finding the user but for demonstration purposes, this will do.
Now, make the script executable and run it:
$ chmod +x alertUser.sh
Ouput:
$ bash alertUser.sh
User spotted
Exploration
run these commands from the script seperatley to explore what is going on
cat /etc/passwd
cat /etc/passwd | awk -F ":" '/1/ {print $1}'
cat < uname.log | awl 'NR==2')