OFFSET偏移
← All articlesOFFSET / FIELD NOTESNO. 042

Shell fundamentals

Combine command-line tools with pipes, redirection, variables, and history operations.

English translation · Code and commands follow the Chinese original.

IN THESE READING PATHSLinux command-line fundamentals and composition ↗
About 4 min readStart reading

This article primarily uses Bash-style syntax. Fish differs in its syntax for variables, command substitution, history, and configuration.

The previous three articles covered "which commands to use"; this one covers "how to join them together". Pipes and redirection put the Linux philosophy into practice: small tools combine to do powerful things.


Pipes#

| — Feed one command’s output into the next command

command1 | command2

The basic idea: standard output from command1 becomes standard input to command2 directly.

ps aux | grep python        # ps 的输出交给 grep 筛选
ls -l | wc -l               # 统计显示行数,含 total 行;不是可靠文件数
cat file.txt | grep error   # 等价于 grep error file.txt
du -sh * | sort -hr         # 列出文件大小,从大到小排

You can keep chaining pipes:

cat access.log | grep "404" | cut -d' ' -f1 | sort | uniq -c | sort -rn
# 解释:日志 → 筛404 → 取IP列 → 排序 → 去重计数 → 按次数倒排
# 结果:谁在疯狂 404 一目了然

Redirection#

> — Write output to a file (overwrite)

echo "hello" > file.txt       # 写内容到文件,覆盖原有内容
ls -la > listing.txt          # ls 的结果存到文件,终端不显示

>> — Write output to a file (append)

echo "another line" >> file.txt    # 追加到末尾

< — Read input from a file

wc -l < file.txt         # 把文件内容喂给 wc(等价于 wc -l file.txt)

2>&1 — Merge standard error into standard output

./script.sh > output.log 2>&1    # 正常输出和报错都进同一个文件
./script.sh > /dev/null 2>&1     # 静默运行,什么都不显示

A model for understanding it:

Number Name Default source or destination
0 stdin (standard input) Keyboard
1 stdout (standard output) Terminal
2 stderr (standard error) Terminal

2>&1 means: redirect stderr (2) to wherever stdout (1) is currently going.


Wildcards#

*          # 匹配任意多个字符
?          # 匹配恰好一个字符
[abc]      # 匹配 a、b、c 中的任意一个
[0-9]      # 匹配 0-9 任意数字
ls *.md           # 所有 .md 文件
ls report_??.txt  # 先预览任意两个字符后缀(不限定数字)
rm -- report_??.txt  # 核对列表后才删除;不匹配时的行为取决于 Shell
ls [A-Z]*.md      # 大写字母开头的 .md 文件

Variables#

Definition and use

name="hello"          # 定义变量,等号两边不能有空格
echo "$name"            # 使用变量,加 $ 前缀
echo "${name}"          # 也可以用花括号包起来

Environment variables

echo $PATH            # 可执行文件的搜索路径
echo $HOME            # 你的家目录
echo $USER            # 当前用户名
env                   # 列出所有环境变量
export MY_VAR="val"   # 设为环境变量(子进程可见)

Command substitution

now=$(date)           # 把 date 命令的输出赋给变量
echo "现在时间:$now"
files=$(find . -maxdepth 1 -type f -printf 'x\n' | wc -l)  # GNU find:当前目录普通文件数

Aliases#

alias — Give a long command a short name

alias ll='ls -lah'              # ll 代替 ls -lah
alias gs='git status'           # git 常用操作缩到两字母
alias ..='cd ..'                # 快速回上级

alias                          # 查看当前所有别名

To make it persistent, put it in ~/.bashrc (bash) or ~/.config/fish/config.fish (fish).


History and shortcuts#

history — View command history

history                     # 全部历史
history | grep git          # 在历史中搜 git 相关命令

Reuse commands from history

!!          # 重复上一条命令
!git        # 重复最近一条以 git 开头的命令
!$          # 上一条命令的最后一个参数

Terminal keyboard shortcuts

Shortcut Action
Ctrl+C Terminate the current foreground program
Ctrl+Z Suspend the current foreground program
Ctrl+D Send EOF (exit the terminal or close the input stream)
Ctrl+L Clear the screen (equivalent to clear)
Ctrl+R Search command history backward
Ctrl+A Move the cursor to the beginning of the line
Ctrl+E Move the cursor to the end of the line
Ctrl+W Delete the word before the cursor
Ctrl+U Delete everything before the cursor

Ctrl+R is particularly useful: search a long history for a command you have used, then press it again to continue searching backward.


Combination examples#

Solve practical problems with a one-line command

# 当前目录项目(含目录)的磁盘占用前 5 名
du -sh * | sort -hr | head -5

# 统计名称匹配的进程数(不是线程数)
pgrep -c nginx
# 查看已知 PID 的线程数与线程列表
ps -o nlwp= -p PID
ps -T -p PID

# 查看 8080 端口被哪个程序占了
ss -tlnp | grep 8080

# 递归找所有 .md 文件中有多少行包含 "TODO"
grep -rn --include="*.md" "TODO" . | wc -l

This is the Unix philosophy: have each command do one thing well, and connect them with pipes.


The core mental model#

输入(stdin) → [命令] → 输出(stdout)
                  ↓
              错误(stderr)

管道 | :把 A 的 stdout 给 B 的 stdin
重定向 > :把 stdout 存到文件
2>&1   :把 stderr 合并到 stdout 同一去处

Once you understand this, the ways of connecting commands fall naturally into place.



Redirections are processed from left to right: > output.log 2>&1 and 2>&1 > output.log are not equivalent. History expansions such as !! are available in interactive Bash; check the expanded command before executing it.

References & further reading

  1. GNU Bash manual ↗

Adapted from technical notes. Examples use generic names.

Continue along a reading path

Linux command-line fundamentals and composition

  1. 01File and directory operations
  2. 02Process and job management
  3. 03Text processing and system queries
  4. 04Shell fundamentals
  5. 05Fuzzy finding with fzf
  6. 06Passing arguments with xargs
CONTINUE READING / NEXT ARTICLEFuzzy finding with fzf

Turn lists of files, processes, branches, and history entries into interactive selection inputs.