Frage

Was Ihre Vorschläge für eine gute bash / KSH Skriptvorlage für alle neu erstellten Skripte als Standard nutzen würde?

I beginnt in der Regel (nach der #! Linie) mit einem kommentierten-out-Header mit einem Dateinamen, Synopse, Nutzung, Rückgabewerten, Autor (en), Changelog und würde in 80-char Linien passen.

Alle Dokumentation Linien I mit Doppelrautenzeichen beginnen ## so kann ich für sie grep leicht und lokale var Namen vorangestellt mit „__“.

Alle anderen Best Practices? Tipps? Regeln der Namensgebung? Was ist Return-Codes?

Kommentare zu den Versionskontrolle: wir SVN alles in Ordnung, aber eine andere Abteilung im Unternehmen hat einen separaten Repo und das ist ihr Skript. Wie kann ich wissen, wer mit Q zu kontaktieren, wenn es keine @author info ist? Mit Einträge ähnlich wie javadocs hat einen gewissen Wert auch in der Schale Kontext, IMHO, aber ich könnte falsch sein.

War es hilfreich?

Lösung

würde ich Norman Antwort auf 6 Zeilen erstrecken, und die letzte davon ist leer:

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

In der dritten Zeile ist eine Versionskontrolle Identifikationszeichenfolge - es ist tatsächlich ein Hybrid mit einer SCCS Marker ‚@(#)‘, die von der (SCCS) Programm what und einer RCS Version Zeichenfolge identifiziert werden können, die erweitert wird, wenn die Datei unter gesetzt wird RCS, der Standard-VCS I für meinen privaten Gebrauch verwenden. Das RCS-Programm ident nimmt die erweiterte Form $Id$ up, die wie $Id: mkscript.sh,v 2.3 2005/05/20 21:06:35 jleffler Exp $ aussehen könnte. Die fünfte Zeile erinnert mich, dass das Skript eine Beschreibung seines Zwecks an der Spitze haben sollte; Ich ersetze das Wort mit einer tatsächlichen Beschreibung des Skripts (weshalb es kein Doppelpunkt, nachdem es, zum Beispiel).

Danach gibt es im Wesentlichen nichts Standard für einen Shell-Skript. Es gibt Standard-Fragmente, die, aber kein Standard-Fragment angezeigt werden, die in jedem Skript angezeigt. (Meine Diskussion geht davon aus, dass Skripte geschrieben werden, in Bourne, Korn oder POSIX (Bash) Shell-Notationen. Es gibt eine ganze separate Diskussion darüber, warum jemand einen C-Shell-Derivat nach der #! Sigill setzt in Sünde lebt.)

Zum Beispiel dieser Code in irgendeiner Form, wenn ein Skript Zwischen erstellt (temporären Dateien):

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

Die erste Zeile ein temporäres Verzeichnis wählt, säumige / tmp, wenn der Benutzer keine Alternative angegeben haben ($ TMPDIR ist sehr allgemein anerkannt und wird von POSIX standardisiert). Es erstellt dann eine Dateinamenspräfix einschließlich der Prozess-ID. Dies ist keine Sicherheitsmaßnahme; es ist eine einfache Parallelität Maßnahme, mehrere Instanzen des Skripts zu verhindern gegenseitig auf die Daten von trampeln. (Zur Sicherheit verwendet nicht vorhersagbaren Dateinamen in einem nicht öffentlichen Verzeichnis.) Die zweite Zeile stellt sicher, dass die ‚rm‘ und ‚exit‘ Befehle ausgeführt werden, wenn die Schale eine der Signal SIGHUP empfängt (1), SIGINT (2 ), SIGQUIT (3), SIGPIPE (13) oder SIGTERM (15). Der ‚rm‘ Befehl entfernt alle Zwischendateien, die die Vorlage übereinstimmen; der exit Befehl stellt sicher, dass der Status nicht Null ist, irgendeine Art von Fehlern anzeigt. Die ‚trap‘ 0 bedeutet, dass der Code auch, wenn die Shell beendet aus irgendeinem Grund ausgeführt wird - es Unachtsamkeit im Abschnitt behandelt ‚echte Arbeit‘ gekennzeichnet. Der Code am Ende entfernt dann alle überlebenden temporären Dateien, vor die Falle beim Verlassen hob und tritt schließlich mit einem Null (Erfolg) Status. Klar, wenn Sie mit einem anderen Status verlassen möchten, können Sie -. Nur sicherstellen, dass Sie es in einer Variablen gesetzt, bevor die rm und trap Linien ausgeführt wird, und dann exit $exitval verwenden

ich in der Regel verwenden die folgenden den Pfad und Suffix aus dem Skript zu entfernen, also kann ich $arg0 verwenden, wenn Fehler berichten:

arg0=$(basename $0 .sh)

Ich verwende oft eine Shell-Funktion, Fehler zu melden:

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

Wenn es nur ein oder vielleicht zwei Fehler beendet wird, ich nicht die Mühe mit der Funktion; wenn es mehr ist, tue ich, weil es die Codierung vereinfacht. Ich schaffe auch mehr oder weniger aufwendige Funktionen aufgerufen usage die Zusammenfassung zu geben, wie Sie den Befehl verwenden, -. Wieder, nur wenn es mehr als ein Ort, an dem es verwendet werden würde

Ein weiteres ziemlich Standard-Fragment ist eine Option Parsing-Schleife, mit der getopts Schale eingebaut:

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)

oder:

shift $(($OPTIND - 1))

Die Anführungszeichen um "$ OPTARG" Griffflächen in Argumenten. Die DFLAG ist kumulativ, aber die Schreibweise verwendet hier verliert den Überblick über Räume in Argumente. Es gibt (Nicht-Standard) Möglichkeiten, um dieses Problem zu umgehen, auch.

Die erste Verschiebung Notation mit jeder Schale arbeitet (oder tun würde, wenn ich verwende zurück-Ticks statt ‚$(...)‘. Die second arbeitet in modernen Schalen; es könnte auch eine Alternative mit eckigen Klammern statt Klammer sein, aber das funktioniert so habe ich zu arbeiten, nicht die Mühe gemacht, was das ist.

Ein letzter Trick für jetzt ist, dass ich oft sowohl das GNU und eine nicht-GNU-Version von Programmen um, und ich möchte in der Lage sein zu entscheiden, welche ich verwende. Viele meiner Skripte, daher verwenden, um Variablen wie:

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

Und dann, wenn ich Perl oder sed müssen aufrufen, verwendet das Skript $PERL oder $SED. Das hilft mir, wenn etwas anders verhält - ich die operative Version wählen können - oder während das Skript zu entwickeln (ich kann ohne Änderung des Skripts auf den Befehl zusätzliche Debug-Optionen nur hinzufügen). (Siehe Shell Parameter Expansion Informationen über die ${VAR:=value} und damit verbundene Bezeichnungen.)

Andere Tipps

Ich verwende den ersten Satz von ## Linien für die Nutzung Dokumentation. Ich kann jetzt nicht erinnern, wo ich das erste Mal sah.

#!/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.
}

Jeder Code, der in die Freiheit entlassen werden soll sollte folgende kurze Header:

# 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

ein Änderungsprotokoll Keeping in Code-Header gehen ist eine Reminiszenz aus, als Versionskontrollsysteme waren schrecklich unbequem. Ein Datum der letzten Änderung zeigt jemand, wie alt das Skript.

Wenn Sie vorhaben, auf bashisms zu verlassen, verwenden Sie #! / Ist / bash, nicht / ist / sh, wie sh ist der POSIX-Aufruf eines Shell. Auch wenn / bin / sh Punkte einzuschlagen, werden viele Funktionen ausgeschaltet, wenn Sie es über / bin / sh ausgeführt werden. Die meisten Linux-Distributionen nicht Scripts, die auf bashisms verlassen, versuchen tragbar zu sein.

Für mich, Kommentare in Shell-Skripten sind eine Art albern, wenn sie nicht lesen so etwas wie:

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

Shell Scripting ist so einfach, dass (es sei denn, Ihr Schreiben eine Demonstration jemand beibringen, wie man es tun) der Code fast immer beseitigt selbst.

Einige Shells mögen es nicht, ‚lokale‘ typisierte Variablen gefüttert werden. Ich glaube, Busybox an diesem Tag (eine gemeinsame Rettungsschale) ist einer von ihnen. Machen GLOBALS_OBVIOUS stattdessen seine viel einfacher zu lesen, vor allem bei der Fehlersuche über / bin / sh -x ./script.sh.

Meine persönliche Präferenz ist die Logik für sich selbst sprechen zu lassen und die Arbeit für den Parser zu minimieren. Zum Beispiel könnten viele Menschen schreiben:

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

Wo ich gerade:

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

Ebenso könnte jemand schreiben:

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

... wo ich würde:

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

Das einzige Mal, dass ich herkömmliche if / then / else ist, wenn es ein anderes, wenn in der Mischung zu werfen.

Ein schrecklich verrückt Beispiel sehr gut tragbaren Shell-Code kann nur durch Betrachten des configure-Skript in den meisten freien Softwarepaketen, die autoconf verwenden sucht werden. Ich sage verrückt, weil seine 6300 Codezeilen, die auf jedem System richtet sich an Menschen bekannt, die eine UNIX-artige Shell hat. Sie wollen nicht diese Art von aufblasen, aber es ist interessant, einige der verschiedenen Portabilität zu studieren Hacks innerhalb .. wie denen, schön zu sein, der / bin / sh Punkt könnte zu zsh:)

Der einzige andere Rat, den ich geben kann, ist Ihre Expansion in hier-docs zu beobachten, das heißt

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

... wird $ name erweitern, wenn Sie wahrscheinlich die Variable an ihrem Platz bleiben wollen. Lösen Sie diese über:

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

, die $ name als Variable verlassen, anstatt sie zu erweitern.

Ich empfehle auch lernen, wie Falle verwenden Signale .. zu fangen und die Verwendung dieser Handler als Standardcode zu machen. ein laufendes Skript zu sagen mit einem einfachen SIGUSR1 zu verlangsamen ist ganz praktisch:)

