Diferencia entre revisiones de «Scripts Bash»
(No se muestran 26 ediciones intermedias del mismo usuario) | |||
Línea 2: | Línea 2: | ||
La estructura básica de un script de Bash es: | La estructura básica de un script de Bash es: | ||
<syntaxhighlight lang="bash"> | |||
#!/bin/bash | #!/bin/bash | ||
# Indicamos que se debe ejecutar con el shell bash | # Indicamos que se debe ejecutar con el shell bash | ||
Línea 11: | Línea 11: | ||
exit 0 | exit 0 | ||
# Con esta salida indicamos a otro posible script que lo invoque que ha terminado correctamente. | # Con esta salida indicamos a otro posible script que lo invoque que ha terminado correctamente. | ||
</syntaxhighlight> | |||
== Redirecciones == | == Redirecciones == | ||
$ cmd > fichero | $ cmd > fichero | ||
Redirige la salida estandar a un fichero. | Redirige la salida estandar a un fichero. | ||
$ cmd 2> fichero | $ cmd 2> fichero | ||
Redirige la salida de error a un fichero. | Redirige la salida de error a un fichero. | ||
$ cmd >> fichero | $ cmd >> fichero | ||
Añade a un fichero la salida estandar sin borrar el contenido anterior. | Añade a un fichero la salida estandar sin borrar el contenido anterior. | ||
$ cmd 2>> fichero | $ cmd 2>> fichero | ||
Añade los errores al final de un fichero. | Añade los errores al final de un fichero. | ||
$ cmd &> fichero | $ cmd &> fichero | ||
Redirige la salida estandar y la de error a un fichero. | Redirige la salida estandar y la de error a un fichero. | ||
$ cmd > fichero 2>&1 | $ cmd > fichero 2>&1 | ||
{{nota|Ojo con | {{nota|Ojo con | ||
$ cmd 2>&1 >>fichero | $ cmd 2>&1 >>fichero | ||
Porque no funcionará, ya que evalúa de izquierda a derecha. Es decir, primero | Porque no funcionará, ya que evalúa de izquierda a derecha. Es decir, primero envía el error a la salida estándar 1 que es la terminal y luego la salida estándar la modifica por el fichero. Pero ya ha salido el error por pantalla}} | ||
Otra manera de redirigir las dos salidas a un fichero. | Otra manera de redirigir las dos salidas a un fichero. En este caso, la estándar va al fichero y la de error va a la estándar. | ||
$ cmd > /dev/null | $ cmd > /dev/null | ||
Línea 48: | Línea 42: | ||
$ cmd < fichero | $ cmd < fichero | ||
Redirige el contenido de un fichero a la entrada estandar del comando. | Redirige el contenido de un fichero a la entrada estandar del comando. | ||
Línea 59: | Línea 52: | ||
$ cmd <<< "palabra" | $ cmd <<< "palabra" | ||
Redirecciona el texto al comando. Se llama ''here-string''. | Redirecciona el texto al comando. Se llama ''here-string''. | ||
$ exec 2> fichero | $ exec 2> fichero | ||
Redirige la salida de error a un fichero para todos los comandos. El comando exec es uno de los llamados ''built-in'' de bash. Permite ejecutar un comando sustituyéndo al shell actual o, los que nos interesa ahora, gestionar las redirecciones. | |||
Redirige la salida de error a un fichero para todos los comandos. | |||
$ exec 3< fichero | $ exec 3< fichero | ||
Abre un fichero para leer usando un nuevo descriptor. | Abre un fichero para leer usando un nuevo descriptor. | ||
Línea 79: | Línea 69: | ||
Cierra un descriptor de fichero. | Cierra un descriptor de fichero. | ||
<syntaxhighlight lang="bash"> | |||
# open it | # open it | ||
exec 3< input.txt | exec 3< input.txt | ||
# for example: read one line from the file(-descriptor) | # for example: read one line from the file(-descriptor) | ||
read -u 3 LINE | read -u 3 LINE # la opción -u en read permite seleccionar el descriptor de fichero | ||
# or | # or | ||
read LINE <&3 | read LINE <&3 | ||
Línea 90: | Línea 81: | ||
exec 3<&- | exec 3<&- | ||
</syntaxhighlight> | |||
Si queremos unir las salidas de varios comandos seguidos, podemos usar las {}: | |||
ERROR=$( { ./useless.sh | sed s/Output/Useless/ > outfile; } 2>&1 ) | |||
http://mywiki.wooledge.org/BashFAQ/032 | |||
=== read interactivo de ficheros === | === read interactivo de ficheros === | ||
Línea 96: | Línea 94: | ||
Observemos este script: | Observemos este script: | ||
<syntaxhighlight lang="bash"> | |||
#!/bin/bash | #!/bin/bash | ||
# cada 10 linies espera a que l'usuari escriga alguna cosa | # cada 10 linies espera a que l'usuari escriga alguna cosa | ||
Línea 106: | Línea 104: | ||
((ContLin % 10)) > /dev/null || read | ((ContLin % 10)) > /dev/null || read | ||
done < numeros | done < numeros | ||
</syntaxhighlight> | |||
El resultado es este: | El resultado es este: | ||
Línea 114: | Línea 112: | ||
Esto es porque el read de dentro del bucle lee también del fichero. Se puede forzar a que lea de la terminal: | Esto es porque el read de dentro del bucle lee también del fichero. Se puede forzar a que lea de la terminal: | ||
<syntaxhighlight lang="bash"> | |||
#!/bin/bash | #!/bin/bash | ||
# cada 10 linies espera a que l'usuari escriga alguna cosa | # cada 10 linies espera a que l'usuari escriga alguna cosa | ||
Línea 124: | Línea 122: | ||
((ContLin % 10)) > /dev/null || read '''< /dev/tty''' | ((ContLin % 10)) > /dev/null || read '''< /dev/tty''' | ||
done < numeros | done < numeros | ||
</syntaxhighlight> | |||
O se puede crear un descriptor de fichero y modificar el read del while: | O se puede crear un descriptor de fichero y modificar el read del while: | ||
<syntaxhighlight lang="bash"> | |||
#!/bin/bash | #!/bin/bash | ||
# cada 10 linies espera a que l'usuari escriga alguna cosa | # cada 10 linies espera a que l'usuari escriga alguna cosa | ||
exec 3< numeros | |||
while read Num '''<&3''' | while read Num '''<&3''' | ||
do | do | ||
Línea 137: | Línea 135: | ||
((ContLin % 10)) > /dev/null || read | ((ContLin % 10)) > /dev/null || read | ||
done | done | ||
</syntaxhighlight> | |||
=== Process Substitution === | === Process Substitution === | ||
Línea 145: | Línea 144: | ||
$ diff <(ls $first_directory) <(ls $second_directory) | $ diff <(ls $first_directory) <(ls $second_directory) | ||
{{nota|Obsérvese que delante de los paréntesis no hay un $, si lo hubiera, el resultado lo pasaría literal, como una variable. De esta manera, el resultado de los ls lo encapsula en una especie fichero virtual temporal y así lo entiende el comando diff. Esto se puede ver si se lo pasamos al echo. Da un fichero situado en /dev/fd/ donde, casualmente, hay otros con el nombre de 1, 2, 255 ... esos son los descriptores de la salida estándar o de error. Los definidos por exec también se ven ahí.}} | |||
== Parámetros, variables y argumentos == | == Parámetros, variables y argumentos == | ||
Línea 156: | Línea 157: | ||
* Si son caracteres especiales tienen diversos significados: | * Si son caracteres especiales tienen diversos significados: | ||
* '''$#''' Candidad de argumentos | * '''$#''' Candidad de argumentos | ||
* '''$*''' Todos los argumentos | * '''$*''' Todos los argumentos. Es equivalente a $@ si se ponen sin dobles comillas. Con dobles comillas, $* son todos los argumentos seguidos y como separador el primer caracter del $IFS y $@ son todos los argumentos separados. | ||
* '''$?''' : a 0 si el comando anterior ha terminado | $ function f(){ IFS='-'; echo $*; echo "$*"; echo $@; echo "$@";}; f 2 3 4 5 "6 7" | ||
* '''$_''' : El | * '''$?''' : a 0 si el comando anterior ha terminado con éxito. A 1 o más si ha dado erroro a terminado sin éxito. Es el '''Exit Status''' | ||
* '''$_''' : El último argumento del comando anterior | |||
* '''$$''' : El PID del proceso actual | |||
* '''$!''' : El PID del último proceso ejecutado en background. | |||
$ sleep 1000 & ps; echo -e "\nProceso actual: $$\nProceso en background: $!"; | |||
* '''$-''' : La lista de opciones del proceso actual. | |||
===shift=== | ===shift=== | ||
Línea 165: | Línea 173: | ||
En este ejemplo se ve bastante bien. [http://www.ehu.es/ehusfera/davidfernandez/2010/03/01/bash-captura-de-parametros-en-un-script/ Via] | En este ejemplo se ve bastante bien. [http://www.ehu.es/ehusfera/davidfernandez/2010/03/01/bash-captura-de-parametros-en-un-script/ Via] | ||
<syntaxhighlight lang="bash"> | |||
while test -n "$1"; do | while test -n "$1"; do | ||
case "$1" in | case "$1" in | ||
Línea 191: | Línea 199: | ||
shift | shift | ||
done | done | ||
</syntaxhighlight> | |||
El while recorre todos los argumentos, aunque sólo comprueba el $1 en cada iteración, se queda con $2 y pasa al siguiente. De esta manera, el órden de los argumentos y sus opciones no es relevante. | El while recorre todos los argumentos, aunque sólo comprueba el $1 en cada iteración, se queda con $2 y pasa al siguiente. De esta manera, el órden de los argumentos y sus opciones no es relevante. | ||
Línea 200: | Línea 208: | ||
El shell se encarga de sustituir los caracteres de sustitución y las variables antes de pasar los argumentos a un comando o script. Esto se puede ver con este script: | El shell se encarga de sustituir los caracteres de sustitución y las variables antes de pasar los argumentos a un comando o script. Esto se puede ver con este script: | ||
<syntaxhighlight lang="bash"> | |||
#!/bin/bash | #!/bin/bash | ||
echo $* | echo $* | ||
echo $# | echo $# | ||
</syntaxhighlight> | |||
Si se invoca de estas maneras, por ejemplo: | Si se invoca de estas maneras, por ejemplo: | ||
$ ./argumentos * | $ ./argumentos * | ||
Línea 211: | Línea 219: | ||
$ ./argumentos {1..100} | $ ./argumentos {1..100} | ||
$ ./argumentos $HOME | $ ./argumentos $HOME | ||
A esto se le llama '''Expansión de parámetros'''. Se pueden hacer muchas cosas manipulando esta expansión: | |||
Uso simple: | |||
*$PARAMETER | |||
*${PARAMETER} | |||
Indirección: | |||
*${!PARAMETER} | |||
$ a=100 | |||
$ b=a | |||
$ echo "$b=${!b}" | |||
Modificación de mayúsculas: | |||
*${PARAMETER^} # La primera | |||
*${PARAMETER^^} # todas | |||
*${PARAMETER,} # la primera en minúscula | |||
*${PARAMETER,,} # todas | |||
*${PARAMETER~} # invertir | |||
*${PARAMETER~~} | |||
Expansión del nombre de la variable: | |||
*${!PREFIX*} # todas las variables que tengan ese prefijo | |||
*${!PREFIX@} | |||
Quitar substrings: | |||
*${PARAMETER#PATTERN} #Quita el prefijo más corto que cumpla el patrón | |||
*${PARAMETER##PATTERN} # Quita el prefijo más largo que cumpla el patrón | |||
*${PARAMETER%PATTERN} # Quita sufijo más corto | |||
*${PARAMETER%%PATTERN} # quita el sufijo más largo | |||
Buscar y reemplazar | |||
*${PARAMETER/PATTERN/STRING} # Una vez | |||
*${PARAMETER//PATTERN/STRING} # Todas las ocurrencias | |||
*${PARAMETER/PATTERN} # Lo quita una vez | |||
*${PARAMETER//PATTERN} # Lo quita todas las ocurrencias | |||
Longitud de la cadena | |||
*${#PARAMETER} | |||
Substrings: | |||
*${PARAMETER:OFFSET} | |||
*${PARAMETER:OFFSET:LENGTH} | |||
Usar valor por defecto | |||
*${PARAMETER:-WORD} # Si es nulo o vacío | |||
*${PARAMETER-WORD} # Si es nulo. Si la variable está vacía no imprime el valor por defecto. | |||
Asignar valor por defecto | |||
*${PARAMETER:=WORD} # Si es nulo o vacío. | |||
*${PARAMETER=WORD} # Sólo asigna valor si es nulo. | |||
Valor alternativo | |||
*${PARAMETER:+WORD} # Si está declarada y con contenido da un valor alternativo. | |||
*${PARAMETER+WORD} # Da un valor alternativo sólo si está declarada aunque su contenido sea nulo . | |||
Error si no está declarado | |||
*${PARAMETER:?WORD} # Error si no está declarado o es nulo. | |||
*${PARAMETER?WORD} # error si no está declarado, si es nulo no da error. | |||
== Arrays == | == Arrays == | ||
Línea 330: | Línea 387: | ||
a=$((a+7)) # POSIX-compatible version of previous code. | a=$((a+7)) # POSIX-compatible version of previous code. | ||
if test $((a%4)) = 0; then ... | if test $((a%4)) = 0; then ... | ||
Las expressions aritmèticas tambien aceptan resultados condicionales: | |||
echo $((2<3?4:5)) | |||
No se recomienda usar $[...] porque se considera obsoleta. | |||
via: [http://mywiki.wooledge.org/ArithmeticExpression] | via: [http://mywiki.wooledge.org/ArithmeticExpression] | ||
== if == | |||
El if en bash lo que hace es evaluar la variable $? resultante de la ejecución de un comando. Por tanto, el if se puede poner antes de cualquier comando. | |||
{{nota|$? es una variable que guarda un 0 si el comando anterior se ha ejecutado con éxito (no es lo mismo que sin errores) y un 1 o más si ha finalizado sin éxito, ya sea por un error o porque no ha dado resultado}} | |||
Ejecuta esto después de un comando exitoso o no exitoso: | |||
<syntaxhighlight lang="bash"> | |||
$ echo -e ":\\0$(($??50:51))"; | |||
</syntaxhighlight> | |||
Para hacer un if tradicional como en [[C]] que evalúa una expresión lógica se puede hacer con los comandos [] o [[]]. El [ es un comando que tiene hasta manual. Es otra forma del comando test, pero más parecido sintácticamente a otros lenguajes de programación. | |||
Así, se pueden hacer muchos if: | |||
<syntaxhighlight lang="bash"> | |||
$ if test 1 -le 3; then echo "1 es menor que 3"; fi | |||
$ if [ 1 -le 3 ]; then echo "1 es menor que 3"; fi | |||
$ if [[ 1 -le 3 ]]; then echo "1 es menor que 3"; fi | |||
$ if grep lliurex /etc/passwd > /dev/null; then echo "El usuario Lliurex existe"; fi | |||
</syntaxhighlight> | |||
En ocasiones se puede evitar el if, uniendo comandos con '''&&''' o '''||'''. En el caso de &&, el segundo comando sólo se ejecuta si el primero ha acabado con éxito. En el caso de || sólo se ejecuta si el primero ha terminado sin éxito. | |||
Estos son los comandos anteriores sin if: | |||
<syntaxhighlight lang="bash"> | |||
$ test 1 -le 3 && echo "1 es menor que 3" | |||
$ [ 1 -le 3 ] && echo "1 es menor que 3" | |||
$ [[ 1 -le 3 ]] && echo "1 es menor que 3" | |||
$ grep lliurex /etc/passwd > /dev/null && echo "El usuario Lliurex existe" | |||
</syntaxhighlight> | |||
== while == | |||
El comportamiento es igual que el if, evalúa el éxito del comando que va a continuación. | |||
== for == | |||
Este comando se comporta de manera distinta a lenguajes como C. El for de bash sólo recorre una lista que le has pasado. Por ejemplo: | |||
$ for i in 1 2 3; do echo $i; done | |||
Si queremos un rango más ámplio se puede usar {..}: | |||
$ for i in {1..1000}; do echo $i; done | |||
Si queremos aumentar la i de 2 en 2, por ejemplo: | |||
$ for i in $(seq 1 2 1000); do echo $i; done | |||
Si necesitamos una sintaxis parecida a C: | |||
$ for ((i=0;i<10;i++)); do echo $i; done | |||
==Funciones== | |||
ftp://ftp.monash.edu.au/pub/linux/docs/LDP/abs/html/functions.html | |||
http://blog.joncairns.com/2013/08/what-you-need-to-know-about-bash-functions/ | |||
== Enlaces == | == Enlaces == | ||
Línea 341: | Línea 461: | ||
http://serverfault.com/questions/7503/how-to-determine-if-a-bash-variable-is-empty | http://serverfault.com/questions/7503/how-to-determine-if-a-bash-variable-is-empty | ||
http://www.kfirlavi.com/blog/2012/11/14/defensive-bash-programming | |||
http://tldp.org/LDP/abs/html/writingscripts.html | |||
http://mywiki.wooledge.org/BashPitfalls | |||
https://lucasfcosta.com/2019/04/07/streams-introduction.html |
Revisión actual - 09:42 15 abr 2019
Estructura de un script
La estructura básica de un script de Bash es:
#!/bin/bash
# Indicamos que se debe ejecutar con el shell bash
# comandos del script
# Tenemos a nuestra disposición las variables $1, $2.. $*, $#, $@, $0
exit 0
# Con esta salida indicamos a otro posible script que lo invoque que ha terminado correctamente.
Redirecciones
$ cmd > fichero
Redirige la salida estandar a un fichero.
$ cmd 2> fichero
Redirige la salida de error a un fichero.
$ cmd >> fichero
Añade a un fichero la salida estandar sin borrar el contenido anterior.
$ cmd 2>> fichero
Añade los errores al final de un fichero.
$ cmd &> fichero
Redirige la salida estandar y la de error a un fichero.
$ cmd > fichero 2>&1
$ cmd 2>&1 >>ficheroPorque no funcionará, ya que evalúa de izquierda a derecha. Es decir, primero envía el error a la salida estándar 1 que es la terminal y luego la salida estándar la modifica por el fichero. Pero ya ha salido el error por pantalla
Otra manera de redirigir las dos salidas a un fichero. En este caso, la estándar va al fichero y la de error va a la estándar.
$ cmd > /dev/null $ cmd 2> /dev/null $ cmd &> /dev/null
Descartar la salida estándar, la de error o las dos.
$ cmd < fichero
Redirige el contenido de un fichero a la entrada estandar del comando.
$ cmd << FIN linea1 linea2 FIN
Redirige una serie de líneas a un comando. Esto se llama Here-Document. La palabra FIN sólo es para indicar el final de las líneas y puede ser cualquier otra.
$ cmd <<< "palabra"
Redirecciona el texto al comando. Se llama here-string.
$ exec 2> fichero
Redirige la salida de error a un fichero para todos los comandos. El comando exec es uno de los llamados built-in de bash. Permite ejecutar un comando sustituyéndo al shell actual o, los que nos interesa ahora, gestionar las redirecciones.
$ exec 3< fichero
Abre un fichero para leer usando un nuevo descriptor.
$ exec 3> fichero
Abre un fichero para escribir usando un nuevo descriptor.
$ exec 3<> fichero
Abre un fichero para leer o escribir usando el descriptor 3.
$ exec 3>&-
Cierra un descriptor de fichero.
# open it
exec 3< input.txt
# for example: read one line from the file(-descriptor)
read -u 3 LINE # la opción -u en read permite seleccionar el descriptor de fichero
# or
read LINE <&3
# finally, close it
exec 3<&-
Si queremos unir las salidas de varios comandos seguidos, podemos usar las {}:
ERROR=$( { ./useless.sh | sed s/Output/Useless/ > outfile; } 2>&1 )
http://mywiki.wooledge.org/BashFAQ/032
read interactivo de ficheros
$ seq 30 > numeros
Observemos este script:
#!/bin/bash
# cada 10 linies espera a que l'usuari escriga alguna cosa
while read Num
do
let ContLin++ # Contando...
echo -n "$Num " # -n para no saltar línea
((ContLin % 10)) > /dev/null || read
done < numeros
El resultado es este:
1 2 3 4 5 6 7 8 9 10 12 13 14 15 16 17 18 19 20 21 23 24 25 26 27 28 29 30
Y no se espera a que el usuario ponga nada.
Esto es porque el read de dentro del bucle lee también del fichero. Se puede forzar a que lea de la terminal:
#!/bin/bash
# cada 10 linies espera a que l'usuari escriga alguna cosa
while read Num
do
let ContLin++ # Contando...
echo -n "$Num " # -n para no saltar línea
((ContLin % 10)) > /dev/null || read '''< /dev/tty'''
done < numeros
O se puede crear un descriptor de fichero y modificar el read del while:
#!/bin/bash
# cada 10 linies espera a que l'usuari escriga alguna cosa
exec 3< numeros
while read Num '''<&3'''
do
let ContLin++ # Contando...
echo -n "$Num " # -n para no saltar línea
((ContLin % 10)) > /dev/null || read
done
Process Substitution
Los comandos que necesitan un fichero, pueden ejecutarse con el |, por ejemplo el wc puede hacerse:
$ cat /etc/passwd | wc -l
A veces un comando acepta como entrada dos ficheros. En ese caso, el ejemplo anterior no se puede seguir. Para solucionarse, se pueden usar los () después de una < [1]
$ diff <(ls $first_directory) <(ls $second_directory)
Parámetros, variables y argumentos
Un parámetro es una entidad referenciada por su nombre, un número o un carácter especial.
- Si tiene nombre se llama Variable
- Si es un número se considera parámetros posicionales y tienen que ver con los argumentos que se le pasa a una función o script.
- $0: Nombre del script.
- $1,$2,$3...$9 Argumentos. En principio no se puede poner más de 9 argumentos. Si se quiere poner 10 o más, se puede recurrir al shift o poniéndolo como ${12}
- Si son caracteres especiales tienen diversos significados:
- $# Candidad de argumentos
- $* Todos los argumentos. Es equivalente a $@ si se ponen sin dobles comillas. Con dobles comillas, $* son todos los argumentos seguidos y como separador el primer caracter del $IFS y $@ son todos los argumentos separados.
$ function f(){ IFS='-'; echo $*; echo "$*"; echo $@; echo "$@";}; f 2 3 4 5 "6 7"
- $? : a 0 si el comando anterior ha terminado con éxito. A 1 o más si ha dado erroro a terminado sin éxito. Es el Exit Status
- $_ : El último argumento del comando anterior
- $$ : El PID del proceso actual
- $! : El PID del último proceso ejecutado en background.
$ sleep 1000 & ps; echo -e "\nProceso actual: $$\nProceso en background: $!";
- $- : La lista de opciones del proceso actual.
shift
Este comando permite usar un mismo número de argumento para ir recorriéndolos.
En este ejemplo se ve bastante bien. Via
while test -n "$1"; do
case "$1" in
-a)
opciona=$2
shift
;;
-b)
opcionb=$2
shift
;;
-c)
opcionc=$2
shift
;;
-d)
opciond=$2
shift
;;
*)
echo "Unknown argument: $1"
exit 0
;;
esac
shift
done
El while recorre todos los argumentos, aunque sólo comprueba el $1 en cada iteración, se queda con $2 y pasa al siguiente. De esta manera, el órden de los argumentos y sus opciones no es relevante.
# miscript -a opciona -b opcionb -d opciond # miscript -b opcionb -c opcionc
Interpretación de los argumentos por el shell
El shell se encarga de sustituir los caracteres de sustitución y las variables antes de pasar los argumentos a un comando o script. Esto se puede ver con este script:
#!/bin/bash
echo $*
echo $#
Si se invoca de estas maneras, por ejemplo:
$ ./argumentos * $ ./argumentos p[a-z] $ ./argumentos {1..100} $ ./argumentos $HOME
A esto se le llama Expansión de parámetros. Se pueden hacer muchas cosas manipulando esta expansión:
Uso simple:
- $PARAMETER
- ${PARAMETER}
Indirección:
- ${!PARAMETER}
$ a=100 $ b=a $ echo "$b=${!b}"
Modificación de mayúsculas:
- ${PARAMETER^} # La primera
- ${PARAMETER^^} # todas
- ${PARAMETER,} # la primera en minúscula
- ${PARAMETER,,} # todas
- ${PARAMETER~} # invertir
- ${PARAMETER~~}
Expansión del nombre de la variable:
- ${!PREFIX*} # todas las variables que tengan ese prefijo
- ${!PREFIX@}
Quitar substrings:
- ${PARAMETER#PATTERN} #Quita el prefijo más corto que cumpla el patrón
- ${PARAMETER##PATTERN} # Quita el prefijo más largo que cumpla el patrón
- ${PARAMETER%PATTERN} # Quita sufijo más corto
- ${PARAMETER%%PATTERN} # quita el sufijo más largo
Buscar y reemplazar
- ${PARAMETER/PATTERN/STRING} # Una vez
- ${PARAMETER//PATTERN/STRING} # Todas las ocurrencias
- ${PARAMETER/PATTERN} # Lo quita una vez
- ${PARAMETER//PATTERN} # Lo quita todas las ocurrencias
Longitud de la cadena
- ${#PARAMETER}
Substrings:
- ${PARAMETER:OFFSET}
- ${PARAMETER:OFFSET:LENGTH}
Usar valor por defecto
- ${PARAMETER:-WORD} # Si es nulo o vacío
- ${PARAMETER-WORD} # Si es nulo. Si la variable está vacía no imprime el valor por defecto.
Asignar valor por defecto
- ${PARAMETER:=WORD} # Si es nulo o vacío.
- ${PARAMETER=WORD} # Sólo asigna valor si es nulo.
Valor alternativo
- ${PARAMETER:+WORD} # Si está declarada y con contenido da un valor alternativo.
- ${PARAMETER+WORD} # Da un valor alternativo sólo si está declarada aunque su contenido sea nulo .
Error si no está declarado
- ${PARAMETER:?WORD} # Error si no está declarado o es nulo.
- ${PARAMETER?WORD} # error si no está declarado, si es nulo no da error.
Arrays
Ver Arrays en Bash
Tests
El [ para hacer test se puede usar en todos los shell que cumplen con POSIX. Mientras que el doble [[ está en bash y otros shells modernos.
#POSIX [ "$variable" ] || echo 'variable is unset or empty!' >&2 [ -f "$filename" ] || printf 'File does not exist: %s\n' "$filename" >&2
Saber más:
$ whereis [ [: /usr/bin/[ /usr/share/man/man1/[.1.gz $ man [
Los dobles [[ no son un comando, sino que forman parte del bash.
Funciones | ||
---|---|---|
Función | Expresión | Ejemplo |
Comparación de strings | < , > , = , == != |
[[ a > b ]] || echo "a does not come before b" [[ az < za ]] && echo "az comes before za" |
Comparación de integers | -ge , -gt , -lt , -le , -eq , -ne |
[[ 6 -ne 20 ]] && echo "6 is not equal to 20" |
Condicional | || , && |
[[ -n $var && -f $var ]] && echo "$var is a file" |
Agrupar | (...) (obsoleto) |
[[ $var = img* && ($var = *.png || $var = *.jpg) ]] && echo "$var starts with img and ends with .jpg or .png" |
Patrones | = o == |
[[ $name = a* ]] || echo "name does not start with an 'a': $name" |
Expresión regular | =~ |
[[ $(date) =~ ^Fri\ ...\ 13 ]] && echo "It's Friday the 13th" |
Negación | ! |
[[ ! -u $file ]] && echo "$file is not a setuid file" |
Ejemplos con ficheros:
[[ -e $config ]] && echo "config file exists: $config" [[ $file0 -nt $file1 ]] && echo "$file0 is newer than $file1" [[ $input -ef $output ]] && { echo "will not overwrite input file: $input"; exit 1; }
Se recomienda usar [[ si el nombre de los ficheros puede tener espacios en blanco:
file="file name" [[ -f $file ]] && echo "$file is a file"
via:[2]
Expresiones aritméticas
El comando típico para hacer operaciones matemáticas es let
let a=17+23 let a="17 + 23"
Pero es más útil usar (()), ya que puedes usar un $ delante $(()). Permite usar espacios en blanco y no necesita usar $ en las variables porque no están permitidas cadenas dentro.
((a=$a+7)) # Add 7 to a ((a = a + 7)) # Add 7 to a. Identical to the previous command. ((a += 7)) # Add 7 to a. Identical to the previous command. ((a = RANDOM % 10 + 1)) # Choose a random number from 1 to 10. # % is modulus, as in C. # (( )) may also be used as a command. > or < inside (( )) means # greater/less than, not output/input redirection. if ((a > 5)); then echo "a is more than 5"; fi
Los (()) sin $ delante sólo va en bash. Los siguientes ejemplos son compatibles con todos los POSIX
a=$((a+7)) # POSIX-compatible version of previous code. if test $((a%4)) = 0; then ...
Las expressions aritmèticas tambien aceptan resultados condicionales:
echo $((2<3?4:5))
No se recomienda usar $[...] porque se considera obsoleta.
via: [3]
if
El if en bash lo que hace es evaluar la variable $? resultante de la ejecución de un comando. Por tanto, el if se puede poner antes de cualquier comando.
Ejecuta esto después de un comando exitoso o no exitoso:
$ echo -e ":\\0$(($??50:51))";
Para hacer un if tradicional como en C que evalúa una expresión lógica se puede hacer con los comandos [] o [[]]. El [ es un comando que tiene hasta manual. Es otra forma del comando test, pero más parecido sintácticamente a otros lenguajes de programación.
Así, se pueden hacer muchos if:
$ if test 1 -le 3; then echo "1 es menor que 3"; fi
$ if [ 1 -le 3 ]; then echo "1 es menor que 3"; fi
$ if [[ 1 -le 3 ]]; then echo "1 es menor que 3"; fi
$ if grep lliurex /etc/passwd > /dev/null; then echo "El usuario Lliurex existe"; fi
En ocasiones se puede evitar el if, uniendo comandos con && o ||. En el caso de &&, el segundo comando sólo se ejecuta si el primero ha acabado con éxito. En el caso de || sólo se ejecuta si el primero ha terminado sin éxito.
Estos son los comandos anteriores sin if:
$ test 1 -le 3 && echo "1 es menor que 3"
$ [ 1 -le 3 ] && echo "1 es menor que 3"
$ [[ 1 -le 3 ]] && echo "1 es menor que 3"
$ grep lliurex /etc/passwd > /dev/null && echo "El usuario Lliurex existe"
while
El comportamiento es igual que el if, evalúa el éxito del comando que va a continuación.
for
Este comando se comporta de manera distinta a lenguajes como C. El for de bash sólo recorre una lista que le has pasado. Por ejemplo:
$ for i in 1 2 3; do echo $i; done
Si queremos un rango más ámplio se puede usar {..}:
$ for i in {1..1000}; do echo $i; done
Si queremos aumentar la i de 2 en 2, por ejemplo:
$ for i in $(seq 1 2 1000); do echo $i; done
Si necesitamos una sintaxis parecida a C:
$ for ((i=0;i<10;i++)); do echo $i; done
Funciones
ftp://ftp.monash.edu.au/pub/linux/docs/LDP/abs/html/functions.html http://blog.joncairns.com/2013/08/what-you-need-to-know-about-bash-functions/
Enlaces
http://mywiki.wooledge.org/BashGuide/Practices
http://www.pixelbeat.org/programming/shell_script_mistakes.html
http://serverfault.com/questions/7503/how-to-determine-if-a-bash-variable-is-empty
http://www.kfirlavi.com/blog/2012/11/14/defensive-bash-programming
http://tldp.org/LDP/abs/html/writingscripts.html
http://mywiki.wooledge.org/BashPitfalls
https://lucasfcosta.com/2019/04/07/streams-introduction.html