﻿---
title: Shell Programming
date: 2024-06-19
excerpt: Shell startup and features, command forms, I/O redirection and pipes, shell variables and quoting, writing and running shell scripts, plus a brief intro to sed and awk.
tags:
  - Linux
  - Shell
cover: https://assets.vluv.space/cover/OS/Ch8-Shell.webp
updated: 2026-07-08 21:17:39
lang: en
i18n:
  cn: /Ch8-Shell
  translation: 2
---

<script type="module" src="/js/components/tab.js"></script>

## Common Commands

`read`, `expr`, `tput`, and the like are external commands on Linux systems, and can be called from any shell script.

### read

`read` reads one line from standard input and assigns it to variables. It supports several options to control how it reads.

**Common options:**

```bash
-p "prompt" # display a prompt
-s          # silent mode, do not echo input
-t seconds  # timeout
-n chars    # return after reading the given number of characters
```

<x-tabs>

<x-tab title="Single variable" active>

```bash
read var
# assign all input data to var
```

</x-tab>

<x-tab title="Multiple variables">

```bash
read var1 var2 var3
# the first argument goes to var1, the second to var2, and any remaining arguments to the last variable
```

</x-tab>

<x-tab title="With a prompt">

```bash
read -p "Enter your name: " name
echo "Hello, $name"
```

</x-tab>

</x-tabs>

If there is no data on standard input when `read` runs, the program waits for input or gets terminated.

**Example:**

```bash
$ read name age
[stdin] yilan 23
$ echo "student $name is $age years old"
[stdout] student yilan is 23 years old
```

### tput

`tput` (terminal put) controls the terminal: it can position the cursor, set colors, clear the screen, and more. It connects to the terminal control code database `terminfo` and reads the control codes for the terminal indicated by the `TERM` environment variable.

**Common uses:**

```bash
tput clear       # clear the screen
tput cup row col # move the cursor to the given position
tput bold        # bold text
tput smso        # start standout mode
tput rmso        # end standout mode
tput sgr0        # reset all attributes
tput cols        # print the number of terminal columns
tput lines       # print the number of terminal lines
```

## Bash Syntax

### Common System Variables

```bash
$0    # name of the current shell program
$1-$9 # the first through ninth command-line arguments
$#    # number of command-line arguments (not counting $0)
$*    # all command-line arguments
$@    # all command-line arguments, each individually double-quoted
$$    # process ID (PID) of the current process
$?    # exit status of the last command
$!    # process ID of the last background process
```

### Bash Special Characters

Bash uses several kinds of brackets and special characters; understanding what they mean and how to use them matters a lot.

#### Command Substitution

```bash
$(command) # backticks, substitute the command's output
$(command) # $() syntax, same as backticks and the recommended form
```

#### Condition Tests

```bash
[ string ]   # traditional condition test, spaces required on both sides
[[ string ]] # Bash extended condition test, spaces on both sides, supports pattern matching
```

#### Arithmetic

```bash
((expr)) # arithmetic evaluation, no need to escape * and /
```

#### Grouping and Code Blocks

```bash
(command)    # run in a subshell
{ command; } # run in the current shell; the semicolon and space after command are required
```

### Conditionals

**Note:** the brackets in `[ condition ]` must have spaces on both sides, otherwise the condition test will not run correctly.

Basic syntax for `if`, `else`, and `elif`:

<x-tabs>

<x-tab title="if" active>

```bash
if [ condition ]; then
  # runs when the condition is true
fi
```

</x-tab>

<x-tab title="if-else">

```bash
if [ condition ]; then
  # runs when the condition is true
else
  # runs when the condition is false
fi
```

</x-tab>

<x-tab title="if-elif-else">

```bash
if [ condition1 ]; then
  # runs when condition1 is true
elif [ condition2 ]; then
  # runs when condition1 is false but condition2 is true
else
  # runs when all conditions are false
fi
```

</x-tab>

</x-tabs>

#### String Tests

```bash
-z STRING          # true if the string is empty
-n STRING          # true if the string is not empty
STRING1 = STRING2  # true if the two strings are equal
STRING1 != STRING2 # true if the two strings are not equal
```

#### Numeric Comparisons

```bash
NUM1 -eq NUM2          # true if the two numbers are equal
NUM1 -ne NUM2          # true if the two numbers are not equal
NUM1 -gt NUM2          # true if NUM1 is greater than NUM2
NUM1 -ge NUM2          # true if NUM1 is greater than or equal to NUM2
NUM1 -lt NUM2          # true if NUM1 is less than NUM2
NUM1 -le NUM2          # true if NUM1 is less than or equal to NUM2
[[ $string =~ regex ]] # true if the string matches the regular expression
```

#### File Tests

```bash
-e FILE # true if the file exists
-f FILE # true if the file exists and is a regular file
-d FILE # true if the file exists and is a directory
-r FILE # true if the file exists and is readable
-w FILE # true if the file exists and is writable
-x FILE # true if the file exists and is executable
```

#### Logical Operators

```bash
&&   # logical AND
||   # logical OR
```

### Loops

<x-tabs>

<x-tab title="for loop" active>

```bash
for ((counter = 1; counter <= 5; counter++)); do
  echo "Welcome, this is iteration number $counter."
done
```

</x-tab>

<x-tab title="while loop">

```bash
counter=0

# loop until the user enters the number 5
while [ $counter -ne 5 ]; do
  echo "Enter a number (current count: $counter)"
  read input_number

  # check whether the input is a number
  if ! [[ "$input_number" =~ ^[0-9]+$ ]]; then
    echo "Error: Please enter a valid number."
    continue # skip the rest of this iteration on invalid input
  fi

  # assign the input number to the counter
  counter=$input_number

  # print the current counter value
  echo "You entered: $counter"
done
```

</x-tab>

<x-tab title="case statement">

```bash
echo "Enter a number between 1 and 3:"
read number

case $number in
1) echo "You entered one." ;;
2) echo "You entered two." ;;
3) echo "You entered three." ;;
*) echo "You did not enter a number between 1 and 3." ;;
esac
```

</x-tab>

</x-tabs>
