命令行环境概述
命令行环境不仅仅是输入命令的地方,它是一个强大的工作空间。通过合理配置命令行环境,可以极大提升工作效率。
什么是命令行环境
命令行环境包括:
- Shell 配置文件:定义环境的行为和外观
- 环境变量:存储系统级别的配置信息
- 别名和函数:自定义快捷命令
- 命令历史:记录之前执行的命令
- 作业控制:管理前台和后台进程
- 终端复用器:在单个终端中管理多个会话
Shell 配置文件
Bash 配置文件加载顺序详解
当 Bash 启动时,会按照以下顺序查找并加载配置文件:
/etc/profile # 系统级配置(所有用户共享)
↓
~/.bash_profile # 用户登录配置(优先)
↓ 如果不存在
~/.bash_login # 备用登录配置
↓ 如果不存在
~/.profile # 备用登录配置(适用于多种 Shell)
↓
~/.bashrc # 交互式非登录 Shell 配置
↓
/etc/bash.bashrc # 系统级交互式配置
加载规则:
登录 Shell(如
ssh user@host、login命令):- 首先执行
/etc/profile(系统级) - 然后依次查找
~/.bash_profile→~/.bash_login→~/.profile,只执行第一个找到的 - 通常会在登录配置中通过
source ~/.bashrc加载交互式配置
- 首先执行
非登录 Shell(如终端窗口、直接输入
bash):- 只执行
~/.bashrc
- 只执行
脚本执行(如
bash script.sh):- 默认不加载任何配置文件
- 使用
bash -l script.sh强制作为登录 Shell 执行
登录 Shell vs 非登录 Shell
| 类型 | 启动方式 | 加载配置 | 用途 |
|---|---|---|---|
| 登录 Shell | ssh user@host、login、su - user | .bash_profile → .bashrc | 远程登录、系统启动 |
| 非登录 Shell | 终端窗口、bash、su 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_profile | PATH、环境变量 |
| 系统级配置 | /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 |
PYTHONPATH | Python 模块搜索路径 | ~/projects |
JAVA_HOME | Java 安装目录 | /usr/lib/jvm/java-11 |
GOPATH | Go 工作目录 | ~/go |
NODE_PATH | Node.js 模块搜索路径 | /usr/lib/node_modules |
MANPATH | 手册页搜索路径 | /usr/share/man |
DISPLAY | X Window 显示服务器 | :0 |
SSH_AUTH_SOCK | SSH 代理套接字 | /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退出搜索 - 按
Tab或Ctrl + 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 - 继续
常用信号表:
| 信号 | 编号 | 说明 |
|---|---|---|
SIGHUP | 1 | 终端断开连接 |
SIGINT | 2 | 用户中断(Ctrl + C) |
SIGQUIT | 3 | 用户退出(Ctrl + \),产生 core dump |
SIGKILL | 9 | 强制终止(不可捕获) |
SIGTERM | 15 | 优雅终止(默认) |
SIGSTOP | 19 | 暂停进程(不可捕获) |
SIGCONT | 18 | 继续暂停的进程 |
SIGUSR1 | 10 | 用户自定义信号 1 |
SIGUSR2 | 12 | 用户自定义信号 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-cpu | CPU 使用率显示 |
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
练习
基础练习
- 修改
~/.bashrc添加自定义别名和函数 - 设置自定义命令提示符,显示用户名、主机名和当前目录
- 使用
tmux创建多个窗口和窗格 - 配置 SSH config 文件连接到远程服务器
进阶练习
- 创建一个函数,自动创建项目目录结构
- 使用
fzf实现快速文件查找和打开 - 设置 Git 分支信息在命令提示符中显示
- 配置 SSH 端口转发访问远程服务
- 编写一个备份脚本,自动备份指定目录到远程服务器
综合练习
- 使用
tmux管理多个开发会话,包含编辑器、服务器、日志窗口 - 配置远程开发环境,实现代码同步和自动部署
- 编写完整的 Shell 脚本,实现自动化部署流程
- 配置终端配色主题和快捷键,打造个性化开发环境
总结
配置文件
| 文件 | 用途 |
|---|---|
~/.bashrc | Bash 交互式配置 |
~/.bash_profile | Bash 登录配置 |
~/.zshrc | Zsh 配置 |
~/.ssh/config | SSH 配置 |
~/.tmux.conf | tmux 配置 |
环境变量
| 变量 | 用途 |
|---|---|
PATH | 命令搜索路径 |
EDITOR | 默认编辑器 |
PS1 | 命令提示符 |
HISTCONTROL | 历史记录控制 |
快捷键
| 快捷键 | 功能 |
|---|---|
Ctrl + R | 反向搜索历史 |
Ctrl + Z | 暂停任务 |
Ctrl + D | 退出 Shell |
Ctrl + L | 清屏 |
终端复用器
| 工具 | 特点 |
|---|---|
tmux | 现代、功能强大、活跃维护 |
screen | 传统、兼容性好 |
SSH 高级特性
| 特性 | 说明 |
|---|---|
| Agent Forwarding | 在远程服务器上使用本地密钥 |
| ControlMaster | 连接复用,提升连接速度 |
| Port Forwarding | 端口转发,访问内网服务 |
| ProxyJump | 跳板机配置 |
通过这一课的学习,你已经掌握了命令行环境的高级配置技巧。合理配置命令行环境可以极大提升工作效率,让你成为真正的命令行高手!