Skip to content
On this page

Get started - Shell script

关于 Shell 脚本的一些学习笔记。其全部参考于Advanced Bash-Scripting Guide

TIP

部分参数解析参考工具网站:explainshell

Runtime

Docker compose

本次运行环境使用 Alpine 的 Docker 容器。其 Docker Compose 文件如下:

yaml
version: "3.7"

# Please refer to the official documentation:
# https://docs.docker.com/compose/compose-file/

networks:
  default:
    driver: bridge

services:
  linux:
    image: alpine:3.17
    working_dir: /opt/www
    volumes:
      - .:/opt/www
    networks:
      - default
    
    # /dev/null 代表 linux 的空设备文件,所有往这个文件里面写入的内容都会丢失,俗称“黑洞”。
    # 那么执行了 >/dev/null 之后,标准输出就会不再存在,没有任何地方能够找到输出的内容。
    # 该 entrypoint 命令是为了让容器始终保持在线状态
    entrypoint: ["tail", "-f", "/dev/null"]

注意事项

  • 在 Docker 容器中,直接启动该容器时,其用户变量未设置(ex:$UID$USER的值均为空)
  • /dev/null代表 linux 的空设备文件,所有往这个文件里面写入的内容都会丢失

Basic

关于 shell 脚本中常见的一些用法说明。

授权脚本

bash
# 授予所有用户对该脚本的「读取/执行」权限
$ chmod 555 scriptname
# 等同于
$ chmod +rx scriptname

# 仅授予该脚本的拥有者(owner)以对该脚本的「读取/执行」权限
$ chmod u+rx scriptname

变量

CAUTION

变量名不能使用 Shell 里的关键字(可通过help命令查看保留关键字)

TIP

可通过env命令查看用户环境变量;通过set命令查看Shell预定义变量+用户变量

shell
#!/bin/sh

# 设置变量
LOG_DIR=/var/log

# 使用变量
echo $LOG_DIR

Shell 变量

变量含义
$0当前脚本的文件名
$n传递给脚本或函数的参数。n 是一个数字,表示第几个参数。
例如,第一个参数是$1,第二个参数是$2
$#传递给脚本或函数的参数个数
$*传递给脚本或函数的所有参数。
$?上个命令的退出状态,或函数的返回值,成功会返回 0,失败返回非 0
$$当前 Shell 进程 ID,对于 Shell 脚本,就是这些脚本所在的进程 ID

输出


echo

TIP

详细信息请使用echo -h查看使用帮助。

shell
#!/bin/sh

# 字符串拼接(使用空格隔开即可)
# shell 脚本中存在大量预定义变量,例如 $USER 为当前用户名
echo "当前用户名:" $USER "当前UID:" $UID "当前GID:" $GID

# 将信息写入某文件
# 文件不存在时创建
# 存在时覆盖
echo "Some information..." > /var/log/tmp.txt

tail

TIP

详细信息请使用tail -h查看使用帮助。

shell
#!/bin/sh

# tail
# 输出文件 $filepath 的最后 $lines 行内容
lines=100
filepath=/var/log/tmp.log

tail -n $lines $filepath

一些条件表达式和运算符

shell
#!/bin/sh

T_NUMBER=1

# -eq 等于
OPERATOR="-eq 等于"
[ $T_NUMBER -eq 1 ] && echo "$OPERATOR: INTEGER1 is equal to INTEGER2" \
  || echo "INTEGER1 is not equal to INTEGER2"

# -ne 不等于
OPERATOR="-ne 等于"
[ $T_NUMBER -ne 0 ] && echo "$OPERATOR: INTEGER1 is not equal to INTEGER2" \
  || echo "INTEGER1 is equal to INTEGER2"

# -gt 大于
OPERATOR="-gt 大于"
[ $T_NUMBER -gt 0 ] && echo "$OPERATOR: INTEGER1 is greater than INTEGER2" \
  || echo "INTEGER1 is less than or equal to INTEGER2"

# -lt 小于
OPERATOR="-lt 小于"
[ $T_NUMBER -lt 2 ] && echo "$OPERATOR: INTEGER1 is less than INTEGER2" \
  || echo "INTEGER1 is greater than or equal to INTEGER2"

# -ge 大于等于
OPERATOR="-ge 大于等于"
[ $T_NUMBER -ge 1 ] && echo "$OPERATOR: INTEGER1 is greater than or equal to INTEGER2" \
  || echo "INTEGER1 is less than INTEGER2"

# -le 小于等于
OPERATOR="-le 小于等于"
[ $T_NUMBER -le 1 ] && echo "$OPERATOR: INTEGER1 is less than or equal to INTEGER2" \
  || echo "INTEGER1 is greater than INTEGER2"

分支结构 - if

if判断条件使用[]包裹,而不是常见编程语言中的圆括号。并且必须以fi结尾。

shell
#!/bin/sh

# 如果命令行参数不为空
# -n the length of STRING is nonzero
if [ -n "$1" ]; then
  echo "the length of STRING is nonzero: $1"
else
  echo "the length of STRING is zero"
fi

# 如果命令行参数为空
# -z the length of STRING is zero
if [ -z "$1" ]; then
  echo "the length of STRING is zero"
elif [ -n "$1" ]; then
  echo "the length of STRING is nonzero: $1"
else
  echo "It is not possible to reach the condition branch"
fi

# Can only exit with status 0-255. Other data should be written to stdout/stderr.
exit 0

Demo

文件远程复制

shell
#!/bin/bash

REMOTE_PATH="/data/www/runtime/"
LOCAL_PATH="/Users/$USER/logs"

ENV=test
if [ -n "$1" ]; then
    ENV=$1
fi

if [ "$ENV" == 'prod' ];then
    SERVER_USER=userName1
    SERVER_HOST=prod.ap-southeast-1.compute.amazonaws.com
elif [ "$ENV" == 'prev' ];then
    SERVER_USER=userName2
    SERVER_HOST=staging.ap-southeast-1.compute.amazonaws.com
else
    SERVER_USER=userName3
    SERVER_HOST=test.ap-east-1.compute.amazonaws.com
    REMOTE_PATH=/data/code/runtime/
fi

echo "当前拷贝环境:" $ENV " 正在拷贝日志文件至 " $LOCAL_PATH

rsync -avzrm --ignore-existing --include="*.log" --exclude="*.php" \
    $SERVER_USER@$SERVER_HOST:$REMOTE_PATH $LOCAL_PATH/$ENV

exit 0