Translator: Tianhao Zhou
Although Windows has advantages over Linux in terms of graphical interfaces, due to Windows' limited support for terminal scripts, I still prefer Linux. Using terminal may, at first, seem counterintuitive, but with more familiarity, it can become a timer saver over graphical interfaces.
Instead of demonstrating the usage of Linux terminal, the post focuses on the basic yet confusing gotchas:
The difference between standard input and variables.
Why processes running on the background exit upon terminal termination?
Single-quotes vs. double-quotes.
sudo make commands
The difference between standard input and variables boils down to the question of when to use pipe
| and redirecting
< vs. when to use variables
For example, if a shell script to automate ethernet connection locates in my home directory:
$ where connect.sh/home/fdl/bin/connect.sh
To remove the script with minimal effort, I tried:
where connect.sh | rm
However, the command above is incorrect. The proper way is:
rm $(where connect.sh)
The former attempts to pipe the output from
where into the standard input of
rm, whereas the latter passes it in as an variable.
Typically standard inputs appear in programming languages as
readline; Variables refer to the literal arguments,
main program consumes.
As mentioned in「Linux file descriptor」, Pipe and redirecting aim to use data as standard input. By contrast,
$(cmd) reads the output from
cmd as variables.
Revisiting the previous example, the source code of
rm will certainly prefer receiving variable arguments over standard input to remove a file. In comparison, the
cat command accepts both standard input and variables.
$ cat filename...file text...$ cat < filename...file text...$ echo 'hello world' | cathello world
If a command can clog the terminal, then it accepts standard input and vice versa. For example, running "cat" without arguments will suspend (intentionally clog) the terminal to wait for user input and print back the same content.
For example, we want to spin up a Django web server on a remote server:
$ python manager.py runserver 0.0.0.0Listening on 0.0.0.0:8080...
With the server up and running, we can test it through the server's IP address. However, at the same time, the terminal will suspend, not responding to any input, until it detects
Ctrl-/ and kills the Python process.
With a tailing
&, the command won't clog the terminal and will continue to respond to incoming commands. However, the website becomes unavailable once you log out of the server.
To keep the web service available after logging out of the server, consider using this command
$ (python manager.py runserver 0.0.0.0 &)Listening on 0.0.0.0:8080...$ logout
Under the hood:：
Every terminal is a shell process, and it forks itself to provide child processes to execute commands. Usually, the shell process clogs while waiting for the child processes to exit, not accepting new commands. With a tailing
&, the shell process allows issuing new commands. However, when the shell process exits upon the termination of the terminal window, all its child processes will exit.
Nevertheless, commands like
(cmd &) move the process under
systemd, an OS guarded process that prevents the process from exiting when we close the current terminal.
An alternative approach to background execution is:
$ nohup some_cmd &
nohup functions similarly, but with extensive testing,
(cmd &) appears to be more stable.
Shells with different flavors behave differently, but with one invariant: for
), single-quote won't trigger evaluation, but double-quote will.
The shell behavior is observable through
set -x, which triggers playback:
As shown above,
echo $(cmd) and
echo "$(cmd)" differ slightly. Look closely, double-quote adds single-quote after evaluation whereas single-quote doesn't.
As a result, if the literal value from
$ contains space, we should use double-quote to avoid errors.
Under certain situations, a command that non-privileged users can execute becomes "not found" when privileged users try to run with
$ connect.shnetwork-manager: Permission denied$ sudo connect.shsudo: command not found
The root cause is that the
connect.sh script only exists in the user's environment variables.
$ where connect.sh/home/fdl/bin/connect.sh
sudo, we tell the OS that the
sudoer is executing the command, so the OS will search the environment variables of the
/etc/sudoer) where the
connect.sh script doesn't exist.
The solution is to locate the script with a path instead of a name: