数百万开发人员编写shell脚本来解决各种类型的任务.我使用shell脚本来简化部署,生命周期管理,安装或简单地作为粘合语言.
我注意到的是没有人真正关心shell脚本的风格和质量.很多团队花了很多时间修复Java,C++,...样式问题,但完全忽略了shell脚本中的问题.顺便说一句,通常没有标准的方法来在特定项目中实现shell脚本,因此可以在代码库中找到许多不同的,丑陋的和错误的脚本.
为了在我的项目中克服这个问题,我决定创建一个通用且足够好的shell脚本模板.我会按原样提供我的模板,以使这个问题更有用.开箱即用的这些模板提供:
参数处理:getopts(最新版本:shell-script-template @ github)
#!/bin/bash
# ------------------------------------------------------------------
# [Author] Title
# Description
# ------------------------------------------------------------------
VERSION=0.1.0
SUBJECT=some-unique-id
USAGE="Usage: command -ihv args"
# --- Options processing -------------------------------------------
if [ $# == 0 ] ; then
echo $USAGE
exit 1;
fi
while getopts ":i:vh" optname
do
case "$optname" in
"v")
echo "Version $VERSION"
exit 0;
;;
"i")
echo "-i argument: $OPTARG"
;;
"h")
echo $USAGE
exit 0;
;;
"?")
echo "Unknown option $OPTARG"
exit 0;
;;
":")
echo "No argument value for option $OPTARG"
exit 0;
;;
*)
echo "Unknown error while processing options"
exit 0;
;;
esac
done
shift $(($OPTIND - 1))
param1=$1
param2=$2
# --- Locks -------------------------------------------------------
LOCK_FILE=/tmp/$SUBJECT.lock
if [ -f "$LOCK_FILE" ]; then
echo "Script is already running"
exit
fi
trap "rm -f $LOCK_FILE" EXIT
touch $LOCK_FILE
# --- Body --------------------------------------------------------
# SCRIPT LOGIC GOES HERE
echo $param1
echo $param2
# -----------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)
Shell Flags(shFlags)允许简化命令行参数处理,因此在某些时刻我决定不忽略这种可能性.
参数处理:shflags(最新版本:shell-script-template @ github)
#!/bin/bash
# ------------------------------------------------------------------
# [Author] Title
# Description
#
# This script uses shFlags -- Advanced command-line flag
# library for Unix shell scripts.
# http://code.google.com/p/shflags/
#
# Dependency:
# http://shflags.googlecode.com/svn/trunk/source/1.0/src/shflags
# ------------------------------------------------------------------
VERSION=0.1.0
SUBJECT=some-unique-id
USAGE="Usage: command -hv args"
# --- Option processing --------------------------------------------
if [ $# == 0 ] ; then
echo $USAGE
exit 1;
fi
. ./shflags
DEFINE_string 'aparam' 'adefault' 'First parameter'
DEFINE_string 'bparam' 'bdefault' 'Second parameter'
# parse command line
FLAGS "$@" || exit 1
eval set -- "${FLAGS_ARGV}"
shift $(($OPTIND - 1))
param1=$1
param2=$2
# --- Locks -------------------------------------------------------
LOCK_FILE=/tmp/${SUBJECT}.lock
if [ -f "$LOCK_FILE" ]; then
echo "Script is already running"
exit
fi
trap "rm -f $LOCK_FILE" EXIT
touch $LOCK_FILE
# -- Body ---------------------------------------------------------
# SCRIPT LOGIC GOES HERE
echo "Param A: $FLAGS_aparam"
echo "Param B: $FLAGS_bparam"
echo $param1
echo $param2
# -----------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)
我认为可以改进这些模板以进一步简化开发人员的生活.
所以问题是如何改进它们以获得以下内容:
Mic*_*com 22
这是我的脚本shell模板的标题(可以在这里找到:http://www.uxora.com/unix/shell-script/18-shell-script-template).
这man看起来很相似,用于()也用于diplsay帮助.
#!/bin/ksh
#================================================================
# HEADER
#================================================================
#% SYNOPSIS
#+ ${SCRIPT_NAME} [-hv] [-o[file]] args ...
#%
#% DESCRIPTION
#% This is a script template
#% to start any good shell script.
#%
#% OPTIONS
#% -o [file], --output=[file] Set log file (default=/dev/null)
#% use DEFAULT keyword to autoname file
#% The default value is /dev/null.
#% -t, --timelog Add timestamp to log ("+%y/%m/%d@%H:%M:%S")
#% -x, --ignorelock Ignore if lock file exists
#% -h, --help Print this help
#% -v, --version Print script information
#%
#% EXAMPLES
#% ${SCRIPT_NAME} -o DEFAULT arg1 arg2
#%
#================================================================
#- IMPLEMENTATION
#- version ${SCRIPT_NAME} (www.uxora.com) 0.0.4
#- author Michel VONGVILAY
#- copyright Copyright (c) http://www.uxora.com
#- license GNU General Public License
#- script_id 12345
#-
#================================================================
# HISTORY
# 2015/03/01 : mvongvilay : Script creation
# 2015/04/01 : mvongvilay : Add long options and improvements
#
#================================================================
# DEBUG OPTION
# set -n # Uncomment to check your syntax, without execution.
# set -x # Uncomment to debug this shell script
#
#================================================================
# END_OF_HEADER
#================================================================
Run Code Online (Sandbox Code Playgroud)
以下是使用功能:
#== needed variables ==#
SCRIPT_HEADSIZE=$(head -200 ${0} |grep -n "^# END_OF_HEADER" | cut -f1 -d:)
SCRIPT_NAME="$(basename ${0})"
#== usage functions ==#
usage() { printf "Usage: "; head -${SCRIPT_HEADSIZE:-99} ${0} | grep -e "^#+" | sed -e "s/^#+[ ]*//g" -e "s/\${SCRIPT_NAME}/${SCRIPT_NAME}/g" ; }
usagefull() { head -${SCRIPT_HEADSIZE:-99} ${0} | grep -e "^#[%+-]" | sed -e "s/^#[%+-]//g" -e "s/\${SCRIPT_NAME}/${SCRIPT_NAME}/g" ; }
scriptinfo() { head -${SCRIPT_HEADSIZE:-99} ${0} | grep -e "^#-" | sed -e "s/^#-//g" -e "s/\${SCRIPT_NAME}/${SCRIPT_NAME}/g"; }
Run Code Online (Sandbox Code Playgroud)
这是你应该得到的:
# Display help
$ ./template.sh --help
SYNOPSIS
template.sh [-hv] [-o[file]] args ...
DESCRIPTION
This is a script template
to start any good shell script.
OPTIONS
-o [file], --output=[file] Set log file (default=/dev/null)
use DEFAULT keyword to autoname file
The default value is /dev/null.
-t, --timelog Add timestamp to log ("+%y/%m/%d@%H:%M:%S")
-x, --ignorelock Ignore if lock file exists
-h, --help Print this help
-v, --version Print script information
EXAMPLES
template.sh -o DEFAULT arg1 arg2
IMPLEMENTATION
version template.sh (www.uxora.com) 0.0.4
author Michel VONGVILAY
copyright Copyright (c) http://www.uxora.com
license GNU General Public License
script_id 12345
# Display version info
$ ./template.sh -v
IMPLEMENTATION
version template.sh (www.uxora.com) 0.0.4
author Michel VONGVILAY
copyright Copyright (c) http://www.uxora.com
license GNU General Public License
script_id 12345
Run Code Online (Sandbox Code Playgroud)
您可以在此处获取完整的脚本模板:http://www.uxora.com/unix/shell-script/18-shell-script-template
D.S*_*ley 19
我将避免依赖bashshell作为shell并在POSIX定义的shell语法之上建立解决方案并/bin/sh在shebang上使用.我们有一些惊喜最近在Ubuntu的变化/bin/sh来dash.
shell世界中的另一个大流行是对退出状态代码的一般误解.退出可理解的代码是让其他shell脚本以编程方式对特定故障做出反应的原因.不幸的是,除了"sysexits.h"头文件之外,没有很多指导.
如果您正在寻找有关良好shell脚本实践的更多信息,请专注于Korn shell脚本资源.与编写随意脚本相反,Ksh编程倾向于专注于真正的编程.
就个人而言,我没有找到太多用于shell模板的东西.不幸的是,大多数工程师只需复制并粘贴您的模板,并继续编写相同的草率shell代码.更好的方法是创建一个具有明确定义的语义的shell函数库,然后说服其他人使用它们.这种方法也有助于改变控制.例如,如果您在模板中发现缺陷,那么基于它的每个脚本都会被破坏并需要修改.使用库可以在一个地方修复缺陷.
欢迎来到shell脚本世界.编写shell脚本是一种似乎正在进入文艺复兴时期的迷失艺术.在90年代后期有一些关于这个主题的好书 - 伯恩斯和亚瑟的UNIX Shell编程让人想起虽然亚马逊对这本书的评论让它看起来很糟糕.恕我直言,有效的shell代码包含了Eric S. Raymond在"Unix编程艺术"中所描述的UNIX哲学.
这是我的 bash 样板,其中有一些合理的选项,在评论中进行了解释
#!/usr/bin/env bash
set -e # Abort script at first error, when a command exits with non-zero status (except in until or while loops, if-tests, list constructs)
set -u # Attempt to use undefined variable outputs error message, and forces an exit
set -x # Similar to verbose mode (-v), but expands commands
set -o pipefail # Causes a pipeline to return the exit status of the last command in the pipe that returned a non-zero return value.
Run Code Online (Sandbox Code Playgroud)
如果您担心可移植性,请不要==在测试中使用.请=改用.不要显式检查是否$#为0.而是${n?error message}在第一次引用所需参数时使用(例如${3?error message}).这可以防止发出使用声明而非错误消息的极其恼人的做法.最重要的是,始终将错误消息放在正确的流上并以正确的状态退出.例如:
echo "Unknown error while processing options" >&2
exit 1;
Run Code Online (Sandbox Code Playgroud)
做以下事情通常很方便:
die() { echo "$*"; exit 1; } >&2
Run Code Online (Sandbox Code Playgroud)