Die meisten neuen Programme, die ich schreibe (welche Werkzeug / Befehlszeile orientiert) beginnen als Shell-Skripte, seine eine gute Möglichkeit, UNIX-Tools zum Prototyp.

Sie können auch wie die SHC-Shell-Skript-Compiler, es hier .

Dies ist der Header, die für eine Shell-Skript verwenden (bash oder ksh). Es ist ein man gleich aussehen und es verwendet wird, Verwendung () als auch angezeigt werden.

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

Und hier ist die Nutzungs Funktionen gehen mit:

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

Hier ist, was Sie sollten erhalten:

# 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

Sie können die vollständige Skriptvorlage erhalten hier: http: / /www.uxora.com/unix/shell-script/18-shell-script-template

Aktivieren der Fehlererkennung macht es viel einfacher, Probleme im Skript frühzeitig zu erkennen:

set -o errexit

Beenden Skript auf den ersten Fehler. So können Sie auf vermeiden weiterhin etwas zu tun, das auf etwas früher im Skript hing, vielleicht mit einigen seltsamen Systemzustand endet.

set -o nounset

Treat Verweise auf nicht definierter Variablen als Fehler. Sehr wichtig Lauf Dinge wie rm -you_know_what "$var/" mit einer nicht ausgehärteten $var zu vermeiden. Wenn Sie wissen, dass die Variable nicht gesetzt werden können, und dies ist eine sichere Situation können Sie ${var-value} verwenden, um einen anderen Wert zu verwenden, wenn es nicht gesetzt oder ${var:-value} ist, einen anderen Wert zu verwenden, wenn es nicht gesetzt ist oder leer.

