Shell脚本常用模板

Ren*_*nov 55 bash shell

数百万开发人员编写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与sh,使用`#!/ bin/bash`应该是安全的.(毕竟,有多少Linux安装根本没有bash?)问题是说`#!/ bin/sh`然后编写依赖于bash特定功能的脚本; 当那些像Ubuntu的[DashAsBinSh](https://wiki.ubuntu.com/DashAsBinSh)这样的变化会破坏时. (3认同)

cri*_*aig 7

这是我的 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)


Wil*_*ell 6

如果您担心可移植性,请不要==在测试中使用.请=改用.不要显式检查是否$#为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)

  • 我不是`${var?msg}` 的忠实粉丝。它几乎和 set -u 一样糟糕。它抛出一个真正的 bash 错误,对于那些不熟悉错误消息的人来说,它与内部错误无法区分。它可能发生在函数中命令中间的不明显位置,该函数可能会无意中从子 shell 调用。或者,如果您在多个分支中使用参数进行分支,则必须重复检查。在评估扩张的中途救助可能会产生有害的副作用。显式检查要好得多,例如`${3+:} die msg` 或`! ${3+false} || 死消息`。 (2认同)