Jacky's blog
首页
  • 学习笔记

    • web
    • android
    • iOS
    • vue
  • 分类
  • 标签
  • 归档
收藏
  • tool
  • algo
  • python
  • java
  • server
  • growth
  • frida
  • blog
  • SP
  • more
GitHub (opens new window)

Jack Yang

编程; 随笔
首页
  • 学习笔记

    • web
    • android
    • iOS
    • vue
  • 分类
  • 标签
  • 归档
收藏
  • tool
  • algo
  • python
  • java
  • server
  • growth
  • frida
  • blog
  • SP
  • more
GitHub (opens new window)
  • shell

    • shell 入门指南
      • Variable
      • common
        • config
        • 父子 shell
        • path
        • 进程替换
        • 变量扩展语法
        • Here Tag
      • 数组操作
        • 数组定义和访问
        • 关联数组 (Bash 4+)
      • 函数定义
        • 基本函数语法
        • 函数参数处理
      • 错误处理
        • 基本错误处理
        • 错误重定向
      • 作业控制
        • 后台任务
        • 信号处理
      • 输入验证
        • 参数验证
        • 输出重定向
        • IFS
      • test 命令
      • string
      • 输出着色
      • 性能优化
        • 避免外部命令调用
        • 使用内置功能
        • 批量操作
      • 最佳实践
        • 脚本头部
        • 变量引用
        • 错误处理
        • 临时文件处理
      • 调试技巧
        • 调试选项
        • 日志记录
      • 常用工具函数
        • 文件操作
        • 字符串处理
      • link
    • linux 入门指南
    • Shell 常用命令速查手册
    • Shell 代码片段集合
    • awk
    • fd
    • ftp
    • sftp
    • ifconfig
    • ssh
    • sed
    • xargs
  • tool

  • 网络

  • algo

  • compute_base

  • blog

  • growth

  • java

  • C&C++

  • ai

  • secure

  • cms

  • english

  • 生活

  • 金融学

  • more

  • other
  • shell
Jacky
2023-06-04
目录

shell 入门指南

