命令行环境概述

命令行环境不仅仅是输入命令的地方,它是一个强大的工作空间。通过合理配置命令行环境,可以极大提升工作效率。

什么是命令行环境

命令行环境包括:

  • Shell 配置文件:定义环境的行为和外观
  • 环境变量:存储系统级别的配置信息
  • 别名和函数:自定义快捷命令
  • 命令历史:记录之前执行的命令
  • 作业控制:管理前台和后台进程
  • 终端复用器:在单个终端中管理多个会话

Shell 配置文件

Bash 配置文件加载顺序详解

当 Bash 启动时,会按照以下顺序查找并加载配置文件:

/etc/profile                    # 系统级配置(所有用户共享)
~/.bash_profile                 # 用户登录配置(优先)
    ↓ 如果不存在
~/.bash_login                   # 备用登录配置
    ↓ 如果不存在
~/.profile                      # 备用登录配置(适用于多种 Shell)
~/.bashrc                       # 交互式非登录 Shell 配置
/etc/bash.bashrc                # 系统级交互式配置

加载规则:

  1. 登录 Shell(如 ssh user@hostlogin 命令):

    • 首先执行 /etc/profile(系统级)
    • 然后依次查找 ~/.bash_profile~/.bash_login~/.profile,只执行第一个找到的
    • 通常会在登录配置中通过 source ~/.bashrc 加载交互式配置
  2. 非登录 Shell(如终端窗口、直接输入 bash):

    • 只执行 ~/.bashrc
  3. 脚本执行(如 bash script.sh):

    • 默认不加载任何配置文件
    • 使用 bash -l script.sh 强制作为登录 Shell 执行

登录 Shell vs 非登录 Shell

类型启动方式加载配置用途
登录 Shellssh user@hostloginsu - user.bash_profile.bashrc远程登录、系统启动
非登录 Shell终端窗口、bashsu user.bashrc日常操作、本地开发

典型配置模式:

~/.bash_profile 中:

# 如果 .bashrc 存在,则加载它
if [ -f ~/.bashrc ]; then
    source ~/.bashrc
fi

# 登录时执行的一次性操作
echo "Welcome back, $USER!"

这样无论是登录还是非登录 Shell,都会加载 .bashrc 的配置。

配置文件分离策略

将配置按功能分离可以提高可维护性:

mkdir -p ~/.bash.d

# 创建功能模块
touch ~/.bash.d/aliases          # 别名
touch ~/.bash.d/functions        # 函数
touch ~/.bash.d/path             # 路径配置
touch ~/.bash.d/prompt           # 提示符配置
touch ~/.bash.d/git              # Git 相关配置
touch ~/.bash.d/tmux             # tmux 相关配置

~/.bashrc 中统一加载:

