문제

새로 생성 된 모든 스크립트의 표준으로 사용하기 위해 좋은 bash/ksh 스크립트 템플릿에 대한 제안은 무엇입니까?

나는 보통 시작한다 (이후 #! 라인) 파일 이름, 시놉시스, 사용법, 반환 값, 저자, Changelog가있는 댓글이있는 헤더와 80 char 라인에 맞습니다.

모든 문서 줄은 이중 해시 기호로 시작합니다 ## 그래서 나는 그들을 쉽게 grep 할 수 있으며 로컬 var 이름은 "__"로 선불됩니다.

다른 모범 사례가 있습니까? 팁? 이름 지정 규칙? 반품 코드는 어떻습니까?

버전 제어에 대한 의견 : 우리는 SVN을 모두 사용하지만 기업의 다른 부서에는 별도의 리포지토리가 있으며 이것은 스크립트입니다. @Author 정보가 없으면 Q와 접촉 할 사람을 어떻게 알 수 있습니까? Javadocs와 유사한 항목을 사용하면 쉘 컨텍스트에서도 약간의 장점이 있지만 IMHO, 나는 틀릴 수 있습니다.

도움이 되었습니까?

해결책

Norman의 답변을 6 줄로 확장하고 마지막은 비어 있습니다.

#!/bin/ksh
#
# @(#)$Id$
#
# Purpose
 

세 번째 줄은 버전 제어 식별 문자열입니다. 실제로 SCCS 마커가있는 하이브리드입니다.@(#)'(SCCS) 프로그램으로 식별 할 수 있습니다. what 그리고 파일이 RCS에 넣을 때 확장되는 RCS 버전 문자열, 개인 용도로 사용하는 기본 VC입니다. RCS 프로그램 ident 확장 된 형태를 선택합니다 $Id$, 어떻게 생겼을 수도 있습니다 $Id: mkscript.sh,v 2.3 2005/05/20 21:06:35 jleffler Exp $. 다섯 번째 줄은 스크립트가 맨 위에 그 목적에 대한 설명을 가져야한다는 것을 상기시켜줍니다. 나는 그 단어를 스크립트에 대한 실제 설명으로 바꿉니다 (예를 들어 결장이없는 이유).

그 후, 쉘 스크립트에는 본질적으로 표준이 없습니다. 나타나는 표준 조각이 있지만 모든 스크립트에 나타나는 표준 조각은 없습니다. (내 논의는 스크립트가 Bourne, Korn 또는 Posix (Bash) 쉘 표기법으로 작성되었다고 가정합니다. 왜 C 쉘 파생물을 한 후에 누군가가 왜 C 쉘 파생물을 입력했는지에 대한 전체 별도의 논의가 있습니다. #! 시길은 죄로 살고 있습니다.)

예를 들어,이 코드는 스크립트가 중간 (임시) 파일을 생성 할 때마다 어떤 모양 또는 양식으로 나타납니다.

tmp=${TMPDIR:-/tmp}/prog.$$
trap "rm -f $tmp.?; exit 1" 0 1 2 3 13 15

...real work that creates temp files $tmp.1, $tmp.2, ...

rm -f $tmp.?
trap 0
exit 0

첫 번째 줄은 사용자가 대안을 지정하지 않은 경우 /tmp로 기본적으로 임시 디렉토리를 선택합니다 ($ TMPDIR은 매우 널리 인식되고 POSIX에 의해 표준화 됨). 그런 다음 프로세스 ID를 포함하여 파일 이름 접두사를 만듭니다. 이것은 보안 조치가 아닙니다. 간단한 동시성 측정 값으로 스크립트의 여러 인스턴스가 서로의 데이터를 짓밟지 못하게합니다. (보안의 경우 비 공개 디렉토리에서 예측 불가능한 파일 이름을 사용하십시오.) 두 번째 줄은 'rm' 그리고 'exit'쉘이 신호 Sighup (1), Sigint (2), Sigquit (3), Sigpipe (13) 또는 Sigterm (15)을 받으면'명령이 실행됩니다. 'rm'명령은 템플릿과 일치하는 중간 파일을 제거합니다. 그만큼 exit 명령은 상태가 0이 아닌지 확인하여 일종의 오류를 나타냅니다. 'trap'0 중 하나는 쉘이 어떤 이유로 든 종료되면 코드가 실행된다는 것을 의미합니다.'실제 작업 '이라는 섹션의 부주의를 다룹니다. 끝에있는 코드는 생존하는 임시 파일을 제거합니다. ~ 전에 출구에서 함정을 들어 올리고 마지막으로 0 (성공) 상태로 종료합니다. 분명히, 다른 상태로 종료하려면, 실행하기 전에 변수로 설정해야 할 수도 있습니다. rm 그리고 trap 라인을 사용한 다음 사용합니다 exit $exitval.

나는 보통 다음을 사용하여 스크립트에서 경로와 접미사를 제거하므로 사용할 수 있습니다. $arg0 오류를보고 할 때 :

arg0=$(basename $0 .sh)

나는 종종 쉘 기능을 사용하여 오류를보고합니다.

error()
{
    echo "$arg0: $*" 1>&2
    exit 1
}

하나 또는 두 개의 오류 종료 만 있으면 기능을 귀찮게하지 않습니다. 더 이상 있으면 코딩을 단순화하기 때문에해야합니다. 또한 호출 된 다소 정교한 기능을 만듭니다 usage 명령을 사용하는 방법에 대한 요약을 제공합니다. 다시 사용하는 곳이 둘 이상인 경우에만.

또 다른 상당히 표준 조각은 getopts 쉘 내장 :

vflag=0
out=
file=
Dflag=
while getopts hvVf:o:D: flag
do
    case "$flag" in
    (h) help; exit 0;;
    (V) echo "$arg0: version $Revision$ ($Date$)"; exit 0;;
    (v) vflag=1;;
    (f) file="$OPTARG";;
    (o) out="$OPTARG";;
    (D) Dflag="$Dflag $OPTARG";;
    (*) usage;;
    esac
done
shift $(expr $OPTIND - 1)

또는:

shift $(($OPTIND - 1))

"$ optarg"주변의 인용문은 인수의 공간을 처리합니다. DFLAG는 누적이지만 여기에 사용 된 표기법은 인수의 공간 트랙을 잃습니다. 그 문제를 해결하는 방법도 있습니다.

첫 번째 시프트 표기법은 모든 쉘과 함께 작동합니다 (또는 내가 대신 백 틱을 사용한 경우$(...)'. 두 번째는 현대 껍질에서 작용합니다. 괄호 대신 사각형 괄호가있는 대안이있을 수도 있지만, 이것이 작동하므로 그것이 무엇인지 알아 내지 않았습니다.

지금의 마지막 요령 중 하나는 종종 GNU와 GNU가 아닌 프로그램의 프로그램을 모두 가지고 있으며 내가 사용하는 것을 선택할 수 있다는 것입니다. 따라서 많은 내 스크립트가 다음과 같은 변수를 사용합니다.

: ${PERL:=perl}
: ${SED:=sed}

그리고 Perl을 호출해야 할 때 sed, 스크립트가 사용됩니다 $PERL 또는 $SED. 이것은 무언가가 다르게 행동 할 때 도움이됩니다 - 작동 버전을 선택하거나 스크립트를 개발할 수 있습니다 (스크립트를 수정하지 않고 명령에 추가 디버그 전용 옵션을 추가 할 수 있음). (보다 쉘 매개 변수 확장 에 대한 정보 ${VAR:=value} 관련 표기법.)

다른 팁

사용 설명서에 첫 번째 ## 라인 세트를 사용합니다. 나는 지금 내가 이것을 처음 본 곳을 기억할 수 없다.

#!/bin/sh
## Usage: myscript [options] ARG1
##
## Options:
##   -h, --help    Display this message.
##   -n            Dry-run; only show what would be done.
##

usage() {
  [ "$*" ] && echo "$0: $*"
  sed -n '/^##/,/^$/s/^## \{0,1\}//p' "$0"
  exit 2
} 2>/dev/null

main() {
  while [ $# -gt 0 ]; do
    case $1 in
    (-n) DRY_RUN=1;;
    (-h|--help) usage 2>&1;;
    (--) shift; break;;
    (-*) usage "$1: unknown option";;
    (*) break;;
    esac
  done
  : do stuff.
}

Wild에서 릴리스 될 코드는 다음과 같은 짧은 헤더가 있어야합니다.

# Script to turn lead into gold
# Copyright (C) 2009 Joe Q Hacker - All Rights Reserved
# Permission to copy and modify is granted under the foo license
# Last revised 1/1/2009

코드 헤더에서 변경 로그를 유지하는 것은 버전 제어 시스템이 끔찍한 불편할 때와의 후퇴입니다. 마지막으로 수정 된 날짜는 스크립트가 몇 살인지 보여줍니다.

Bashisms에 의존하려는 경우 Sh는 쉘의 Posix 호출이므로/bin/sh가 아닌 #!/bin/bash를 사용하십시오. /bin /sh가 bash를 가리키더라도 /bin /sh를 통해 실행하면 많은 기능이 꺼집니다. 대부분의 Linux 배포판은 Bashism에 의존하는 스크립트를 사용하지 않고 휴대용을 시도합니다.

나에게 쉘 스크립트의 의견은 다음과 같은 것을 읽지 않는 한 일종의 바보입니다.

# I am not crazy, this really is the only way to do this

쉘 스크립팅은 매우 간단하여 (누군가에게 어떻게 해야하는지 가르치는 데모를 작성하지 않는 한) 코드는 거의 항상 스스로를 사용하지 않습니다.

일부 쉘은 '로컬'변수를 입력하는 것을 좋아하지 않습니다. 나는 오늘날 Busybox (일반적인 구조 껍질)가 그 중 하나라고 믿는다. 대신 globals_obvious를 대신, 특히/bin/sh -x ./script.sh를 통해 디버깅 할 때 읽기가 훨씬 쉽습니다.

저의 개인적인 취향은 논리가 스스로 말하고 파서의 작업을 최소화하게하는 것입니다. 예를 들어, 많은 사람들이 다음을 쓸 수 있습니다.

if [ $i = 1 ]; then
    ... some code 
fi

그냥 내가 그냥 :

[ $i = 1 ] && {
    ... some code
}

마찬가지로 누군가는 다음을 쓸 수 있습니다.

if [ $i -ne 1 ]; then
   ... some code
fi

... 여기서 :

[ $i = 1 ] || {
   ... some code 
}

기존의 if / the / else를 사용하는 유일한 시간은 믹스를 던질 다른 IF가있는 경우입니다.

AutoConf를 사용하는 대부분의 무료 소프트웨어 패키지에서 'Configure'스크립트를 보면 매우 좋은 휴대용 쉘 코드의 끔찍한 미친 예를 연구 할 수 있습니다. 나는 쉘과 같은 유닉스가있는 사람에게 알려진 모든 시스템을 수용하는 6300 줄의 코드가 있기 때문에 미쳤다고 말합니다. 당신은 그런 종류의 팽창을 원하지 않지만, 내에서 다양한 휴대 성 해킹 중 일부를 연구하는 것은 흥미 롭습니다.

내가 줄 수있는 유일한 조언은 여기 Docs에서 당신의 확장을 지켜 보는 것입니다.

cat << EOF > foo.sh
   printf "%s was here" "$name"
EOF

... 변수를 제자리에두고 싶을 때 $ 이름을 확장 할 것입니다. 이를 통해 다음을 해결하십시오.

  printf "%s was here" "\$name"

확장하는 대신 $ 이름을 변수로 남겨 둡니다.

또한 트랩을 사용하여 신호를 잡는 방법을 배우는 것이 좋습니다. 그리고 해당 핸들러를 보일러 플레이트 코드로 사용하십시오. 간단한 SigusR1로 속도가 느려지도록 실행중인 스크립트를 말하는 것은 매우 편리합니다 :)

내가 쓴 대부분의 새로운 프로그램 (도구 / 명령 줄 방향)은 쉘 스크립트로 시작하여 Unix 도구를 프로토 타입하는 좋은 방법입니다.

SHC 쉘 스크립트 컴파일러를 좋아할 수도 있습니다. 여기에서 확인하십시오.

이것은 내 스크립트 쉘 (bash 또는 ksh)에 사용하는 헤더입니다. 이것은 man 똑같이 보이면 usage ()도 표시하는 데 사용됩니다.

#!/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
#================================================================

다음은 다음과 같이 사용할 수있는 사용 기능입니다.

  #== 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"; }

다음은 얻어야 할 사항입니다.

# 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

여기에서 전체 스크립트 템플릿을 얻을 수 있습니다. http://www.uxora.com/unix/shell-script/18-shell-script-template

오류 감지를 활성화하면 스크립트의 문제를 조기에 더 쉽게 감지 할 수 있습니다.

set -o errexit

첫 번째 오류에서 스크립트를 종료합니다. 그렇게하면 스크립트 초기에 무언가에 의존하는 일을 계속하지 않으면 서 이상한 시스템 상태로 끝날 것입니다.

set -o nounset

미지의 변수에 대한 참조를 오류로 취급하십시오. 같은 일을 피하는 것이 매우 중요합니다 rm -you_know_what "$var/" 셋호와 함께 $var. 변수가 설정이 없을 수 있고 안전한 상황이라는 것을 알고 있다면 사용할 수 있습니다. ${var-value} 다른 값을 사용하지 않거나 ${var:-value} 설정이없는 경우 다른 값을 사용합니다 또는 비어 있는.

set -o noclobber

삽입하는 실수를 쉽게 만드는 것은 쉽습니다. > 삽입하려는 곳 <, 읽은 일부 파일을 덮어 씁니다. 스크립트에서 파일을 클로버 해야하는 경우 관련 줄 전에이를 비활성화하고 나중에 다시 활성화 할 수 있습니다.

set -o pipefail

전체 명령 세트의 종료 코드로 Piped 명령 세트의 첫 번째 비 에어어 출구 코드 (있는 경우)를 사용하십시오. 따라서 파이프 된 명령을 디버그하는 것이 더 쉬워집니다.

shopt -s nullglob

당신의 것을 피하십시오 /foo/* 글로벌이 해석됩니다 문자 그대로 해당 표현식과 일치하는 파일이없는 경우.

이 모든 것을 두 줄로 결합 할 수 있습니다.

set -o errexit -o nounset -o noclobber -o pipefail
shopt -s nullglob

내 배쉬 템플릿은 다음과 같습니다 ( VIM 구성):

#!/bin/bash

## DESCRIPTION: 

## AUTHOR: $USER_FULLNAME

declare -r SCRIPT_NAME=$(basename "$BASH_SOURCE" .sh)

## exit the shell(default status code: 1) after printing the message to stderr
bail() {
    echo -ne "$1" >&2
    exit ${2-1}
} 

## help message
declare -r HELP_MSG="Usage: $SCRIPT_NAME [OPTION]... [ARG]...
  -h    display this help and exit
"

## print the usage and exit the shell(default status code: 2)
usage() {
    declare status=2
    if [[ "$1" =~ ^[0-9]+$ ]]; then
        status=$1
        shift
    fi
    bail "${1}$HELP_MSG" $status
}

while getopts ":h" opt; do
    case $opt in
        h)
            usage 0
            ;;
        \?)
            usage "Invalid option: -$OPTARG \n"
            ;;
    esac
done

shift $(($OPTIND - 1))
[[ "$#" -lt 1 ]] && usage "Too few arguments\n"

#==========MAIN CODE BELOW==========

내가 제안 할게

#!/bin/ksh

그리고 그게 다야. 쉘 스크립트에 대한 헤비급 블록 댓글? 나는 의지를 얻는다.

제안 :

  1. 문서는 주석이 아닌 데이터 또는 코드 여야합니다. 적어도 a usage() 기능. KSH와 다른 AST 도구가 모든 명령의 맨 옵션으로 어떻게 문서화하는지 살펴보십시오. (웹 사이트가 다운되어 링크 할 수 없습니다.)

  2. 로컬 변수를 선언합니다 typeset. 그게 바로 그게되었습니다. 불쾌한 밑줄이 필요하지 않습니다.

당신이 할 수있는 것은 스크립트의 헤더를 만들고 좋아하는 편집기에서 자동 열기를하는 스크립트를 만드는 것입니다. 나는이 사이트에서 남자가 그렇게하는 것을 보았다.

http://code.activestate.com/recipes/577862-bash-scripte-a-header-for-bash-scripts/?in=lang-bash

#!/bin/bash -       
#title           :mkscript.sh
#description     :This script will make a header for a bash script.
#author          :your_name_here
#date            :20110831
#version         :0.3    
#usage           :bash mkscript.sh
#notes           :Vim and Emacs are needed to use this script.
#bash_version    :4.1.5(1)-release
#===============================================================================

일반적으로, 나는 내가 쓰는 모든 대본에 대해 몇 가지 규칙을 가지고 있습니다. 나는 다른 사람들이 읽을 수 있다는 가정으로 모든 스크립트를 씁니다.

헤더로 모든 스크립트를 시작합니다.

#!/bin/bash
# [ID LINE]
##
## FILE: [Filename]
##
## DESCRIPTION: [Description]
##
## AUTHOR: [Author]
##
## DATE: [XX_XX_XXXX.XX_XX_XX]
## 
## VERSION: [Version]
##
## USAGE: [Usage]
##

더 쉽게 GREP/검색을 위해 해당 날짜 형식을 사용합니다. 나는 '['버팀대를 사용하여 사람들이 스스로 입력해야한다는 텍스트를 나타냅니다. 그들이 댓글 밖에서 발생하면 '#['로 시작하려고합니다. 그렇게하면 누군가가 그대로 붙여 넣으면 입력이나 테스트 명령으로 오인되지 않습니다. 이 스타일을 예로 들어 맨 페이지의 사용 섹션을 확인하십시오.

코드 라인을 언급하고 싶을 때 단일 '#'를 사용합니다. 메모로 댓글을 달 때 Double '##'을 사용합니다. 그만큼 /etc/nanorc 그 컨벤션도 사용합니다. 실행하지 않기로 선택한 의견을 구별하는 것이 도움이된다고 생각합니다. 메모로 만들어진 댓글이 있습니다.

모든 쉘 변수는 캡에서 선호합니다. 달리 필요하지 않는 한 4-8 자 사이를 유지하려고합니다. 이름은 가능한 한 최선의 사용과 관련이 있습니다.

또한 성공하면 항상 0으로 나가거나 오류는 1입니다. 스크립트에 여러 가지 유형의 오류가있는 경우 (실제로 누군가를 도울 수 있거나 어떤 방식 으로든 어떤 방식 으로든 사용될 수 있음) 1 이상의 문서화 시퀀스를 선택합니다. 일반적으로 종료 코드는 *에서 엄격하게 시행되지 않습니다 *. 닉스 월드. 불행히도 나는 좋은 일반적인 숫자 체계를 찾지 못했습니다.

나는 표준 방식으로 논쟁을 처리하는 것을 좋아합니다. 나는 항상 getopts, getopt를 선호합니다. 나는 '읽기'명령과 if 문으로 해킹을하지 않습니다. 또한 중첩 된 IFS를 피하기 위해 CASE 문을 사용하고 싶습니다. 긴 옵션을 위해 번역 스크립트를 사용하므로 -HELP는 -H를 통해 getOpts를 의미합니다. 나는 모든 스크립트를 bash (허용 가능한 경우) 또는 일반 sh로 작성합니다.

나는 파일 이름에 Bash 해석 된 기호 (또는 해석 된 기호) 또는 그 문제의 이름을 사용하지 않습니다. 구체적으로 ... " '`$ & * # () {} [] -, 나는 _ 공백에 _를 사용합니다.

이것들은 단지 규칙 일뿐입니다. 모범 사례는 거칠지 만 때로는 선 밖으로 강제로 가야합니다. 가장 중요한 것은 프로젝트 내에서 일관되게하는 것입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top