shell和markdown的实用技巧

229 阅读2分钟

概述

无论什么语言,在大型项目中,shell和markdown都是最常用的脚本构建工具,本文从kata-containers的项目脚本角度,学习shell和markdown的实用技巧。

tricks

shell

set

set -o errexit / set -e # 失败则退出模式
set -o pipefail # | 管道只返回最后一个命令的成功状态,在兼容多发行版时很有用
set -o errtrace # 此选项意味着如果脚本中任何命令失败,而且该命令在函数中或与函数相关的子shell中,则会触发ERR陷阱。基本上,这个选项可以让你捕获在函数内部发生的错误。
set +e # 失败不退出模式,一般只是临时开启

var

DOCKER_RUNTIME=${DOCKER_RUNTIME:-runc} # 默认值
export GOPATH=${GOPATH:-${HOME}/go} # env,方便传递给其他脚本
local exit_code="${?}" # 内部变量,只能用在function
typeset -r CONFIG_SH="config.sh" # 声明只读
declare -r CONFIG_SH="config.sh"

local -a bin_dirs=(
    "/bin"
    "/usr/bin"
    "/usr/local/bin"
)

脚本执行模式

  • source test.sh: 以当前shell环境执行脚本,因此语句副作用会在执行完成后留下,一般用于刷新PATH等变量
  • . test.sh: 本质上和source相同
  • bash test.sh: 使用bash解释器执行脚本,而不是默认的sh指向的解释器,不需要x权限,使用子环境
  • /abs/test.sh: 需要脚本有x权限,使用子环境
  • ./test.sh: 本质上和绝对路径相同

include script

lib_file="${script_dir}/../scripts/lib.sh"
source "$lib_file"

简单语句避免写if语句

[ "${TARGET_ARCH}" == "aarch64" ] && TARGET_ARCH=arm64 # 成功则执行
[ -n "${distro}" ] || usage 1 # 失败则执行

trap / handle err

打印出发生错误的行号。这可以帮助你在调试脚本时定位问题所在。

handle_error() {
	local exit_code="${?}"
	local line_number="${1:-}"
	echo "Failed at $line_number: ${BASH_COMMAND}"
	exit "${exit_code}"

}
trap 'handle_error $LINENO' ERR

trap after_stopping_container EXIT

常用判断器

  • [ -n ]
  • [ -d ]
  • [ -z ]
  • [ -e ]
  • [ -x ]
  • [ -eq ] [ == ]
  • [ -ne ]

检测命令

if command -v selinuxenabled > /dev/null; then

set +e
which ${python_cmd} > /dev/null 2>&1
if [ $? -eq 0 ]; then
  my_python=${python_cmd}
fi

pushd/popd

pushd/popd 相比 cd 更加好用

解析参数

parse_arguments()
{
	[ "$#" -eq 0 ] && usage && return 0

	while getopts a:hlo:r:t: opt
	do
		case $opt in
			a)	AGENT_VERSION="${OPTARG}" ;;
			h)	usage ;;
			l)	get_distros | sort && exit 0;;
			o)	OSBUILDER_VERSION="${OPTARG}" ;;
			r)	ROOTFS_DIR="${OPTARG}" ;;
			t)	get_test_config "${OPTARG}" && exit 0;;
			*)  die "Found an invalid option";;
		esac
	done

	shift $(($OPTIND - 1))
	distro="$1"
	arch=$(uname -m)
}

main

main()
{
	parse_arguments $*
    other_func
}

main $*

markdown

include

include utils.mk
include ./tools/packaging/kata-deploy/local-build/Makefile

var

COMPONENTS =

COMPONENTS += libs
COMPONENTS += agent

STANDARD_TARGETS = build check clean install

KATA_DEBUG_REGISTRY ?= ""

伪目标

makefile默认以文件作为目标,并构建出一条文件依赖链。伪目标则不是文件,更像是一条命令,必须被用户显式执行。

.PHONY: \
	all \
	kata-tarball \
	install-tarball \
	default \
	static-checks \
	docs-url-alive-check

高级:宏 example

# 示例参数
ITEMS = item1 item2
PLATFORMS = platform1 platform2

# 初始化一个空变量用来存储所有目标
ALL_TARGETS =

# 定义一个宏,它用来根据参数创建规则
define create_rule
ALL_TARGETS += $(1)_$(2)
$(1)_$(2):
	echo "Building $(1) for $(2)"
endef

define create_rule_list
$(foreach c,$(1),$(eval $(call create_rule_list2,$(c),$(2))))
endef

define create_rule_list2
$(foreach c,$(2),$(eval $(call create_rule,$(1),$(c))))
endef

$(foreach item, $(ITEMS), \
  $(foreach platform, $(PLATFORMS), \
    $(eval $(call create_rule,$(item),$(platform))) \
  ) \
)

# $(eval $(call create_rule_list,$(ITEMS),$(PLATFORMS)))

.PHONY: all
all: $(ALL_TARGETS)

.PHONY: show-targets
show-targets:
	echo $(ALL_TARGETS)

调试

# -n 表示只打印执行的命令,而不实际执行
make -n