Time to study: ~105 min
You will learn: exit codes, redirecting output and errors, the | pipe, chaining with &&, running things in the background, how to kill a stuck program, and package managers.
← Chapter 7 | back to Terminal 101 | next: Chapter 9 →
1. the exit codes
an exit code is a number returned by a program to show if it was successful or not.
0means success, and1or any other number means failure.
u may ask why do i need something like this, since the apps or the commands already return an error message like
there was an errororcommand not foundorpermission denied. so why?
the reason is that they are needed in scripting and automation. u may write a script that does a couple of things, but when u run it, it does not print those messages for u, so u have to handle it urself using the exit code. for now, understanding why and what it is, is enough.
# if we run a normal command like
ls ~
# it works and shows us a result, not the exit code.
# so in order to see the exit code we use `$?` which holds the exit code of the last command,
# and we print it with echo
echo $?0
and now a command that fails:
ls ~/not-exist-filels: cannot access '/home/ati/not-exist-file': No such file or directory
echo $?2
- note:
$?only holds the code of the last command u ran. if u runecho $?twice in a row, the second one gives u the exit code of the firstecho, which is0. so read it immediately. - different programs use different numbers for different problems.
lsuses2for “file not found”. u don’t have to memorize them, u only need to know that0is success and anything else is not.
2. the standard output and the standard error
we already used the standard output,
echo, which prints a message. in python it is
but what about the standard error,
stderr? we already mentioned it briefly in the first section, it is the error message that appears after a wrong or non-existing thing tries to execute, likels: cannot access '/home/ati/not-exist-file': No such file or directory. it goes directly to the terminal as an error message.
what if we want to redirect it to a file, to save it there and use it later, instead of a message that appears only once? and not only the error, we can also redirect the normal output.
the
>is used to redirect the output to a file:
echo "Hello World" > hello.txt
cat hello.txtHello World
we can also use
>>to append the output to the end of the file:
echo "Hello Again" >> hello.txt
cat hello.txtHello World
Hello Again
- careful: this is the difference that costs people their files:
>erases the file and writes from zero,>>adds to the end of it.
and now the error. we use
2>to redirect the error to a file:
cat not_exist.txt 2> error.txt
cat error.txtcat: not_exist.txt: No such file or directory
and
2>>to append the error instead of erasing:
cat also_not_exist.txt 2>> error.txt
cat error.txtcat: not_exist.txt: No such file or directory
cat: also_not_exist.txt: No such file or directory
- important thing to understand:
>and2>are two different channels. if a command succeeds there is no error to save, and if it fails there is no normal output to save. so:
echo "Hello World" 2> out.txtHello World
cat out.txt(empty, because echo succeeded so it produced no error at all)
the
Hello Worldstill went to ur screen, because2>only catches the error channel andechonever used it.
- the
2is the number of the error channel. the normal output is channel1, so>is really just a short way of writing1>. and there is a0as well, the input channel, which is what the|pipe is really feeding.
so if u ever see
2>&1in someone’s script, u can now read it: send channel 2 to wherever channel 1 is already going. that is how u get the errors and the normal output into the same file.
./my-script.sh > everything.log 2>&13. the | pipe
the
|pipe operator is used to redirect the output of one command into the input of another command. it is super useful when u want to chain more than one command together and the current command takes something from the previous one, instead of writing the first command, storing its output in a file, then using that file as input for the second one, and so on.
lsDocuments Downloads Music Pictures text.txt text2.txt text3.txt
ls | grep ".txt"text.txt
text2.txt
text3.txt
what this command is doing: we run
lsfirst, then we use the|to take the output oflsand use it as the input ofgrep, andgrepsearches for the file names that contain.txt.
another example:
ls | grep ".txt" | wc -l3
it is the same as before, we take the output of
lsand use it as input togrep, so we get the 3 files with the.txtextension. then withwc -l, which means “word count” with the-lflag meaning “count the number of lines”, we get3as a result. which means 3 lines contained.txt.
-
wccounts what comes into it.-lcounts lines,-wcounts words,-ccounts characters. -
u can chain as many as u want, the output of each one becomes the input of the next. this is the single most useful idea in the whole terminal.
4. running more than one command: ; and &&
the
|pipe connects two commands by their output. but sometimes u just want to run 2 commands one after the other, and the second one doesn’t care what the first one printed.
;runs them one after the other, no matter what happened:
mkdir practice ; cd practice ; pwd/home/ati/practice
&&runs the next one only if the previous one succeeded:
mkdir practice && cd practiceand here the exit codes from section 1 stop being theory.
&&is literally checking if the exit code was0before it continues.
so look at the difference, this is why
&&is the one u should use:
# with ; the mkdir fails, but the cd runs anyway
mkdir /etc/practice ; cd /etc/practicemkdir: cannot create directory '/etc/practice': Permission denied
bash: cd: /etc/practice: No such file or directory
# with && the mkdir fails, so the cd never runs at all
mkdir /etc/practice && cd /etc/practicemkdir: cannot create directory '/etc/practice': Permission denied
/etcneeds root, which we learned in Chapter 5, so themkdiris guaranteed to fail and it is a safe thing to try.
- there is also
||which is the opposite, it runs the next one only if the previous one failed:
cd practice || echo "that folder is not there"that folder is not there
u will see
&&everywhere, in tutorials, in install instructions, in scripts. now u know it is not decoration, it is a real check on the exit code.
5. running things in the background
until now every command took the terminal until it finished. if the command needs 10 minutes, u sit and wait 10 minutes. that is not realistic.
putting
&at the end sends it to the background and gives u ur terminal back straight away:
before that, a command we have not used yet:
sleep. it does nothing at all for the number of seconds u give it, which makes it perfect for practising this without breaking anything.
sleep 60 &[1] 4517
and u get ur prompt back immediately instead of waiting a minute. so what are those 2 numbers?
[1]is the job number. it counts the things running in the background of this terminal only, so the first one is 1, the next is 2. u use it with a%in front, likekill %1.4517is the PID, the process id. that one is given by the whole system, not by ur terminal, and it is the same number u would find withps auxin the next section.
so the same running program has 2 names: a short one that only ur terminal knows, and a long one that the whole machine knows.
jobs[1]+ Running sleep 60 &
- if u close the terminal, the job numbers are gone with it. the PID belongs to the system, which is why
killuses it.
and if u already started something and only then realised it will take forever, u don’t have to kill it and start again. press
ctrl + zto pause it:
sleep 60
# now press ctrl + z[1]+ Stopped sleep 60
reading that line:
[1]is the job number again, andStoppedis the important word. the program is still there in memory, it is just frozen and doing nothing. it is not finished and it is not killed.
- the
+next to the number means “this is the one u are working on right now”, so a barefgorbgwith no number will pick this one.
bg[1]+ sleep 60 &
jobsshows u everything running in the background of this terminal:
jobs[1]+ Running sleep 60 &
fgbrings it back to the front, so u can watch it or stop it withctrl + c:
fgsleep 60
- careful:
ctrl + zpauses the program, it does not keep it running. if u pressctrl + zand then forget about it, that program is frozen, not working.bgis what actually starts it moving again.
so the three of them are easy to mix up, and the difference is simple:
ctrl + ckills itctrl + zpauses it&runs it without blocking u
6. the interrupt and killing a process
normally, if we have an app which is running for too long and we would like to stop it, we can use
ctrl + c, which is calledSIGINT(Signal Interrupt). but what if the app is not responding at all?
so here comes the kill signal,
SIGKILL, which is used to kill the app. if we want to stop it we use thekillcommand:kill PID, which means kill + the id of the running process that will be killed.
so how can we get the PID of the running process?
# in order to see the PID we use the `ps` command which stands for process status.
# a = all processes of all users
# u = display in a user-oriented format, which basically adds columns to make it easier to read
# x = show the processes running in the background too
ps auxUSER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 168860 13360 ? Ss Aug08 0:32 /sbin/init splash
root 789 0.0 0.0 71308 3280 ? Ss Aug08 0:00 /usr/sbin/cron -f
root 1024 0.1 0.5 1023456 45200 ? Ssl Aug08 2:34 /usr/bin/dockerd
ati 1500 0.5 1.2 2345678 98760 ? Sl 09:15 0:45 /usr/bin/gnome-shell
ati 1602 0.0 0.0 12876 4580 tty2 S 09:15 0:00 /bin/my-stuck-script.sh
the list is usually hundreds of lines long, so reading it with ur eyes is not realistic. this is where the
|from the previous section becomes useful:
ps aux | grep "my-stuck-script.sh"ati 1602 0.0 0.0 12876 4580 tty2 S 09:15 0:00 /bin/my-stuck-script.sh
ati 1789 0.0 0.0 9032 2140 pts/0 S+ 09:22 0:00 grep my-stuck-script.sh
so we got 2 lines, one is the script we are looking for and the other one is the
grepcommand itself, becausegrepis also a running process at that moment and its own name contains the word we searched for. this confuses everyone the first time.
we take the PID of the real one,
1602, and use it in thekillcommand:
kill 1602(no output, and that is the normal case)
killsays nothing when it works. to check, run theps aux | grepagain and u will see the script is gone.
- if the process still refuses to die,
kill -9 1602is the hard version that the program can not ignore. use it only when the normalkilldid not work.
7. the top command
topis a command which shows u the programs running on ur machine and their status, live and updating by itself.
toptop - 11:15:32 up 2 days, 4:23, 3 users, load average: 1.85, 1.42, 1.23
Tasks: 287 total, 2 running, 285 sleeping, 0 stopped, 0 zombie
%Cpu(s): 12.5 us, 3.2 sy, 0.8 ni, 82.5 id, 0.5 wa, 0.2 hi, 0.3 si, 0.0 st
MiB Mem : 15982.5 total, 2345.6 free, 9876.3 used, 3760.6 buff/cache
MiB Swap: 2048.0 total, 2048.0 free, 0.0 used. 5123.4 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
2200 ati 20 0 4567890 267890 123456 R 18.5 1.7 3:45.23 brave --tab
1100 ati 20 0 3456789 145600 98765 S 8.3 0.9 4:32.12 brave-browser
1600 ati 20 0 2345678 98760 45678 S 4.2 0.6 0:32.45 gnome-shell
1850 root 20 0 1567890 45200 23456 S 1.5 0.3 2:34.12 dockerd
1500 ati 20 0 10328 1384 890 S 0.3 0.0 0:02.34 bash
1 root 20 0 168860 14320 8900 S 0.0 0.1 0:28.56 systemd
- press
qto get out of it, same aslessandman. - the difference from
ps aux:psgives u one snapshot and gives u the terminal back,topkeeps running and refreshing until u quit it. usepswhen u want to pipe it intogrep, usetopwhen u want to watch what is eating ur CPU right now.
8. the package manager
a package manager is the software that installs apps and programs onto ur system, and keeps track of what is installed.
i really prefer not using any external package managers, just use ur system’s official one. for example, if we want to install something like
nvim, which is a better and more configurable version ofvim:
# ubuntu / debian
sudo apt install neovim
# arch linux
sudo pacman -S neovim
# fedora
sudo dnf install neovimthere are some other ones like
flatpak,snapandappimage. these external ones u should ONLY use as a last resort, if the app isn’t in the official ones.
but what happens when u write the command to install an app?
- it checks the system to see if the app is already installed or not.
- if it is, it will not install it again.
- if it is not, it downloads it and installs its relevant dependencies too.
- then it puts the program in a directory that is already in the
PATH, which is why u can typenvimdirectly instead of the full path. this is exactly thePATHvariable from Chapter 7.
NOTES
-
/tmpis a temporary directory, it is used to store temporary files and data that does not need to be saved permanently. most systems empty it when u restart the machine, so never put anything u care about there. -
.logis the extension of a log file, it is used to store the logs of a program or the system. so most of the time, when u are saving the output of something to read later, save it as a.logfile and not as.txtor.md.
# > the normal output goes into run.log, erasing it first
# 2>> the errors go onto the end of errors.log, keeping what was there
./my-script.sh > run.log 2>> errors.logthis line puts the normal output in one file and the errors in another one, which is exactly how u would run a real script in the background.
Assignment
- Run a command that works and print its exit code. Then run one that fails and print its exit code.
- Create a file
notes.logcontaining the linefirst lineusing redirection. - Add a second line
second lineto it without erasing the first one. - Run
cat missing.txtand save only the error message intoerrors.log. - List ur home dir and count how many things are in it, in one command.
- Find how many of them contain the letter
D, in one command. - Find the PID of ur own
bashprocess. - Make a dir and go into it in one line, in a way that does not
cdif themkdirfailed. - Run
sleep 30in the background, check it withjobs, then bring it to the front and kill it. - Start
sleep 30normally, pause it withctrl + z, then set it running in the background again.
stuck, or done and want to check? the solutions are here