Skip to content

shell脚本编程经典实例-JP沃森

Linux Shell 学习笔记


第二章 标准输出

基础操作

  • 去掉换行符
    Terminal window
    printf "%s %s" next prompt
    echo -n prompt
    echo -e 'hi\c' # 不支持,\c 需配合 echo -e 使用(部分 shell 不兼容)

输出重定向

  • 标准流编号

    • 0:标准输入 (STDIN)
    • 1:标准输出 (STDOUT)
    • 2:标准错误 (STDERR)
  • 将输出和错误发送到不同文件

    Terminal window
    myprogram > msg.out 2> msg.err
  • 将输出和错误发送到相同文件

    Terminal window
    both >& outfile # 旧版语法
    both &> outfile # 推荐语法
    both > outfile 2>&1 # 兼容性语法
  • 跳过文件标题

    Terminal window
    tail -n +2 file # 显示除第一行外的所有行
    tail -n +1 file # 显示所有行
  • 丢弃输出

    Terminal window
    nohup > /dev/null 2>&1 # 注意:原文 "noiay" 应为 "nohup"
  • 保存多个命令的输出

    Terminal window
    { pwd; ls; cd /tmp; ls; } > /tmp/all.out
    (pwd; ls; cd /tmp; ls) > /tmp/all.out
  • 输出作为输入,连接多个程序

    Terminal window
    cat /etc/passwd | sort
  • 输出作为输入并保留副本

    Terminal window
    cat my* | tr 'a-z' 'A-Z' | uniq | awk -f transform.awk | wc
    cat my* | tr 'a-z' 'A-Z' | uniq | tee /tmp/out.1 | awk -f transform.awk | wc
  • 将错误一并重定向到 tee 文件

    Terminal window
    find /etc '*.cnf' -print 2>&1 | tee /tmp/find.out
  • 以输出为参数连接两个程序

    Terminal window
    rm $(find . -name '*.class')
  • 重定向失败时保存输出

    Terminal window
    gcc bad.c 2> save.it # 屏幕不显示
    gcc bad.c 2>&1 | tee save.it # 屏幕显示
  • 将输出发送到日志文件,错误发送到错误文件

    Terminal window
    ./myscript 3>&1 1> out.log 2>&3- | tee -a err.log # -a 追加模式
  • 避免意外覆盖文件

    Terminal window
    set -o noclobber
  • 有意覆盖文件

    Terminal window
    echo something > my.file
    set -o noclobber
    echo some more >| my.file
    cat my.file

第三章 标准输入

从文件获取输入

Terminal window
wc < /etc/passwd

使用 Here Document

Terminal window
cat donors
# donors
grep -i $1 <<'EOF'
pete $100
joe $200
bill $ 9
EOF
./donors BILL

缩进(仅适用于制表符)

Terminal window
cat donors
# donors
grep -i $1 <<- 'EOF'
pete $100
joe $200
bill $ 9
EOF
ls

获取用户输入

Terminal window
read -p "your name " ANSWER
read -t 5 -p "your name " ANSWER # 5秒超时
echo $ANSWER

获取 Yes/No 输入

Terminal window
function choice {
CHOICE=''
local prompt="$*"
local answer
read -p "$prompt" answer
case "$answer" in
[yY1] ) CHOICE='y' ;;
[nN0] ) CHOICE='n' ;;
* ) CHOICE="$answer" ;;
esac
}
choice "Do you want to look at the error logfile?[y/n]: "
if [ "$CHOICE" != "n" ]; then
less error.log
fi
choice "Do you want to look at the message logfile?[y/n]: "
if [ "$CHOICE" = "y" ]; then
less message.log
fi

选择选项列表

Terminal window
directorylist="Finished $(for i in /*; do [ -d "$i" ] && echo $i; done)"
PS3='Directory to process? '
select directory in $directorylist; do
if [ "$directory" == "Finished" ]; then
echo "Finished processing directories."
break
elif [ -n "$directory" ]; then
echo "You chose number $REPLY, processing $directory ..."
break
else
echo "Invalid selection!"
fi
done

提升输入密码

Terminal window
read -s -p "password: " PASSWD
printf "%b\n"

第四章 执行命令

命令执行顺序

  • 依次执行
    Terminal window
    long; medium; short
  • 前一个成功后执行
    Terminal window
    long && medium && short

同时执行多个命令

Terminal window
long & medium & short

检查命令是否成功

Terminal window
echo $? # 0 表示成功
if (( $? )); then echo failed; else echo ok; fi

条件执行

Terminal window
if cd mytmp; then rm *; fi
cd mytmp && rm *

无人值守运行任务

Terminal window
nohup long &

出现故障时退出

Terminal window
cmd || { printf "%b\n" "Failed."; exit 1; }

执行变量中的命令

Terminal window
FN=/tmp/x.x
PROG=echo
$PROG $FN
PROG=cat
$PROG $FN

执行目录中的所有脚本

Terminal window
for SCRIPT in /path/to/scripts/*; do
if [ -f "$SCRIPT" -a -x "$SCRIPT" ]; then
$SCRIPT
fi
done

第五章 脚本编程基础:Shell 变量

变量操作

  • 替换字符串

    Terminal window
    NEWPATH=${PATH/:/ } # 替换第一次出现的冒号
    NEWPATH=${PATH//:/ } # 替换所有冒号
  • 提高可读性

    Terminal window
    [ -n "$results" ] && echo "Got a good $results" || echo 'Got an empty result'
  • 引用变量

    Terminal window
    for FN in 1 2 3 4 5; do
    somescript /tmp/rep${FN}port.txt
    done
  • 导出变量

    Terminal window
    export FNAME=/tmp/scratch
    export SIZE=64
  • 查看变量

    Terminal window
    set
    env
    export -p
    declare -p

脚本参数

  • 访问参数

    Terminal window
    echo $1 $10 ${10}
  • 遍历参数

    Terminal window
    for FN in "$@"; do
    echo changing $FN
    file "$FN"
    done
  • 处理带空格的参数

    Terminal window
    ls -l "${1}"
    touch /tmp/'oh the waste'
    ./t.sh "oh the waste"
  • 统计参数数量

    Terminal window
    if (( $# < 3 )); then
    printf "%b\n" "Error. Not enough arguments." >&2
    exit 1
    fi

字符串操作

  • 子串操作

    Terminal window
    name="number:123"
    echo ${name#*:} # 从冒号后开始截取
    echo ${name##*:} # 截取最后一个冒号后的内容
    echo ${name%:*} # 从右边删除第一个冒号及之后内容
    echo ${name%%:*} # 删除所有冒号及之后内容
  • 替换字符串

    Terminal window
    name="hello-world"
    echo ${name/hello/hi} # 替换第一次出现的 hello
    echo ${name//hello/hi} # 替换所有 hello
  • 文件路径操作

    Terminal window
    MYIMAGEFILE=/tmp/img/img1.gif
    echo ${MYIMAGEFILE%/} # 删除结尾斜杠
    echo ${MYIMAGEFILE%/*} # 删除结尾斜杠及之后的内容
    echo ${MYIMAGEFILE##*/} # 获取文件名
    echo ${MYIMAGEFILE%.gif} # 删除后缀
  • 数组操作

    Terminal window
    MYVAR=(first second third home)
    echo runners on ${MYVAR[0]} and ${MYVAR[2]}
  • 大小写转换

    Terminal window
    MYVAR="HELLO"
    echo ${MYVAR,,} # 全小写
    echo ${MYVAR^^} # 全大写
  • 驼峰命名法

    Terminal window
    while read TXT; do
    RA="${TXT,,}"
    RA2=($RA)
    echo ${RA2[@]^}
    done

第六章 Shell 逻辑与算术

算术操作

  • 整数运算

    Terminal window
    X=8; Y=9
    x=$((X+=5, Y*=3))
    echo $X $Y $x
  • 运算符

    = 赋值 a=b
    *= 乘法 a*=b
    /= 除法 a/=b
    %= 求余 a%=b
    += 加法 a+=b
    -= 减法 a-=b
    <<= 左移 a<<=b
    >>= 右移 a>>=b
    &= 与 a&=b
    ^= 异或 a^=b
    |= 或 a|=b

条件分支

Terminal window
if [ -r $file -a -w $file ]; then
echo "File is readable and writable"
fi

循环

Terminal window
for ((i=0; i<10; i++)); do
echo $i
done

多路分支

Terminal window
case $FN in
*.gif) gif2png "$FN" ;;
*.png) pngOK "$FN" ;;
*) echo "Unsupported file: $FN" ;;
esac

批量操作

  • 批量重命名

    Terminal window
    for FN in *.jpg; do mv "$FN" "${FN%jpg}png"; done
  • 批量解压

    Terminal window
    unzip '*.zip'
  • 搜索但不包含 grep 进程

    Terminal window
    ps aux | grep '[s]sh'
  • 安全删除文件

    Terminal window
    [ -n "$files_delete" ] && rm -rf "$files_delete"

测试脚本语法

Terminal window
bash -n t.sh