# Variable

  • $HOME: 用户主目录
  • $0: 脚本被调用时使用的名称(取决于调用方式:相对路径、绝对路径或文件名)
    • 示例:./script.sh → $0 = ./script.sh;/path/to/script.sh → $0 = /path/to/script.sh
    • 获取纯文件名:basename "$0" 或 ${0##*/}
    • 获取完整路径:realpath "$0" 或 readlink -f "$0"(Linux)
  • $1, $2, ...: 脚本的参数,$1是第一个参数,$2是第二个参数
  • $#: 传递给脚本的参数个数
  • $@: 所有参数的列表,每个参数都用双引号包围
  • $_: 一个特殊变量,通常用于引用前一个命令的最后一个参数
    • mkdir -p /path/to/directory && cd $_: 切到目标目录,没有此目录则创建
  • $*: 所有参数的列表,作为一个字符串
  • $$: 当前进程的PID
  • $?: 上一个命令的退出状态(0表示成功,非0表示失败)
  • $!: 最后一个后台进程的PID
  • $PWD: 当前工作目录
  • $USER: 当前用户名
  • $SHELL: 当前shell的路径
  • $HOSTNAME: 主机名(可能为空,建议使用 hostname 命令)
    • 获取主机名的替代方法:
      • hostname: 显示主机名
      • hostnamectl: 显示主机名和系统信息
      • uname -n: 显示网络节点名
      • cat /etc/hostname: 读取主机名文件
  • $RANDOM: 0到32767之间的随机数
  • $SECONDS: 脚本运行的时间(秒)
  • $LINENO: 当前行号
  • $FUNCNAME: 当前函数名
  • $BASH_SOURCE: 当前脚本的源文件名
  • $BASH_LINENO: 当前行号数组
  • $BASH_ARGC: 参数个数数组
  • $BASH_ARGV: 参数值数组

# common

  • printf "%x\n" <102>: 转成 16 进制
  • mktemp: 创建临时文件
  • basename: 提取路径中的文件名
  • dirname: 提取路径中的目录名
  • readlink -f: 获取符号链接的真实路径
  • which: 查找命令的完整路径
  • type: 显示命令的类型和位置
  • command -v: 查找命令路径(POSIX兼容)

# config

  • set -o vi: bash vi 模式. 后续光标移动就跟 vi 模式一样了。 vim 光标

# 父子 shell

  • 在 shell 脚本中, 你可以运行一系列命令, 这些命令按照你编写的顺序执行。每个命令都在父 Shell 的上下文中执行

  • 如果你使用管道 | 来连接命令, 例如 cmd1 | cmd2 | cmd3, 这表示 cmd1 的输出会成为 cmd2 的输入, cmd2 的输出会成为 cmd3 的输入

  • 圆括号 () 可以用来创建一个子 Shell, 其中可以包含一系列命令。子 Shell 是在父 Shell 内部运行的, 但它有自己的独立环境, 包括变量。如果你在子 Shell 中执行 cd / 命令来改变工作目录, 这个改变只会在子 Shell 内部生效, 不会影响到父 Shell 或后续的命令。子 Shell 中定义的变量也是局部变量, 只在子 Shell 内部可见

  • 最后, 子 Shell 在括号 () 内部的命令序列会被依次执行, 然后它的输出可以被父 Shell 或后续命令使用

这个概念对于在 shell 脚本中控制命令的执行流程和作用域非常重要。子 Shell 可以用来隔离一些操作, 以免影响到整个脚本的环境

# path

; 这会将 $ANDROID_BUILD_TOOL/bin 路径添加到当前 PATH 环境变量的末尾. 如果 $ANDROID_BUILD_TOOL 变量未定义,这将不起作用,或可能会导致错误
export PATH=$PATH:$ANDROID_BUILD_TOOL/bin

; 这会将 /usr/local/opt/postgresql@16/bin 路径添加到当前 PATH 环境变量的开头。这样做的好处是,新的路径会优先于其他路径
export PATH="/usr/local/opt/postgresql@16/bin:$PATH"
1
2
3
4
5

# 进程替换

进程替换:将命令的输出转换为临时文件,使那些只能接受文件作为参数的命令也能使用命令输出。 <(...) 语法是进程替换,允许将命令输出作为文件传递给另一个命令

# 比较两个命令的输出
diff <(ls /path1) <(ls /path2)

# 将命令输出作为文件处理
cat <(echo "Hello World")

# 进程替换与管道结合
paste <(cut -d: -f1 /etc/passwd) <(cut -d: -f3 /etc/passwd)
1
2
3
4
5
6
7
8

# 变量扩展语法

  1. ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}: ${ZSH_CUSTOM:-~/.oh-my-zsh/custom} 是一种 Shell 变量扩展语法, 用于在命令中引用一个变量。在这个特定的命令中, ${ZSH_CUSTOM:-~/.oh-my-zsh/custom} 表示一个变量, 它的值是 $ZSH_CUSTOM 的值, 如果 $ZSH_CUSTOM 未定义, 则使用默认值 ~/.oh-my-zsh/custom

  2. remote_name=${1:-origin}: 如果没有第一个参数, 则设置则使用默认值 origin

