Me extiendo la respuesta de Norman a 6 líneas, y el último de los que está en blanco:
#!/bin/ksh
#
# @(#)$Id$
#
# Purpose
La tercera línea es una cadena de identificación del control de versiones - en realidad es un híbrido con un CCSC marcador '@(#)
' que puede ser identificado por el programa (SCCS) what
y una cadena de versión RCS que se expande cuando el archivo se coloca en RCS, el VCS predeterminado que uso para mi uso privado. El programa RCS ident
toma la forma expandida de $Id$
, que podría verse como $Id: mkscript.sh,v 2.3 2005/05/20 21:06:35 jleffler Exp $
. La quinta línea me recuerda que el script debe tener una descripción de su propósito en la parte superior; Sustituyo la palabra con una descripción real de la secuencia de comandos (que es por lo que no hay dos puntos después de ella, por ejemplo).
Después de eso, esencialmente no hay nada estándar para un script de shell. Hay fragmentos estándar que aparecen, pero no un fragmento estándar que aparece en cada script. (Mi discusión asume que los guiones están escritos en notación de shell Bourne, Korn o POSIX (Bash). Hay una discusión por separado sobre por qué alguien que pone un derivado de C Shell después del símbolo #!
está viviendo en pecado.)
Por ejemplo, el código aparece en una forma u otra cada vez que un script crea) los archivos intermedios (temporales:
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
La primera línea elige un directorio temporal, por defecto a/tmp si el usuario no lo hizo especifique una alternativa ($ TMPDIR es ampliamente reconocido y estandarizado por POSIX). Luego crea un prefijo de nombre de archivo que incluye la identificación del proceso. Esta no es una medida de seguridad; es una medida de simultaneidad simple que evita que varias instancias de la secuencia de comandos pisoteen los datos de la otra parte. (Para mayor seguridad, use nombres de archivos no predecibles en un directorio que no sea público.) La segunda línea garantiza que los comandos 'rm
' y 'exit
' se ejecuten si el shell recibe cualquiera de las señales SIGHUP (1), SIGINT (2), SIGQUIT (3), SIGPIPE (13) o SIGTERM (15). El comando 'rm
' elimina cualquier archivo intermedio que coincida con la plantilla; El comando exit
garantiza que el estado sea distinto de cero, lo que indica algún tipo de error. El 'trap
' de 0 significa que el código también se ejecuta si el shell se cierra por algún motivo: cubre el descuido en la sección marcada como 'trabajo real'. El código al final luego elimina cualquier archivo temporal que haya sobrevivido, antes de levantando la trampa al salir, y finalmente sale con un estado de cero (correcto). Claramente, si desea salir con otro estado, puede - simplemente asegúrese de configurarlo en una variable antes de ejecutar las líneas rm
y trap
, y luego use exit $exitval
.
lo general el uso de la siguiente para eliminar la ruta y el sufijo de la secuencia de comandos, por lo que puede utilizar $arg0
al informar errores:
arg0=$(basename $0 .sh)
I a menudo utilizan una función de shell para informar de errores:
error()
{
echo "$arg0: $*" 1>&2
exit 1
}
Si solo hay una o quizás dos salidas de error, no me molesto con la función; si hay más, lo hago porque simplifica la codificación. También creo funciones más o menos elaboradas llamadas usage
para dar el resumen de cómo usar el comando, nuevamente, solo si hay más de un lugar donde se usaría.
Otro fragmento bastante estándar es un bucle análisis de las opciones, utilizando el getopts
incorporada en el shell:
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)
o:
shift $(($OPTIND - 1))
Las comillas alrededor de los espacios de la manija "$ OPTARG" en argumentos. El Dflag es acumulativo, pero la notación utilizada aquí pierde el seguimiento de los espacios en los argumentos. Existen formas (no estándar) de evitar ese problema también.
La notación del primer cambio funciona con cualquier caparazón (o lo haría si usaba retrocesos en lugar de '$(...)
'. El segundo funciona en conchas modernas, incluso podría haber una alternativa con corchetes en lugar de paréntesis, pero esto funciona así que no me he molestado en averiguar qué es eso.
Un truco final por ahora es que a menudo tengo la versión GNU y una versión no GNU de los programas, y quiero poder elegir qué Yo suelo.Muchos de mis guiones, por lo tanto, utilizar variables tales como:
: ${PERL:=perl}
: ${SED:=sed}
Y luego, cuando necesito invocar Perl o sed
, la secuencia de comandos utiliza $PERL
o $SED
. Esto me ayuda cuando algo se comporta de manera diferente (puedo elegir la versión operativa) o mientras desarrollo el guión (puedo agregar opciones extra de solo depuración al comando sin modificar el guión).
Autor (es)? Registro de cambios? ¡Obtenga un sistema de control de versiones! – derobert