linux-shell

I personally like using Linux systems. Although Windows has a better graphical interface, its support for scripts is poor. At first, I was not used to command-line operations, but after getting used to it, I found that moving the mouse and clicking is actually what wastes the most time.

This article is not about specific Linux commands. Instead, I will explain some details and tips that are easy to confuse and can help you work more efficiently, using real situations as examples.

  1. The difference between standard input and command parameters.

  2. Running commands in the background, but they all stop after closing the terminal.

  3. The difference between single and double quotes in representing strings.

  4. Sometimes, a command with sudo will say "command not found".

  5. How to avoid typing repeated filenames, paths, or commands, and some other little tricks.

1. The Difference Between Standard Input and Parameters

This is a common problem. Many people don't know when to use the pipe symbol | or file redirection > and <, and when to use variables like $.

For example, I have a shell script called connect.sh for connecting to broadband, and it's in my home directory:

$ where connect.sh
/home/fdl/bin/connect.sh

If I want to delete this script and type as little as possible, how should I do it? I tried this before:

$ where connect.sh | rm

Actually, this is wrong. The correct way is:

$ rm $(where connect.sh)

The first command tries to send the result of where to rm as standard input. The second command sends the result as a command-line parameter.

Standard input means functions like scanf or readline in programming languages. Parameters are the string arrays passed to the main function as arguments.

In Linux File Descriptorsarrow-up-right, I said that pipe and redirection send data to a program's standard input, while $(cmd) gets the result of a command and passes it as a parameter.

Using the example above, the source code of the rm command does not accept standard input, only command-line parameters to delete the specified file. In comparison, the cat command accepts both standard input and parameters:

If a command will block the terminal and wait for your input, it means it accepts standard input. Otherwise, it does not. For example, if you just run cat without any arguments, the terminal will wait for your input and print what you typed.

2. Run Programs in the Background

Suppose you log in to a server and run a Django web program:

Now you can test the Django service using the server's IP address, but the terminal is blocked and won't respond to your input unless you use Ctrl-C or Ctrl-/ to stop the Python process.

You can add & at the end of the command to run it in the background. This way, the terminal doesn't block and you can run other commands. However, if you log out from the server, you can't access the web page anymore.

If you want the web service to keep running after you log out from the server, use (cmd &) like this:

The underlying reason is:

Every terminal is a shell process. When you run something, the shell makes it a child process. Normally, the shell waits for the child process to finish before accepting new commands. If you add &, the shell does not wait and can continue to accept your commands. But if you close the shell terminal, all its child processes will stop.

If you use (cmd &) to run a command, it will become a child of the systemd daemon process, instead of the shell. So, when you close the terminal, this command is not affected.

There is another common way to keep a process running:

The nohup command works for this too, but in my tests, using (cmd &) is more stable.

3. The Difference Between Single and Double Quotes

Different shells may behave slightly differently, but one thing is certain: if you use single quotes, characters like $, (, and ) are not interpreted, but double quotes will interpret them.

You can test shell behavior with the set -x command, which will show what the shell is really doing:

You can see that echo $(cmd) and echo "$(cmd)" have almost the same result, but there are some differences. Notice that with double quotes, the result adds single quotes automatically, but with no quotes, it does not.

In short, if the string from $ contains spaces, you should use double quotes, otherwise you will get errors.

4. sudo: command not found

Sometimes, a command works for a normal user, but adding sudo gives a "command not found" error:

This happens because the connect.sh script is only in the user's environment:

When you use sudo, the system uses the environment variables defined in /etc/sudoers. That does not include the script's path.

The solution is to run the script with its full path:

5. Typing similar filenames is annoying

You can use curly braces and commas to extend file names easily. Here are some examples:

Every item in the braces is combined with the string outside the braces. Do not add spaces inside the braces or around the commas, or it will not work.

This is very useful for commands like cp, mv, and rm:

6. Typing long paths is annoying

Use cd - to go back to the previous directory. For example:

The special command !$ replaces the last argument of the previous command. Example:

The special command !* replaces all arguments of the previous command. Example:

You can add your common directories to the CDPATH environment variable. When you use cd, the command will search these directories if it can't find the folder in the current location.

For example, if you often go to /var/log, set it like this:

This is very handy. You do not need to type the full path every time, saving time.

Note: These commands work with bash. Other shell interpreters also support searching directories with cd, but you may need to set it up differently. Please search for how to do it in your shell.

7. Typing repeated commands is troublesome

Use the special command !! to quickly repeat the last command:

What if a command is long and you can't remember the exact parameters?

In bash terminal, you can use Ctrl+R to search previous commands. This key combination searches backwards, finding the most recent command that matches.

For example, after pressing Ctrl+R, type sudo, and bash will show you the most recent command containing sudo. Press Enter to run it:

But this method has some drawbacks: First, it only works in bash, not in zsh or other shells. Second, it only finds the most recent match. If you want an older command, it's not convenient.

In this case, the most common way is to use the history command with pipe | and grep to search historical commands:

All the shell commands you use are recorded. The number in front is the command's index. After you find the command you want, you don't need to copy and paste. Just use ! plus the command number to run it again.

For example, to run the git config command shown above:

If you think typing history | grep is still too much, you can add a function to your shell config file (like .bashrc or .zshrc):

Now you only need to type his 'some_keyword' to search history.

I usually don't use bash as my terminal. I recommend a very useful shell: zsh, which is also what I use. Zsh supports many plugins and is easy to customize. You can look up detailed setup guides online.

Other Small Tips

1. The yes command automatically inputs y for confirmation

Sometimes when installing software, you get interactive prompts like:

We usually keep pressing y. If you're automating the installation, these questions can get in the way.

The yes command helps:

This will automatically answer y for every prompt and won't stop to ask you.

If you have read Linux File Descriptorsarrow-up-right, you will understand why: The yes command simply prints a lot of y. By connecting its output to the input of your_cmd, any interactive prompt will get a y and newline, just like you typed it.

2. Special variable $? saves the return value of the previous command

In Linux shell, if the program ends normally, the return value is 0. If not, the return value is not 0. Reading the last command's return value may not seem useful in daily use. But if you're writing shell scripts, it's very useful.

For example, you want to add a footer to many markdown files automatically. Some already have it, some don't. To avoid adding it again, you must check if the footer already exists. Here, $? and grep help:

3. Special variable $$ saves the PID (process ID) of the current process

This may not be useful every day, but it's helpful in shell scripts. For example, when you need to create temporary files in /tmp, you need unique names. You can use the $$ variable to get the process PID, which is always unique. You do not need to remember the file name.

Last updated