更多变量扩展语法:

  • ${var:=default}: 如果变量未设置或为空,则设置为默认值
  • ${var:?message}: 如果变量未设置或为空,则显示错误信息并退出
  • ${var:+alternative}: 如果变量已设置且非空,则使用替代值
  • ${var#pattern}: 删除最短匹配的前缀
  • ${var##pattern}: 删除最长匹配的前缀
  • ${var%pattern}: 删除最短匹配的后缀
  • ${var%%pattern}: 删除最长匹配的后缀

# Here Tag

参考文档: Here Documents (opens new window)

在 Linux 中, <<(称为 Here Document 或 Here Tag)是一种用于输入多行文本的特殊语法。它允许将一段文本作为输入传递给命令或脚本, 而无需使用外部文件

具体来说, <<后面可以跟一个标识符(tag), 用于标识输入的结束。输入文本的开始和结束都由这个标识符来界定。使用<<时, 输入的文本会被 shell 进程处理, 然后传递给相应的命令或脚本

示例
cat << END
这是一段
多行文本输入,
会被cat命令处理
END
1
2
3
4
5

在上面的示例中, cat 命令接收<< END 和 END 之间的多行文本作为输入, 并将其输出到标准输出

Here Document 的用途包括但不限于:

  • 在脚本中嵌入大段的文本数据
  • 在命令行中直接输入多行文本, 而无需创建临时文件
  • 在配置文件或模板中插入变量和文本

需要注意的是, tag 可以是用户自定义的标识符, 只要它在输入文本中没有被使用即可。通常, tag 以大写字母开头, 但并不是强制要求


cat 多行文本写入
cat << END>config.pbtx
这是一段
多行文本输入,
会被 cat 命令处理
END
1
2
3
4
5

作用解释如下:

  • << END 开始了一个 Here Document 区块, END 是结束标记。你可以自行选择合适的结束标记, 只要它在文本中没有出现即可
  • 在开始标记和结束标记之间的所有文本都将被传递给 cat 命令
  • 这个多行文本被写入名为 config.pbtx 的文件

三、赋值多行字符串给变量

info=$(cat << 'EOF'
这是一段
多行文本输入,
会被cat命令处理
EOF
)

echo "$info"
1
2
3
4
5
6
7
8

# 数组操作

# 数组定义和访问

# 定义数组
arr=("apple" "banana" "cherry")

# 访问数组元素
echo "${arr[0]}"    # 第一个元素
echo "${arr[@]}"    # 所有元素
echo "${#arr[@]}"   # 数组长度

# 添加元素
arr+=("date")

# 遍历数组
for item in "${arr[@]}"; do
    echo "$item"
done
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 关联数组 (Bash 4+)

# 声明关联数组
declare -A colors
colors["red"]="#FF0000"
colors["green"]="#00FF00"

# 访问
echo "${colors["red"]}"

# 遍历
for key in "${!colors[@]}"; do
    echo "$key: ${colors[$key]}"
done
1
2
3
4
5
6
7
8
9
10
11
12

# 函数定义

# 基本函数语法

# 函数定义
function_name() {
    local var1="$1"    # 第一个参数
    local var2="$2"    # 第二个参数
    
    # 函数体
    echo "参数1: $var1, 参数2: $var2"
    
    # 返回值
    return 0
}

# 调用函数
function_name "hello" "world"

# 获取函数返回值
echo "退出状态: $?"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 函数参数处理

process_files() {
    local count=0
    for file in "$@"; do
        if [ -f "$file" ]; then
            echo "处理文件: $file"
            ((count++))
        fi
    done
    echo "共处理 $count 个文件"
}

# 调用
process_files *.txt
1
2
3
4
5
6
7
8
9
10
11
12
13

# 错误处理

# 基本错误处理

# 启用错误时退出
set -e

# 启用管道错误退出
set -o pipefail

# 捕获错误
if ! command_that_might_fail; then
    echo "命令执行失败" >&2
    exit 1
fi
1
2
3
4
5
6
7
8
9
10
11

# 错误重定向

# 捕获错误输出
error_output=$(command 2>&1)

# 忽略错误
command 2>/dev/null || true

# 记录错误到文件
command 2>> error.log
1
2
3
4
5
6
7
8

# 作业控制

# 后台任务

# 后台运行命令
command &

# 查看后台任务
jobs

# 将任务移到前台
fg %1

# 将任务移到后台
bg %1

# 杀死任务
kill %1
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 信号处理

# 信号处理函数
cleanup() {
    echo "收到退出信号,清理中..."
    rm -f /tmp/temp_file
    exit 0
}

# 注册信号处理
trap cleanup SIGINT SIGTERM
1
2
3
4
5
6
7
8
9

# 输入验证

# 参数验证

# 检查参数数量
if [ $# -lt 2 ]; then
    echo "用法: $0 <参数1> <参数2>" >&2
    exit 1
fi

# 检查文件是否存在
if [ ! -f "$1" ]; then
    echo "错误: 文件 '$1' 不存在" >&2
    exit 1
fi

# 检查目录是否可写
if [ ! -w "$(dirname "$output")" ]; then
    echo "错误: 无法写入目录" >&2
    exit 1
fi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 输出重定向

  • <command> 2>/dev/null: 将 stderr 重定向到 /dev/null, 从而禁止错误信息的显示
  • <command> > /dev/null 2>&1: 将 stdout 和 stderr 都重定向到/dev/null, 使得没有输出到终端
`2>&1` 的说明

是一种 Shell 中的标准输出(stdout)和标准错误输出(stderr)重定向的语法。这个语法的作用是将标准错误输出重定向到标准输出, 这样两者都将合并在一起输出到同一个地方

具体解释如下:
  • 2 表示标准错误输出(stderr)的文件描述符
  • > 是重定向操作符, 用于将输出重定向到指定位置
  • &1 表示标准输出(stdout)的文件描述符。这里的&表示引用

# IFS

IFS(Internal Field Separator). It is an environment variable in shell scripting that determines how the shell interprets word boundaries when parsing input. 用在一行行读取文本

读取文件中内容 line by line
while IFS= read -r path; do
  if [ -z "$path" ]; then
    continue
  fi
done < "$input_file"
1
2
3
4
5

By setting IFS= (to an empty value) before read -r, the command ensures that:

  1. Whitespace (spaces and tabs) is not treated as a delimiter. This is useful when you want to read lines that may contain spaces or other whitespace characters as part of the input rather than splitting them into separate fields.
  2. Entire line is read as a single entry into the variable (path in this case).
  3. -r option in read prevents backslashes from being interpreted as escape characters, which helps in reading file paths or other strings that may contain special characters.
split string with blank
while IFS=' ' read -r path; do
  echo "$path"
done <<< "abc ddd eee fff"
1
2
3

# test 命令

test command reference (opens new window)

  • 数值测试: -eq|-ne|-gt|-ge|-lt|-le
  • 字符串测试: -z|-n|=|!=
  • 文件测试: -e|-r|-w|-x|-s|-d|-f|-c|-b

常用测试示例:

# 文件存在性检查
if [ -f "$file" ]; then
    echo "File exists"
fi

# 目录检查
if [ -d "$dir" ]; then
    echo "Directory exists"
fi

# 字符串非空检查
if [ -n "$var" ]; then
    echo "Variable is not empty"
fi

# 数值比较
if [ "$num1" -gt "$num2" ]; then
    echo "$num1 is greater than $num2"
fi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# string

字符串处理, 更多可见 (opens new window)

操作
  • ${#string}: 求$string的长度
  • ${string:position}: 在$string 中, 从位置$position开始提取子串
  • ${string:position:length}: 在$string 中, 从位置$position 开始提取长度为$length 的子串
  • ${string#substring}: 从变量$string 的开头, 删除最短匹配$substring 的子串
  • ${string##substring}: 从变量$string 的开头, 删除最长匹配$substring 的子串.
    • 如在脚本中使用 ${0##*/}: $0 是脚本被调用时使用的名称(可能是相对路径、绝对路径或只是文件名,取决于调用方式)。${0##*/} 的效果是:从左边删除最长匹配 */ 的部分,提取出纯文件名。
  • ${string%substring}: 从变量$string 的结尾, 删除最短匹配$substring 的子串
  • ${string%%substring}: 从变量$string 的结尾, 删除最长匹配$substring 的子串
  • ${string/substring/replacement}: 使用$replacement, 来代替第一个匹配的$substring
  • ${string//substring/replacement}: 使用$replacement, 代替所有匹配的$substring
  • ${string/#substring/replacement}: 如果$string 的前缀匹配$substring, 那么就用$replacement 来代替匹配到的$substring
  • ${string/%substring/replacement}: 如果$string 的后缀匹配$substring, 那么就用$replacement 来代替匹配到的$substring
case
  • output_file="${file%.*}_compressed.jpg"

这是一个字符串操作, 用于删除字符串的后缀。在这里, %.* 表示删除字符串中最右边的一个.(包括.本身)及其之后的所有字符。因此, ${file%.*} 将给出文件名的部分, 去除了扩展名

# 输出着色


# 带颜色的输出函数, 使用 ANSI 转义码来实现带颜色的输出
color_echo() {
  local color="$1"
  local message="$2"
  echo -e "\033[${color}m${message}\033[0m"
}

# 红色文本
red() {
  local message="$1"
  color_echo "0;31" "$message"
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 性能优化

# 避免外部命令调用

# 慢:使用外部命令
count=$(wc -l < file.txt)

# 快:使用内置功能
while IFS= read -r line; do
    ((count++))
done < file.txt
1
2
3
4
5
6
7

# 使用内置功能

# 避免使用 cat
cat file.txt | grep "pattern"  # 慢

# 直接重定向
grep "pattern" file.txt        # 快
1
2
3
4
5

# 批量操作

# 避免循环中的重复操作
for file in *.txt; do
    # 重复的预处理
done

# 一次性处理
files=(*.txt)
# 预处理一次
1
2
3
4
5
6
7
8

# 最佳实践

# 脚本头部

#!/bin/bash
set -euo pipefail  # 严格模式
IFS=$'\n\t'       # 安全的IFS设置
1
2
3

# 变量引用

# 总是引用变量
echo "$variable"     # 正确
echo $variable       # 可能有问题

# 数组引用
echo "${array[@]}"   # 正确
1
2
3
4
5
6

# 错误处理

# 检查命令是否成功
if ! command; then
    echo "命令失败: $?" >&2
    exit 1
fi

# 使用 || 提供默认值
result=$(command 2>/dev/null) || result="default"
1
2
3
4
5
6
7
8

# 临时文件处理

# 创建临时文件
temp_file=$(mktemp)
trap "rm -f $temp_file" EXIT

# 使用临时文件
echo "data" > "$temp_file"
1
2
3
4
5
6

# 调试技巧

# 调试选项

# 启用调试模式
set -x    # 显示执行的命令
set -v    # 显示读取的输入

# 在脚本中局部调试
set -x
# 调试代码
set +x
1
2
3
4
5
6
7
8

# 日志记录

# 创建日志函数
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >&2
}

log "开始处理文件"
1
2
3
4
5
6

# 常用工具函数

# 文件操作

# 安全备份
backup_file() {
    local file="$1"
    if [ -f "$file" ]; then
        cp "$file" "${file}.bak.$(date +%Y%m%d_%H%M%S)"
    fi
}

# 检查文件是否可写
is_writable() {
    local file="$1"
    [ -w "$(dirname "$file")" ] && [ ! -f "$file" -o -w "$file" ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 字符串处理

# 去除前后空格
trim() {
    local var="$1"
    var="${var#"${var%%[![:space:]]*}"}"  # 去除前导空格
    var="${var%"${var##*[![:space:]]}"}"  # 去除尾随空格
    echo "$var"
}

# 转换为小写
to_lower() {
    echo "${1,,}"
}

# 转换为大写
to_upper() {
    echo "${1^^}"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# link

  • runoob.com (opens new window) linux shell 学习
    • 运算符 (opens new window) : -z 等
    • Shell 流程控制 (opens new window)
  • Bash Guide (opens new window) - 全面的Bash指南
  • ShellCheck (opens new window) - Shell脚本静态分析工具
  • Google Shell Style Guide (opens new window) - Shell编程规范
#shell#tutorial
上次更新: 2025/10/21, 17:41:45
linux 入门指南

linux 入门指南→

最近更新
01
npx 使用指南
10-12
02
cursor
09-28
03
inspect
07-20
更多文章>
Theme by Vdoing | Copyright © 2019-2025 Jacky | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式