set -o noclobber

Es ist einfach, den Fehler des Einsetzens eines > machen, wo Sie < einfügen soll, und eine Datei zu überschreiben, die Sie lesen soll. Wenn Sie eine Datei in Ihrem Skript verprügeln, dann können Sie diese deaktivieren, bevor Sie die entsprechende Zeile und ermöglichen es danach wieder.

set -o pipefail

Mit dem ersten Nicht-Null-Exit-Code (falls vorhanden) aus einem Satz von verrohrt Befehl als Beendigungscode des vollen Satzes von Befehlen. Dies macht es einfacher zu Debug-Befehle geleitet.

shopt -s nullglob

Vermeiden Sie, dass Ihr /foo/* glob interpretiert wörtlich , wenn keine Dateien, den Ausdruck entsprechen.

Sie können alle diese in zwei Linien kombinieren:

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

Meine Bash-Vorlage wie unten ist (gesetzt in meiner vim Konfiguration ):

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

Ich würde vorschlagen,

#!/bin/ksh

und das ist es. Schwergewichts-Block Kommentare für Shell-Skripte? Ich bekomme die willies.

Vorschläge:

  1. Die Dokumentation sollte Daten oder Code sein, nicht Kommentare. Mindestens eine usage() Funktion. Werfen Sie einen Blick darauf, wie KSH und die anderen AST Werkzeuge selbst dokumentieren mit --man Optionen auf jeden Befehl. (Kann nicht verbinden, weil die Web-Seite nicht erreichbar ist).

  2. Deklarieren Sie lokale Variablen mit typeset. Das ist, was es ist für. Keine Notwendigkeit für böse Unterstrichen.

Was Sie tun können, ist ein Skript zu machen, die einen Header für ein Skript erstellt und und haben es automatisch geöffnet in Ihrem bevorzugten Editor. Ich sah, wie ein Mann das tut an dieser Stelle:

http://code.activestate.com/recipes/577862-bash-script-to-create-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
#===============================================================================

Im Allgemeinen habe ich ein paar Konventionen ich für jedes Skript bleiben wie ich schreibe. Ich schreibe alle Skripte mit Annahme, dass andere Menschen sie lesen könnten.

Ich beginne jedes Skript mit meinem Kopf,

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

Ich benutze dieses Datumsformat, für eine einfachere grep / Suche. Ich benutze die ‚[‘ klammern Text, um anzuzeigen, die Menschen selbst eingeben müssen. wenn sie außerhalb eines Kommentars auftreten, versuche ich sie mit ‚# [‘ zu starten. Auf diese Weise, wenn jemand sie können wie Pasten, wird es nicht für die Eingabe oder einen Testbefehl verwechselt werden. Überprüfen Sie die Nutzungs Abschnitt auf einer Manpage, diesen Stil als Beispiel zu sehen.

Wenn ich eine Zeile Code kommentieren wollen, verwende ich ein einziges ‚#‘. Wenn ich einen Kommentar als Notiz mache, verwende ich eine doppelte ‚##‘. Die /etc/nanorc verwendet auch diese Konvention. Ich finde es hilfreich, um einen Kommentar zu unterscheiden, die nicht gewählt wurden, auszuführen; Verse einen Kommentar, der als Notiz erstellt wurde.

Alle meine Shell-Variablen, ziehe ich es in CAPS zu tun. Ich versuche, zwischen 4 zu halten - 8 Zeichen, wenn nicht anders notwendig. Die Namen beziehen sich, so gut wie möglich, mit deren Nutzung.

ich auch immer Ausgang mit 0, wenn erfolgreich, oder 1 für Fehler. Wenn das Skript viele verschiedene Arten von Fehlern (und würde tatsächlich helfen, jemand, oder könnte in einigen Code in irgendeiner Weise verwendet werden) hat, würde ich eine dokumentierte Folge über 1 wählen. Im Allgemeinen wird Exit-Codes nicht so streng in der * nichts Welt durchgesetzt. Leider finde ich habe noch nie ein gutes allgemeines Nummernschema.

Ich mag Argumente in der üblichen Weise verarbeiten. Ich ziehe es immer getopts, getopt. Ich mache nie etwas Hack mit ‚lesen‘ Befehle und if-Anweisungen. Ich mag auch die Case-Anweisung verwenden, um verschachtelten ifs zu vermeiden. Ich benutze ein Übersetzungs Skript für lange Optionen, so --help -h zu getopts bedeutet. Ich schreibe alle Skripte in beiden bash (falls zulässig) oder generische sh.

ich nie bash interpretiert Symbole (oder jedes interpretiert Symbol) in Dateinamen oder einen beliebigen Namen für diese Angelegenheit verwenden. speziell ... " '` $ & * # () {} [] -., verwende ich _ für Räume

Denken Sie daran, dies sind nur Konventionen. Beste Praxis grober, aber manchmal sind Sie außerhalb der Linien gezwungen. Das wichtigste ist, konsequent sein zwischen und innerhalb Ihrer Projekte.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top