# 加载所有配置模块
for file in ~/.bash.d/*.bash; do
    [ -r "$file" ] && source "$file"
done

Zsh 配置文件

/etc/zshrc                      # 系统级配置
~/.zshrc                        # 用户级配置(主要,交互式)
~/.zprofile                     # 登录 Shell 配置(类似 .bash_profile)
~/.zlogin                       # 登录 Shell 配置(在 .zprofile 之后)
~/.zshenv                       # 所有 Shell 都会加载(包括脚本)
~/.zlogout                      # 退出登录时执行

Zsh 配置推荐:

# ~/.zshenv - 所有 Shell 都会加载,设置关键环境变量
export PATH="/usr/local/bin:$PATH"
export EDITOR="vim"

# ~/.zprofile - 登录 Shell 配置
if [ -f ~/.zshrc ]; then
    source ~/.zshrc
fi

# ~/.zshrc - 交互式配置
autoload -Uz compinit && compinit
setopt autocd extendedglob

Fish Shell 配置

~/.config/fish/config.fish      # Fish 主配置文件
~/.config/fish/functions/       # 自定义函数目录
~/.config/fish/completions/     # 自定义补全目录

配置文件调试

# 查看配置加载过程
bash -x -l -c 'echo "loaded"'

# 检查配置文件语法
bash -n ~/.bashrc

# 临时禁用配置
bash --norc

# 查看当前 Shell 类型
echo $0                          # 显示 Shell 路径
shopt login_shell               # Bash:检查是否为登录 Shell
echo $SHLVL                     # 显示 Shell 嵌套层级

环境变量

查看环境变量

env                              # 查看所有环境变量(较详细)
printenv                         # 查看所有环境变量(更简洁)
echo $PATH                       # 查看特定环境变量
export                           # 查看当前导出的变量及其值
declare -x                       # 查看所有导出的变量(Bash)

设置环境变量

export MY_VAR="hello"            # 设置临时环境变量(仅当前会话有效)
export PATH="$PATH:/my/custom/path"  # 添加到 PATH
MY_VAR="value" command           # 为单个命令设置环境变量
declare MY_VAR="hello"           # 声明变量但不导出(Bash)

环境变量作用域

# 局部变量(仅当前 Shell)
local_var="local"                # 函数内使用
MY_VAR="hello"                   # 当前 Shell 内可用

# 导出变量(子进程可见)
export MY_VAR="hello"            # 子进程可以访问

# 全局变量(所有会话)
# 需要写入配置文件

环境变量继承

# 父进程设置的环境变量会传递给子进程
export MESSAGE="hello"
bash -c 'echo $MESSAGE'          # 输出: hello

# 子进程修改的环境变量不会影响父进程
bash -c 'export MESSAGE="world"'
echo $MESSAGE                    # 输出: hello

永久设置环境变量

按场景选择配置文件:

场景推荐文件说明
用户交互式配置~/.bashrc别名、函数、提示符
用户登录配置~/.bash_profilePATH、环境变量
系统级配置/etc/profile所有用户共享
所有 Shell(包括脚本)~/.zshenv (Zsh)关键环境变量

编辑 ~/.bashrc~/.zshrc

export PATH="$PATH:/my/custom/path"
export EDITOR="vim"
export LANG="zh_CN.UTF-8"
export TERM="xterm-256color"
export HISTCONTROL="ignoredups:ignorespace"

然后执行:

source ~/.bashrc                 # 使配置生效

常用环境变量详解

变量说明示例值
HOME用户主目录/home/edward
PATH命令搜索路径/usr/bin:/bin
USER当前用户名edward
SHELL当前 Shell/bin/bash
EDITOR默认编辑器vim
LANG语言设置zh_CN.UTF-8
PWD当前工作目录/home/edward
PS1命令提示符格式\u@\h \w $
HISTFILE命令历史文件路径~/.bash_history
HISTSIZE内存中保存的历史条数1000
HISTFILESIZE文件中保存的历史条数10000
TERM终端类型xterm-256color
LD_LIBRARY_PATH动态库搜索路径/usr/local/lib
PYTHONPATHPython 模块搜索路径~/projects
JAVA_HOMEJava 安装目录/usr/lib/jvm/java-11
GOPATHGo 工作目录~/go
NODE_PATHNode.js 模块搜索路径/usr/lib/node_modules
MANPATH手册页搜索路径/usr/share/man
DISPLAYX Window 显示服务器:0
SSH_AUTH_SOCKSSH 代理套接字/tmp/ssh-agent.sock
TZ时区设置Asia/Shanghai

自定义命令提示符

PS1 转义字符:

字符含义
\u当前用户名
\h主机名(不含域名)
\H完整主机名
\w当前工作目录(完整路径)
\W当前工作目录(仅目录名)
\d日期(周几 月 日)
\t时间(时:分:秒)
\T时间(12小时制)
\@时间(12小时制 AM/PM)
\$普通用户显示 $,root 显示 #
\[开始非打印字符序列
\]结束非打印字符序列

简单的彩色提示符:

export PS1="\[\e[32m\]\u@\h \[\e[34m\]\w\[\e[0m\] $ "

带 Git 分支信息的提示符:

parse_git_branch() {
    git branch 2>/dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/ (\1)/'
}
export PS1="\[\e[32m\]\u@\h \[\e[34m\]\w\[\e[31m\]\$(parse_git_branch)\[\e[0m\] $ "

带时间和颜色的高级提示符:

export PS1="\[\e[1;36m\]\t \[\e[1;32m\]\u@\h \[\e[1;34m\]\w\[\e[0m\]\[\e[1;31m\]\$(parse_git_branch)\[\e[0m\] \$ "

多 Shell 共享环境变量

方案一:使用 .profile 文件(所有 Shell 通用)

# ~/.profile - 所有 Shell 都会读取
export PATH="$PATH:/my/custom/path"
export EDITOR="vim"
export LANG="zh_CN.UTF-8"

方案二:创建共享配置文件

# 创建共享配置
mkdir -p ~/.config/shell
touch ~/.config/shell/env

# 在各个 Shell 配置文件中加载
# ~/.bashrc
if [ -f ~/.config/shell/env ]; then
    source ~/.config/shell/env
fi

# ~/.zshrc
if [ -f ~/.config/shell/env ]; then
    source ~/.config/shell/env
fi

# ~/.config/fish/config.fish
if test -f ~/.config/shell/env
    source ~/.config/shell/env
end

环境变量实战技巧

# 添加多个路径到 PATH
export PATH="/opt/node/bin:/opt/python/bin:$PATH"

# 临时修改 PATH(不影响当前会话)
PATH="/tmp/bin:$PATH" command

# 从文件加载环境变量
source .env                       # 加载 .env 文件

# 将环境变量保存到文件
env > ~/.env.backup               # 保存所有环境变量

# 检查环境变量是否存在
if [ -z "$MY_VAR" ]; then
    echo "MY_VAR 未设置"
fi

# 设置默认值
MY_VAR="${MY_VAR:-default_value}"  # 如果未设置则使用默认值

# 条件设置环境变量
[ -d ~/go ] && export GOPATH="$HOME/go"

命令历史

查看历史

history                          # 查看命令历史
history | grep "git"             # 搜索历史命令
history | tail -20               # 查看最近 20 条命令
history -n 10                    # 只显示最近 10 条

执行历史命令

!123                             # 执行历史中的第 123 条命令
!!                               # 执行上一条命令
!$                               # 引用上一条命令的最后一个参数
!git                             # 执行最近一条 git 命令
!git:p                           # 打印但不执行最近一条 git 命令
!-2                              # 执行倒数第二条命令
!*                               # 引用上一条命令的所有参数

命令替换

^old^new^                        # 替换上一条命令中的第一个 old 为 new
!!:gs/old/new/                   # 替换上一条命令中的所有 old 为 new
!git:s/add/commit/               # 替换最近 git 命令中的 add 为 commit

反向搜索

Ctrl + R                         # 反向搜索命令历史

输入关键词后:

  • Ctrl + R 继续搜索更早的匹配
  • Enter 执行命令
  • Ctrl + G 退出搜索
  • TabCtrl + Y 将匹配内容粘贴到命令行

历史配置

export HISTSIZE=10000            # 内存中保存的历史条数
export HISTFILESIZE=100000       # 文件中保存的历史条数
export HISTCONTROL="ignoredups:ignorespace"  # 忽略重复和空格开头的命令
export HISTIGNORE="ls:ll:cd"     # 忽略特定命令
export HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S "  # 显示时间戳
export HISTFILE=~/.bash_history  # 指定历史文件路径
export HISTCMD                   # 当前命令在历史中的编号

HISTCONTROL 选项:

选项说明
ignorespace忽略以空格开头的命令
ignoredups忽略连续重复的命令
ignoreboth同时启用 ignorespace 和 ignoredups
erasedups删除历史中所有重复的命令

管理历史文件

~/.bash_history                  # Bash 历史文件
~/.zsh_history                   # Zsh 历史文件

history -c                       # 清空当前会话历史
history -w                       # 立即写入历史文件
history -a                       # 将当前会话历史追加到文件
history -r                       # 从文件读取历史到当前会话
rm ~/.bash_history               # 删除历史文件

历史记录安全

# 禁止保存包含密码的命令
export HISTIGNORE="*password*:*secret*:*token*"

# 使用空格开头隐藏敏感命令
 ls -la                          # 这条命令不会被保存

# 加密历史文件(高级)
gpg -c ~/.bash_history           # 加密
gpg -d ~/.bash_history.gpg > ~/.bash_history  # 解密

跨终端同步历史

Bash 配置:

# ~/.bashrc - 实时同步历史
export PROMPT_COMMAND="history -a; history -c; history -r; $PROMPT_COMMAND"

Zsh 配置:

# ~/.zshrc - 实时同步历史
setopt inc_append_history
setopt share_history

历史记录高级技巧

# 搜索包含特定参数的命令
history | grep "ssh" | grep "server"

# 统计最常用的命令
history | awk '{print $1}' | sort | uniq -c | sort -rn | head -10

# 删除历史中的特定命令
history -d 123                   # 删除第 123 条命令

# 过滤历史记录
history | grep -v "^[0-9]*  ls"  # 排除 ls 命令

# 将历史记录导出到文件
history > ~/history_backup.txt

# 从文件导入历史记录
cat ~/history_backup.txt >> ~/.bash_history
history -r

# 使用 fzf 模糊搜索历史
history | fzf --tac              # 按时间倒序显示

别名和函数

别名

alias ll='ls -la'                # 列出详细信息
alias ls='ls --color=auto'       # 带颜色的 ls
alias la='ls -la'                # 列出所有文件(包括隐藏)
alias l='ls -CF'                 # 简洁列表
alias gs='git status'            # Git 状态
alias ga='git add'               # Git 添加
alias gc='git commit -m'         # Git 提交
alias gp='git push'              # Git 推送
alias gl='git log --oneline'     # Git 日志
alias gd='git diff'              # Git 差异
alias ..='cd ..'                 # 上级目录
alias ...='cd ../..'             # 上两级目录
alias ....='cd ../../..'         # 上三级目录
alias cls='clear'                # 清屏
alias grep='grep --color=auto'   # 带颜色的 grep
alias egrep='egrep --color=auto' # 带颜色的 egrep
alias fgrep='fgrep --color=auto' # 带颜色的 fgrep
alias df='df -h'                 # 人类可读的磁盘使用情况
alias du='du -h'                 # 人类可读的目录大小
alias free='free -h'             # 人类可读的内存使用情况
alias ps='ps aux'                # 显示所有进程

条件别名

# 只有在特定条件下才定义别名
if command -v exa &> /dev/null; then
    alias ls='exa'
    alias ll='exa -l'
    alias la='exa -la'
fi

# 根据操作系统定义不同别名
if [[ "$OSTYPE" == "darwin"* ]]; then
    alias ls='ls -G'             # macOS 颜色
else
    alias ls='ls --color=auto'   # Linux 颜色
fi

函数

# 创建目录并进入
mkcd() {
    mkdir -p "$1" && cd "$1"
}

# 提取各种压缩文件
extract() {
    if [ -f "$1" ] ; then
        case "$1" in
            *.tar.bz2)   tar xvjf "$1"    ;;
            *.tar.gz)    tar xvzf "$1"    ;;
            *.bz2)       bunzip2 "$1"     ;;
            *.rar)       unrar x "$1"     ;;
            *.gz)        gunzip "$1"      ;;
            *.tar)       tar xvf "$1"     ;;
            *.tbz2)      tar xvjf "$1"    ;;
            *.tgz)       tar xvzf "$1"    ;;
            *.zip)       unzip "$1"       ;;
            *.Z)         uncompress "$1"  ;;
            *.7z)        7z x "$1"        ;;
            *)           echo "无法识别的格式: $1" ;;
        esac
    else
        echo "$1 不是有效的文件"
    fi
}

# 快速查找文件并编辑
fe() {
    find . -type f -name "$1" -exec vim {} \;
}

# 查找包含特定文本的文件
ff() {
    find . -type f -exec grep -l "$1" {} \;
}

# 快速创建 Python 虚拟环境
venv() {
    python3 -m venv .venv
    source .venv/bin/activate
    echo "虚拟环境已激活"
}

# 退出虚拟环境并删除
deactivate_rm() {
    deactivate
    rm -rf .venv
    echo "虚拟环境已删除"
}

# 统计目录大小(带进度)
duh() {
    du -h --max-depth=1 "$1" | sort -rh
}

# 查看端口占用情况
port() {
    lsof -i :"$1" 2>/dev/null || ss -tuln | grep "$1"
}

# 杀死占用指定端口的进程
killport() {
    kill -9 $(lsof -ti:"$1") 2>/dev/null || echo "端口 $1 未被占用"
}

# 创建临时目录并进入
tmpcd() {
    local tmpdir=$(mktemp -d)
    cd "$tmpdir"
    echo "进入临时目录: $tmpdir"
}

# 显示最近修改的文件
recent() {
    ls -lt "${1:-.}" | head -"${2:-20}"
}

# 查看文件类型并决定打开方式
openfile() {
    local file="$1"
    if [ -f "$file" ]; then
        case "$file" in
            *.txt|*.md|*.py|*.js|*.json) vim "$file" ;;
            *.pdf) xdg-open "$file" ;;
            *.jpg|*.png|*.gif) xdg-open "$file" ;;
            *) echo "未知文件类型" ;;
        esac
    else
        echo "文件不存在"
    fi
}

函数进阶技巧

# 使用参数默认值
backup() {
    local src="${1:-.}"
    local dest="${2:-~/backup/$(date +%Y%m%d)}"
    mkdir -p "$dest"
    rsync -av --delete "$src" "$dest"
    echo "备份完成: $src -> $dest"
}

# 使用选项参数
mycommand() {
    local verbose=false
    local output="output.txt"
    
    while [[ "$#" -gt 0 ]]; do
        case $1 in
            -v|--verbose) verbose=true ;;
            -o|--output) output="$2"; shift ;;
            *) echo "未知选项: $1"; return 1 ;;
        esac
        shift
    done
    
    if $verbose; then
        echo "详细模式已开启"
        echo "输出文件: $output"
    fi
}

# 使用 read 交互输入
confirm() {
    read -p "$1 (y/n): " -n 1 -r
    echo
    [[ $REPLY =~ ^[Yy]$ ]]
}

# 使用 trap 清理临时文件
temp_example() {
    local tmpfile=$(mktemp)
    trap "rm -f $tmpfile" EXIT
    
    echo "临时文件: $tmpfile"
    # 执行操作...
}

# 返回值处理
check_file() {
    if [ -f "$1" ]; then
        return 0
    else
        return 1
    fi
}

动态别名

# 根据当前目录设置别名
case "$PWD" in
    /home/*/projects/*)
        alias build='npm run build'
        alias dev='npm run dev'
        ;;
    /home/*/go/*)
        alias run='go run .'
        alias build='go build'
        ;;
esac

# 使用函数替代复杂别名
gpush() {
    git push origin "$(git branch --show-current)"
}

gpull() {
    git pull origin "$(git branch --show-current)"
}

别名和函数的区别

特性别名函数
复杂度适合简单命令替换适合复杂逻辑
参数处理不支持参数支持参数
控制流支持 if/for/while
性能稍快稍慢
可读性适合短命令适合长逻辑

保存别名和函数

将别名和函数添加到 ~/.bashrc~/.zshrc 中,这样每次启动 Shell 时都会自动加载。

分离配置推荐:

# ~/.bashrc
source ~/.bash_aliases
source ~/.bash_functions

检查别名和函数:

alias                            # 查看所有别名
unalias ll                       # 删除别名
declare -f                       # 查看所有函数
declare -f mkcd                  # 查看特定函数
unset -f mkcd                    # 删除函数

作业控制

后台运行

command &                        # 在后台运行命令
nohup command &                  # 后台运行,退出终端后仍继续执行
setsid command                   # 创建新会话运行命令
(command &)                      # 在子 Shell 中后台运行

查看后台作业

jobs                             # 查看后台任务
jobs -l                          # 查看后台任务及 PID
jobs -p                          # 只显示 PID
jobs -r                          # 只显示运行中的任务
jobs -s                          # 只显示暂停的任务

切换作业

fg %1                            # 将后台任务 1 切换到前台
fg %-                            # 将最近的后台任务切换到前台
fg %string                       # 将名称包含 string 的任务切换到前台
bg %1                            # 将暂停的任务 1 放到后台继续
bg                               # 将最近暂停的任务放到后台继续

暂停和恢复

Ctrl + Z                         # 暂停当前前台任务(放入后台)
kill -CONT <pid>                 # 恢复暂停的进程
kill -STOP <pid>                 # 暂停进程
kill -CONT %1                    # 恢复作业 1
kill -STOP %1                    # 暂停作业 1

终止作业

kill %1                          # 终止后台任务 1(发送 SIGTERM)
kill -9 %1                       # 强制终止后台任务 1(发送 SIGKILL)
kill -15 %1                      # 优雅终止(SIGTERM)
killall process_name             # 终止所有同名进程
pkill process_name               # 按名称终止进程

信号详解

# 查看所有信号
kill -l

# 常用信号
kill -1 <pid>                    # SIGHUP - 挂起(重新加载配置)
kill -2 <pid>                    # SIGINT - 中断(Ctrl + C)
kill -3 <pid>                    # SIGQUIT - 退出(Ctrl + \)
kill -9 <pid>                    # SIGKILL - 强制终止
kill -15 <pid>                   # SIGTERM - 优雅终止
kill -19 <pid>                   # SIGSTOP - 暂停(不能被捕获)
kill -18 <pid>                   # SIGCONT - 继续

常用信号表:

信号编号说明
SIGHUP1终端断开连接
SIGINT2用户中断(Ctrl + C)
SIGQUIT3用户退出(Ctrl + \),产生 core dump
SIGKILL9强制终止(不可捕获)
SIGTERM15优雅终止(默认)
SIGSTOP19暂停进程(不可捕获)
SIGCONT18继续暂停的进程
SIGUSR110用户自定义信号 1
SIGUSR212用户自定义信号 2

进程组

# 查看进程组
ps -o pgid= <pid>                # 查看进程所属进程组
ps axj                           # 查看进程组信息

# 创建进程组
(set -m; command1 & command2 &)   # 在子 Shell 中创建新进程组

# 向进程组发送信号
kill -TERM -<pgid>               # 向整个进程组发送信号

作业控制高级技巧

# 查看作业状态
jobs -l
# 示例输出: [1]+  Running                 sleep 100 &
#          [2]-  Stopped                 vim file.txt

# 作业状态符号:
# + : 当前作业(最近放入后台的)
# - : 前一个作业
# Running : 运行中
# Stopped : 已暂停

# 切换到特定作业
fg %1                            # 切换到作业 1
fg %vim                          # 切换到 vim 作业

# 查看进程树
pstree <pid>                     # 查看进程树
pstree -p <pid>                   # 显示 PID

# 查看进程详细信息
ps aux | grep process_name       # 查看进程
ps -fp <pid>                     # 查看进程详细信息

# 查看进程资源使用
top -p <pid>                     # 实时查看进程资源
htop -p <pid>                    # 更友好的进程查看

# 查看进程打开的文件
lsof -p <pid>                    # 查看进程打开的文件
lsof -i :8080                    # 查看占用端口的进程

示例:后台运行长时间任务

# 后台运行备份脚本
nohup ./backup.sh > backup.log 2>&1 &

# 查看进度
tail -f backup.log

# 切换到前台
fg %1

# 暂停任务
Ctrl + Z

# 放到后台继续
bg %1

# 查看作业状态
jobs -l

# 终止任务
kill %1

使用 systemd 管理长期运行的服务

# 创建服务文件
sudo vim /etc/systemd/system/myapp.service

# 服务文件内容
[Unit]
Description=My Application
After=network.target

[Service]
Type=simple
User=edward
WorkingDirectory=/home/edward/app
ExecStart=/home/edward/app/start.sh
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

# 启用服务
sudo systemctl daemon-reload
sudo systemctl enable myapp
sudo systemctl start myapp

# 查看状态
sudo systemctl status myapp
sudo journalctl -u myapp -f

终端复用器

终端复用器允许你在单个终端窗口中管理多个会话,是高效开发的必备工具。

tmux(推荐)

安装

sudo apt install tmux            # Debian/Ubuntu
sudo yum install tmux            # CentOS/RHEL
brew install tmux               # macOS

基本操作

tmux                             # 启动 tmux
tmux new -s session_name         # 创建命名会话
tmux attach -t session_name      # 连接到会话
tmux ls                          # 列出所有会话
tmux kill-session -t session_name # 杀死会话
tmux kill-server                 # 杀死所有会话

会话管理

# 创建会话
tmux new -s work                 # 创建名为 work 的会话
tmux new -s project -c ~/projects # 在指定目录创建会话

# 列出会话
tmux ls

# 连接会话
tmux attach -t work              # 连接到 work 会话
tmux a -t work                   # 简写

# 分离会话
tmux detach                      # 分离当前会话
Ctrl + B 然后 d                  # 快捷键分离

# 切换会话
tmux switch -t work              # 切换到 work 会话
tmux switch -n                   # 切换到下一个会话
tmux switch -p                   # 切换到上一个会话

# 重命名会话
tmux rename-session -t old new   # 重命名会话
Ctrl + B 然后 $                  # 快捷键重命名

# 杀死会话
tmux kill-session -t work        # 杀死 work 会话
tmux kill-session -a             # 杀死除当前外的所有会话
tmux kill-session -a -t work     # 杀死除 work 外的所有会话

窗口管理

# 创建窗口
tmux new-window                  # 创建新窗口
tmux new-window -n editor        # 创建名为 editor 的窗口
tmux new-window -c ~/projects    # 在指定目录创建窗口
Ctrl + B 然后 c                  # 快捷键创建

# 切换窗口
tmux select-window -t 0          # 切换到窗口 0
tmux select-window -n            # 切换到下一个窗口
tmux select-window -p            # 切换到上一个窗口
tmux select-window -t :editor    # 切换到名为 editor 的窗口
Ctrl + B 然后 n                  # 快捷键切换到下一个
Ctrl + B 然后 p                  # 快捷键切换到上一个
Ctrl + B 然后 0-9                # 快捷键切换到指定窗口

# 重命名窗口
tmux rename-window new_name      # 重命名窗口
Ctrl + B 然后 ,                  # 快捷键重命名

# 关闭窗口
tmux kill-window                 # 关闭当前窗口
tmux kill-window -t 0            # 关闭窗口 0
Ctrl + B 然后 &                  # 快捷键关闭

# 交换窗口
tmux swap-window -s 0 -t 1       # 交换窗口 0 和 1

窗格管理

# 分割窗格
tmux split-window -h             # 水平分割
tmux split-window -v             # 垂直分割
tmux split-window -h -c ~/projects # 在指定目录分割
Ctrl + B 然后 %                  # 快捷键水平分割
Ctrl + B 然后 "                  # 快捷键垂直分割

# 切换窗格
tmux select-pane -U              # 向上切换
tmux select-pane -D              # 向下切换
tmux select-pane -L              # 向左切换
tmux select-pane -R              # 向右切换
tmux select-pane -t :.0          # 切换到指定窗格
Ctrl + B 然后 方向键             # 快捷键切换
Ctrl + B 然后 o                  # 顺时针切换

# 移动窗格
tmux swap-pane -U                # 向上移动
tmux swap-pane -D                # 向下移动
Ctrl + B 然后 {                  # 向左移动
Ctrl + B 然后 }                  # 向右移动

# 调整窗格大小
tmux resize-pane -U 5            # 向上调整 5 行
tmux resize-pane -D 5            # 向下调整 5 行
tmux resize-pane -L 5            # 向左调整 5 列
tmux resize-pane -R 5            # 向右调整 5 列
tmux resize-pane -Z              # 最大化/恢复
Ctrl + B 然后 Ctrl + 方向键      # 快捷键调整大小
Ctrl + B 然后 z                  # 快捷键最大化

# 关闭窗格
tmux kill-pane                   # 关闭当前窗格
tmux kill-pane -t :.1            # 关闭指定窗格

# 窗格布局
tmux select-layout even-horizontal  # 水平均分
tmux select-layout even-vertical    # 垂直均分
tmux select-layout main-horizontal  # 主水平布局
tmux select-layout main-vertical    # 主垂直布局
tmux select-layout tiled            # 平铺布局

tmux 快捷键

快捷键功能
Ctrl + B 然后 %水平分割窗口
Ctrl + B 然后 "垂直分割窗口
Ctrl + B 然后 方向键在窗格间切换
Ctrl + B 然后 z最大化/恢复窗格
Ctrl + B 然后 c创建新窗口
Ctrl + B 然后 n切换到下一个窗口
Ctrl + B 然后 p切换到上一个窗口
Ctrl + B 然后 d分离会话(后台运行)
Ctrl + B 然后 :进入命令模式
Ctrl + B 然后 ?显示快捷键帮助
Ctrl + B 然后 $重命名会话
Ctrl + B 然后 ,重命名窗口
Ctrl + B 然后 {向左移动窗格
Ctrl + B 然后 }向右移动窗格
Ctrl + B 然后 &关闭窗口
Ctrl + B 然后 o顺时针切换窗格
Ctrl + B 然后 0-9切换到指定窗口
Ctrl + B 然后 !将窗格拆分为独立窗口

tmux 配置

创建 ~/.tmux.conf

# 基础配置
set -g default-terminal "xterm-256color"
set -g history-limit 10000
set -g mouse on
set -g default-shell "/bin/bash"

# 前缀键
set -g prefix C-a
unbind C-b
bind C-a send-prefix

# 状态栏
set -g status-bg "#222222"
set -g status-fg "#888888"
set -g status-left "#[fg=green]#H"
set -g status-right "#[fg=blue]%H:%M:%S #[fg=red]%d-%b-%y"
set -g status-interval 1
set -g status-justify centre

# 窗口列表
set -g window-status-format "#[fg=cyan]#I #[fg=white]#W"
set -g window-status-current-format "#[fg=green]#I #[fg=white]#W"

# 窗格分割
bind | split-window -h
bind - split-window -v
bind k split-window -v -c '#{pane_current_path}'
bind j split-window -h -c '#{pane_current_path}'

# 窗格导航
bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R

# 窗格大小调整
bind -r H resize-pane -L 5
bind -r J resize-pane -D 5
bind -r K resize-pane -U 5
bind -r L resize-pane -R 5

# 窗口导航
bind -r 0 select-window -t :0
bind -r 1 select-window -t :1
bind -r 2 select-window -t :2
bind -r 3 select-window -t :3
bind -r 4 select-window -t :4
bind -r 5 select-window -t :5
bind -r 6 select-window -t :6
bind -r 7 select-window -t :7
bind -r 8 select-window -t :8
bind -r 9 select-window -t :9

# 重新加载配置
bind r source-file ~/.tmux.conf \; display-message "Config reloaded!"

# 复制模式
set -g mode-keys vi
bind-key -T copy-mode-vi 'v' send-keys -X begin-selection
bind-key -T copy-mode-vi 'y' send-keys -X copy-selection-and-cancel
bind-key -T copy-mode-vi 'Escape' send-keys -X cancel

# 同步窗格
bind w setw synchronize-panes

tmux 插件系统

安装 TPM(tmux plugin manager):

git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm

配置插件:

~/.tmux.conf 中添加:

# 插件列表
set -g @plugin 'tmux-plugins/tpm'
set -g @plugin 'tmux-plugins/tmux-sensible'
set -g @plugin 'tmux-plugins/tmux-resurrect'
set -g @plugin 'tmux-plugins/tmux-continuum'
set -g @plugin 'tmux-plugins/tmux-yank'
set -g @plugin 'catppuccin/tmux'

# 初始化插件
run '~/.tmux/plugins/tpm/tpm'

常用插件:

插件功能
tmux-sensible合理的默认配置
tmux-resurrect保存和恢复会话
tmux-continuum自动保存和恢复
tmux-yank系统剪贴板支持
tmux-cpuCPU 使用率显示
tmux-memory内存使用率显示
catppuccin/tmux美观的主题

使用 TPM:

Ctrl + B 然后 I                  # 安装插件
Ctrl + B 然后 U                  # 更新插件
Ctrl + B 然后 Alt + U            # 卸载插件

进阶用法

# 在会话间切换
tmux switch -t session_name

# 重命名会话
Ctrl + B 然后 $

# 重命名窗口
Ctrl + B 然后 ,

# 移动窗格
Ctrl + B 然后 {}

# 调整窗格大小
Ctrl + B 然后 Ctrl + 方向键

# 同步所有窗格(同时执行相同命令)
tmux setw synchronize-panes on   # 开启同步
tmux setw synchronize-panes off  # 关闭同步

# 保存和恢复会话(需要 tmux-resurrect)
Ctrl + B 然后 Ctrl + s           # 保存
Ctrl + B 然后 Ctrl + r           # 恢复

# 录制和回放会话
tmux capture-pane -p > output.txt # 捕获当前窗格内容
tmux new-session -d -s demo 'cat output.txt' # 回放

脚本化 tmux

# 创建开发环境
tmux new-session -d -s dev
tmux split-window -h -t dev
tmux split-window -v -t dev:0
tmux select-window -t dev
tmux attach -t dev

# 启动项目
tmux new-session -d -s project
tmux send-keys -t project 'cd ~/projects/myapp' C-m
tmux send-keys -t project 'npm run dev' C-m
tmux split-window -h -t project
tmux send-keys -t project:1 'cd ~/projects/myapp' C-m
tmux send-keys -t project:1 'vim .' C-m
tmux attach -t project

screen(传统终端复用器)

screen                           # 启动 screen
screen -S session_name           # 创建命名会话
screen -r session_name           # 恢复会话
screen -ls                       # 列出会话
screen -x session_name           # 多用户共享会话
Ctrl + A 然后 d                  # 分离会话
Ctrl + A 然后 c                  # 创建新窗口
Ctrl + A 然后 n                  # 切换到下一个窗口
Ctrl + A 然后 p                  # 切换到上一个窗口
Ctrl + A 然后 0-9                # 切换到指定窗口
Ctrl + A 然后 ?                  # 显示帮助
screen -wipe                     # 清理死会话

SSH 配置

基础连接

ssh user@hostname                # 基本连接
ssh -p 2222 user@hostname        # 指定端口
ssh -i ~/.ssh/my_key user@hostname # 指定密钥文件
ssh -l user hostname              # 指定用户名
ssh user@hostname command         # 在远程执行命令并返回

SSH 配置文件

创建 ~/.ssh/config

Host server1
    HostName 192.168.1.100
    User edward
    Port 22
    IdentityFile ~/.ssh/id_ed25519
    ForwardAgent yes
    ServerAliveInterval 60

Host github
    HostName github.com
    User git
    IdentityFile ~/.ssh/github_key

Host *
    Compression yes
    TCPKeepAlive yes

使用配置:

ssh server1                      # 直接连接

配置文件权限:

chmod 600 ~/.ssh/config          # 必须设置为 600
chmod 700 ~/.ssh                 # 必须设置为 700

SSH 密钥管理

# 生成密钥对
ssh-keygen -t ed25519 -C "your_email@example.com"
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"  # RSA 格式

# 指定密钥文件路径
ssh-keygen -t ed25519 -f ~/.ssh/github_key -C "github"

# 复制公钥到服务器
ssh-copy-id user@hostname
ssh-copy-id -i ~/.ssh/my_key user@hostname

# 手动复制公钥
cat ~/.ssh/id_ed25519.pub | ssh user@hostname "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

# 使用密钥代理
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
ssh-add -l                       # 查看已添加的密钥
ssh-add -d ~/.ssh/id_ed25519     # 删除密钥
ssh-add -D                       # 删除所有密钥

密钥文件权限:

chmod 600 ~/.ssh/id_ed25519      # 私钥必须设置为 600
chmod 644 ~/.ssh/id_ed25519.pub  # 公钥设置为 644

SSH Agent Forwarding

什么是 Agent Forwarding?

Agent Forwarding 允许你在远程服务器上使用本地的 SSH 密钥,而不需要将私钥复制到远程服务器上。

启用 Agent Forwarding:

# 命令行启用
ssh -A user@hostname

# 配置文件启用
Host server1
    ForwardAgent yes

工作原理:

本地机器 (Agent 运行,持有私钥)
    |
    | SSH 连接 (Agent Forwarding 启用)
    |
远程服务器 (通过 SSH 连接访问本地 Agent)
    |
    | 使用本地 Agent 认证
    |
GitHub/GitLab/其他服务器

安全注意事项:

  • 只在信任的服务器上启用 Agent Forwarding
  • 可以通过 ssh-agent -k 结束 Agent
  • 使用 ssh-add -x 锁定 Agent,需要密码才能使用

SSH ControlMaster(连接复用)

什么是 ControlMaster?

ControlMaster 允许复用同一个 SSH 连接,避免每次连接都重新认证,大幅提升连接速度。

配置 ControlMaster:

# ~/.ssh/config
Host *
    ControlMaster auto
    ControlPath ~/.ssh/sockets/%r@%h-%p
    ControlPersist 600            # 保持连接 10 分钟

手动控制:

# 创建主连接
ssh -M -S ~/.ssh/sockets/master user@hostname

# 复用连接
ssh -S ~/.ssh/sockets/master user@hostname

# 关闭连接
ssh -O exit -S ~/.ssh/sockets/master user@hostname

SSH 端口转发

# 本地端口转发
ssh -L 8080:localhost:3000 user@server
ssh -L 8080:remote-host:3000 user@server

# 远程端口转发
ssh -R 8080:localhost:3000 user@server

# 动态端口转发(SOCKS 代理)
ssh -D 1080 user@server

# 绑定到所有接口
ssh -L 0.0.0.0:8080:localhost:3000 user@server

端口转发示例:

# 通过 SSH 访问远程数据库
ssh -L 5432:localhost:5432 user@db-server

# 通过 SSH 访问内网服务
ssh -L 8080:internal-service:80 user@gateway

# 将本地服务暴露到远程
ssh -R 8080:localhost:3000 user@server

SSH 隧道示例

# 通过跳板机访问内网服务器
ssh -J jump@jumphost user@internal-server

# 配置到 ~/.ssh/config
Host internal
    HostName internal-server.local
    User edward
    ProxyJump jump@jumphost

# 使用 ProxyCommand(旧版方式)
Host internal
    HostName internal-server.local
    User edward
    ProxyCommand ssh -W %h:%p jump@jumphost

SSH 安全配置

服务器端安全配置(/etc/ssh/sshd_config):

# 禁用密码登录
PasswordAuthentication no

# 禁用 root 登录
PermitRootLogin no

# 限制登录用户
AllowUsers edward admin
AllowGroups ssh-users

# 更改默认端口
Port 2222

# 限制连接数
MaxStartups 10:30:60

# 启用公钥认证
PubkeyAuthentication yes

# 禁用无用的认证方式
ChallengeResponseAuthentication no
GSSAPIAuthentication no

# 设置空闲超时
ClientAliveInterval 600
ClientAliveCountMax 3

客户端安全配置:

# ~/.ssh/config
Host *
    PasswordAuthentication no     # 优先使用密钥认证
    PubkeyAuthentication yes
    StrictHostKeyChecking accept-new  # 自动接受新主机密钥
    HashKnownHosts yes            # 对主机名进行哈希

SSH 多跳连接

# 直接多跳
ssh -J user1@host1,user2@host2 user3@host3

# 通过多个跳板机
ssh -o ProxyJump="user1@host1,user2@host2" user3@host3

# 配置文件
Host host3
    HostName host3.internal
    User user3
    ProxyJump user1@host1,user2@host2

SSH 高级技巧

# 保持连接活跃
ssh -o ServerAliveInterval=60 user@hostname

# 压缩传输
ssh -C user@hostname

# 禁用 X11 转发
ssh -x user@hostname

# 设置连接超时
ssh -o ConnectTimeout=10 user@hostname

# 调试连接
ssh -v user@hostname             # 详细输出
ssh -vvv user@hostname           # 更详细输出

# 批量执行命令
ssh user@hostname 'command1; command2; command3'

# 传送文件
scp file.txt user@hostname:/path/
scp -r folder/ user@hostname:/path/

# 使用 rsync 同步
rsync -avz local/ user@hostname:/remote/

# 创建 SSH 隧道并后台运行
ssh -f -N -L 8080:localhost:3000 user@server

# 使用 SSH 作为 SOCKS 代理
ssh -D 1080 -f -N user@server

SSH 配置文件高级选项

选项说明
IdentityFile指定私钥文件路径
ForwardAgent是否启用 Agent Forwarding
ForwardX11是否启用 X11 转发
ServerAliveInterval心跳间隔(秒)
ControlMaster是否启用连接复用
ControlPath连接套接字路径
ControlPersist保持连接时间
ProxyJump跳板机配置
ProxyCommand代理命令
Compression是否启用压缩
TCPKeepAlive是否启用 TCP 保活
StrictHostKeyChecking主机密钥检查策略
HashKnownHosts是否哈希主机名

文件查找与定位

which/whereis/locate

which python                     # 查找命令的可执行文件位置
whereis python                   # 查找命令的二进制、源码和手册
locate "*.txt"                   # 快速查找文件(基于数据库)
updatedb                         # 更新 locate 数据库

find

find . -name "*.py"              # 查找 Python 文件
find . -type f -size +10M        # 查找大于 10MB 的文件
find . -mtime -7                 # 查找最近 7 天修改的文件
find . -user edward              # 查找指定用户的文件
find . -name "__pycache__" -exec rm -rf {} +  # 删除目录

fzf(模糊查找工具)

安装:

git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf
~/.fzf/install

使用:

fzf                              # 交互式模糊查找
Ctrl + T                         # 在命令行中插入文件路径
Ctrl + R                         # 模糊搜索命令历史

命令行工具链

管道与重定向

command1 | command2              # 管道:将 command1 的输出作为 command2 的输入
command > output.txt             # 覆盖输出到文件
command >> output.txt            # 追加输出到文件
command 2> error.txt             # 错误输出重定向
command 2>&1                     # 错误重定向到标准输出
command < input.txt              # 输入重定向
command << EOF                   # Here-doc 输入

进程替换

diff <(sort file1.txt) <(sort file2.txt)  # 比较两个排序后的文件
grep "pattern" <(cat file1.txt file2.txt) # 在合并内容中搜索

命令分组

(command1 && command2) > output.txt       # 分组执行,重定向输出
{ command1; command2; } > output.txt      # 分组执行(注意分号和空格)

子 Shell

(cd /tmp && ls)                  # 在子 Shell 中执行,不影响当前目录

终端配置

终端类型

echo $TERM                       # 查看终端类型
export TERM="xterm-256color"     # 设置 256 色终端

颜色支持

# 测试 256 色
for i in {0..255}; do
    printf "\x1b[38;5;${i}mcolour${i}\x1b[0m\n"
done

字体配置

推荐字体:

  • JetBrains Mono:JetBrains 出品的编程字体
  • Fira Code:支持连字的编程字体
  • Source Code Pro:Adobe 出品的编程字体

终端多路复用

# 在 tmux 中使用不同目录
tmux new-window -c ~/projects    # 在指定目录创建窗口

远程开发环境

本地开发 vs 远程开发

方式优点缺点
本地开发响应快、调试方便环境配置繁琐、资源受限
远程开发环境统一、资源充足网络延迟、需要 SSH

远程开发工具

VS Code Remote Development

# 安装 Remote-SSH 扩展
# 在 VS Code 中按 F1,输入 "Remote-SSH: Connect to Host"
# 选择配置文件或输入 ssh 命令

SSHFS(远程文件系统挂载)

sudo apt install sshfs           # 安装
sshfs user@hostname:/remote/path /local/mountpoint  # 挂载
umount /local/mountpoint         # 卸载

rsync(文件同步)

rsync -avz local/ user@hostname:/remote/  # 同步到远程
rsync -avz user@hostname:/remote/ local/  # 同步到本地
rsync -avz --delete local/ user@hostname:/remote/  # 同步并删除多余文件

实用技巧

快速导航

cd -                             # 切换到上一个目录
cd ~/projects                    # 使用波浪号
cd /                             # 切换到根目录
cd ..                            # 切换到上级目录
cd ../..                         # 切换到上两级目录
pushd /path/to/dir               # 入栈并切换
pushd +1                         # 切换到栈中第 1 个目录
popd                             # 出栈并切换
dirs                             # 查看目录栈
dirs -v                          # 查看目录栈(带编号)

命令补全

ls docu<Tab>                     # 自动补全
git che<Tab>                     # 自动补全命令
cd /usr/loc<Tab>                 # 自动补全路径
<Tab><Tab>                       # 显示所有可能的补全

# 命令行参数补全
ssh <Tab>                        # 补全 SSH 配置
kill <Tab>                       # 补全进程名

# 自定义补全(Bash)
complete -W "start stop restart status" myservice

命令编辑

Ctrl + A                         # 移动到命令行开头
Ctrl + E                         # 移动到命令行结尾
Ctrl + U                         # 删除光标前的所有字符
Ctrl + K                         # 删除光标后的所有字符
Ctrl + W                         # 删除光标前的一个单词
Ctrl + Y                         # 粘贴上次删除的内容
Ctrl + T                         # 交换光标前后两个字符
Alt + F                          # 向前移动一个单词
Alt + B                          # 向后移动一个单词
Alt + D                          # 删除光标后的一个单词
Alt + Backspace                  # 删除光标前的一个单词
Alt + C                          # 大写光标所在单词
Alt + U                          # 大写光标后所有字符
Alt + L                          # 小写光标后所有字符
Esc + T                          # 交换光标前后两个单词
Ctrl + X + Ctrl + E              # 在编辑器中打开当前命令

命令行计算器

echo "2 + 3" | bc                # 基本计算
echo "scale=2; 10 / 3" | bc      # 设置精度
echo "sqrt(16)" | bc             # 平方根
echo "10^2" | bc                 # 幂运算
echo "s(0.5)" | bc -l            # 正弦函数
python3 -c "print(2 ** 10)"      # Python 计算
python3 -c "import math; print(math.pi)"
node -e "console.log(2 + 3)"     # Node.js 计算

临时环境

# 在子 Shell 中测试配置
bash
# 修改配置...
exit  # 退出子 Shell,回到原始环境

# 使用子 Shell 执行命令
(cd /tmp && ls)                  # 不影响当前目录

# 使用 fakeroot 模拟 root 权限
fakeroot dpkg-deb --build package/

命令计时

time command                     # 显示命令执行时间
time ls -la                      # 示例

# 详细计时
time -p command                  # POSIX 格式
/usr/bin/time command            # 更详细的计时

# 多次执行取平均值
for i in {1..10}; do time command; done

# 统计执行时间
echo "scale=3; $(date +%s.%N)"   # 高精度时间戳

文件编码转换

iconv -f GBK -t UTF-8 input.txt > output.txt  # GBK 转 UTF-8
iconv -l                         # 列出所有支持的编码
dos2unix file.txt                # DOS 转 Unix
unix2dos file.txt                # Unix 转 DOS
sed -i 's/\r$//' file.txt       # 手动去除换行符
tr -d '\r' < input.txt > output.txt  # 删除回车符

终端配色主题

安装主题:

# 安装 Base16 主题
git clone https://github.com/chriskempson/base16-shell.git ~/.config/base16-shell

# 安装 Solarized 主题
git clone https://github.com/altercation/solarized.git ~/.solarized

配置主题:

# ~/.bashrc
BASE16_SHELL="$HOME/.config/base16-shell/"
[ -n "$PS1" ] && \
    source "$BASE16_SHELL/profile_helper.sh"

# 选择主题
base16_ocean

终端配色测试:

# 测试 256 色
for i in {0..255}; do
    printf "\x1b[38;5;${i}m%3d " "${i}"
    ((i + 1) % 16 == 0) && printf "\n"
done

# 测试真彩色
awk 'BEGIN{
    s="/\\/\\/\\/\\/\\"; s=s s s s s s s s;
    for (colnum = 0; colnum<77; colnum++) {
        r = 255-(colnum*255/76);
        g = (colnum*510/76);
        b = (colnum*255/76);
        if (g>255) g = 510-g;
        printf "\033[48;2;%d;%d;%dm", r,g,b;
        printf "\033[38;2;%d;%d;%dm", 255-r,255-g,255-b;
        printf "%s\033[0m", substr(s,colnum+1,1);
    }
    printf "\n";
}'

终端性能优化

# 禁用不必要的 Bash 特性
shopt -u autocd                  # 禁用自动 cd
shopt -u cdspell                 # 禁用 cd 拼写检查

# 优化 ls 命令
alias ls='ls --color=auto -F'    # 减少输出信息

# 使用 faster alternatives
command -v exa && alias ls='exa'
command -v bat && alias cat='bat'
command -v fd && alias find='fd'
command -v rg && alias grep='rg'

# 禁用 Bash 历史同步(如果不需要)
# unset PROMPT_COMMAND

命令行安全

# 隐藏敏感命令(以空格开头)
 rm -rf /                         # 这条命令不会被保存到历史

# 使用密钥而非密码
ssh-keygen -t ed25519
ssh-copy-id user@hostname

# 加密敏感文件
gpg -c secret.txt                # 加密
gpg -d secret.txt.gpg            # 解密

# 使用一次性密码
ssh user@hostname -o PreferredAuthentications=keyboard-interactive

自动化技巧

# 使用 expect 自动化交互
expect << 'EOF'
    spawn ssh user@hostname
    expect "password:"
    send "mypassword\r"
    interact
EOF

# 使用 yes 命令自动回答
yes | apt install package

# 使用 timeout 限制命令执行时间
timeout 10 long-running-command

# 使用 watch 定期执行命令
watch -n 1 "date && uptime"

Shell 脚本基础

脚本结构

#!/bin/bash
# 脚本描述
# Usage: script.sh [options]

set -e                          # 遇到错误立即退出
set -u                          # 禁止使用未定义变量
set -o pipefail                 # 管道中任何命令失败都视为失败

# 脚本内容
echo "Hello, World!"

变量

# 定义变量
name="Edward"
age=25

# 使用变量
echo "My name is $name"
echo "I am $age years old"

# 命令替换
today=$(date +%Y-%m-%d)
echo "Today is $today"

# 数组
fruits=("apple" "banana" "cherry")
echo ${fruits[0]}               # 访问数组元素
echo ${#fruits[@]}              # 数组长度
echo ${fruits[@]}               # 所有元素

条件判断

# if 语句
if [ -f "file.txt" ]; then
    echo "文件存在"
elif [ -d "dir" ]; then
    echo "目录存在"
else
    echo "都不存在"
fi

# 字符串比较
if [ "$name" = "Edward" ]; then
    echo "Hello Edward"
fi

# 数字比较
if [ $age -gt 18 ]; then
    echo "成年人"
fi

# 逻辑运算
if [ -f "file.txt" ] && [ -r "file.txt" ]; then
    echo "文件存在且可读"
fi

# 模式匹配
if [[ "$name" == E* ]]; then
    echo "名字以 E 开头"
fi

循环

# for 循环
for i in {1..10}; do
    echo $i
done

# 遍历文件
for file in *.txt; do
    echo $file
done

# while 循环
count=1
while [ $count -le 10 ]; do
    echo $count
    count=$((count + 1))
done

# until 循环
count=1
until [ $count -gt 10 ]; do
    echo $count
    count=$((count + 1))
done

函数

# 定义函数
greet() {
    echo "Hello, $1!"
}

# 调用函数
greet "Edward"

# 返回值
add() {
    echo $(( $1 + $2 ))
}
result=$(add 3 5)
echo "Result: $result"

参数处理

# 访问参数
echo "脚本名: $0"
echo "参数1: $1"
echo "参数2: $2"
echo "所有参数: $@"
echo "参数个数: $#"

# 处理选项
while [[ "$#" -gt 0 ]]; do
    case $1 in
        -h|--help) echo "帮助信息"; exit ;;
        -v|--verbose) verbose=true ;;
        -o|--output) output="$2"; shift ;;
        *) echo "未知选项: $1"; exit 1 ;;
    esac
    shift
done

脚本调试

# 启用调试模式
set -x                          # 打印每条执行的命令

# 使用调试参数运行
bash -x script.sh

# 条件调试
if [ "$DEBUG" = "true" ]; then
    set -x
fi

实用脚本示例

#!/bin/bash
# 文件备份脚本

SOURCE_DIR="$1"
DEST_DIR="$2"

if [ -z "$SOURCE_DIR" ] || [ -z "$DEST_DIR" ]; then
    echo "Usage: $0 <source_dir> <dest_dir>"
    exit 1
fi

mkdir -p "$DEST_DIR"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$DEST_DIR/backup_$TIMESTAMP.tar.gz"

echo "Starting backup..."
tar -czf "$BACKUP_FILE" "$SOURCE_DIR"

if [ $? -eq 0 ]; then
    echo "Backup completed: $BACKUP_FILE"
else
    echo "Backup failed!"
    exit 1
fi

终端多任务管理

使用 tmux 管理开发环境

# 创建开发会话
tmux new-session -d -s dev -n editor
tmux send-keys -t dev 'cd ~/projects/myapp && vim .' C-m

tmux new-window -t dev -n server
tmux send-keys -t dev:1 'cd ~/projects/myapp && npm run dev' C-m

tmux new-window -t dev -n logs
tmux send-keys -t dev:2 'cd ~/projects/myapp && tail -f logs/app.log' C-m

tmux attach -t dev

使用 screen 管理后台任务

# 创建后台会话
screen -dmS backup
screen -S backup -X stuff './backup.sh\n'

# 查看会话
screen -ls

# 恢复会话
screen -r backup

使用 systemd 管理服务

# 创建服务文件
sudo vim /etc/systemd/system/myapp.service

[Unit]
Description=My Application
After=network.target

[Service]
Type=simple
User=edward
WorkingDirectory=/home/edward/app
ExecStart=/home/edward/app/start.sh
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

# 启用服务
sudo systemctl daemon-reload
sudo systemctl enable myapp
sudo systemctl start myapp

# 查看状态
sudo systemctl status myapp
sudo journalctl -u myapp -f

性能优化

Shell 启动优化

# 检查启动时间
time bash -c 'exit'

# 分析启动过程
bash -x -l -c 'exit' 2>&1 | head -50

# 延迟加载配置
# ~/.bashrc
load_lazy() {
    case "$1" in
        git) source ~/.bash_git ;;
        python) source ~/.bash_python ;;
    esac
}

# 使用 zsh 替代 bash(更快的启动速度)

命令执行优化

# 使用 faster alternatives
command -v exa && alias ls='exa'
command -v bat && alias cat='bat'
command -v fd && alias find='fd'
command -v rg && alias grep='rg'

# 禁用不必要的特性
shopt -u autocd
shopt -u cdspell

# 使用管道替代临时文件
grep "pattern" file.txt | sort | uniq

# 使用进程替换
diff <(sort file1.txt) <(sort file2.txt)

内存优化

# 查看内存使用
free -h
top -o %MEM

# 清理缓存
sudo sync && sudo echo 3 > /proc/sys/vm/drop_caches

# 限制进程内存
ulimit -v 1024000               # 限制为 1GB

# 使用临时文件系统
mkdir -p /tmp/cache
mount -t tmpfs -o size=1G tmpfs /tmp/cache

练习

基础练习

  1. 修改 ~/.bashrc 添加自定义别名和函数
  2. 设置自定义命令提示符,显示用户名、主机名和当前目录
  3. 使用 tmux 创建多个窗口和窗格
  4. 配置 SSH config 文件连接到远程服务器

进阶练习

  1. 创建一个函数,自动创建项目目录结构
  2. 使用 fzf 实现快速文件查找和打开
  3. 设置 Git 分支信息在命令提示符中显示
  4. 配置 SSH 端口转发访问远程服务
  5. 编写一个备份脚本,自动备份指定目录到远程服务器

综合练习

  1. 使用 tmux 管理多个开发会话,包含编辑器、服务器、日志窗口
  2. 配置远程开发环境,实现代码同步和自动部署
  3. 编写完整的 Shell 脚本,实现自动化部署流程
  4. 配置终端配色主题和快捷键,打造个性化开发环境

总结

配置文件

文件用途
~/.bashrcBash 交互式配置
~/.bash_profileBash 登录配置
~/.zshrcZsh 配置
~/.ssh/configSSH 配置
~/.tmux.conftmux 配置

环境变量

变量用途
PATH命令搜索路径
EDITOR默认编辑器
PS1命令提示符
HISTCONTROL历史记录控制

快捷键

快捷键功能
Ctrl + R反向搜索历史
Ctrl + Z暂停任务
Ctrl + D退出 Shell
Ctrl + L清屏

终端复用器

工具特点
tmux现代、功能强大、活跃维护
screen传统、兼容性好

SSH 高级特性

特性说明
Agent Forwarding在远程服务器上使用本地密钥
ControlMaster连接复用,提升连接速度
Port Forwarding端口转发,访问内网服务
ProxyJump跳板机配置

通过这一课的学习,你已经掌握了命令行环境的高级配置技巧。合理配置命令行环境可以极大提升工作效率,让你成为真正的命令行高手!