├── 00-hola-mundo.sh ├── 01-exit-status.sh ├── 02-variables.sh ├── 02a-parametros.sh ├── 02b-arreglos.sh ├── 03-comandos-utiles.sh ├── 04-redirecciones.sh ├── 05-estructuras-de-control.sh ├── 05a-if-case-select.sh ├── 05b-for.sh ├── 05c-while-until.sh ├── 06-funciones.sh ├── 06a-alcance-de-variables.sh ├── 07-sustitucion-de-comandos.sh ├── 08-break-N.sh ├── 08-break.sh ├── 09-continue-N.sh ├── 09-continue.sh ├── 10-dereferenciacion-variables.sh ├── 11-subshells.sh ├── 12-subshells-scope-variables.sh ├── 13-subshells-2.sh ├── README.md ├── ejercicios └── iso │ ├── tp3-14.sh │ └── tp3-18.sh └── tips ├── 01-validacion-parametros.sh └── 02-validacion-archivos.sh /00-hola-mundo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Esta, y todas las líneas que comienzan con "#" son comentarios, y no 4 | # serán interpretados al ejecutar el script. 5 | # 6 | # Si la primera linea del script comienza con "#!", se considera el path 7 | # al intérprete a utilizar y se la llama "shebang". 8 | # 9 | # Otros ejemplos de shebangs pueden ser: 10 | # 11 | # #|/bin/zsh 12 | # #!/bin/php 13 | # #!/usr/bin/env ruby 14 | # #!/usr/bin/env python 15 | # #!/usr/bin/awk 16 | # 17 | # Es una buena práctica agregar un breve comentario que documente qué hacen 18 | # los scripts que escribimos, para que nuestro "futuro yo" no quiera volver 19 | # en el tiempo a golpearnos la cabeza contra el teclado por no entender qué 20 | # quisimos hacer en el script de 625 lineas que dejó de funcionar... 21 | 22 | echo "¡Hola mundo!" 23 | 24 | # Todo script termina con un estado o "exit status" particular: un valor 25 | # numérico entre 0 y 255, especificado mediante el comando `exit`, con 26 | # la siguiente convención: 27 | # - si el valor el 0, se considera exitoso el resultado del script 28 | # - si el valor es mayor que 0, se considera que ocurrió algo fuera de lo 29 | # deseado. ¿Qué indica cada valor (entre 1 y 255)? Eso lo define cada 30 | # script 31 | # 32 | # Si el script termina sin invocar el comando `exit`, se retorna un valor 33 | # por defecto. ¿Cuál? Ejecutar este script y luego chequear su exit status 34 | # con la variable especial $? 35 | # 36 | # De hecho, ya que todo comando o script en bash retorna un exit status, 37 | # podemos ver cuál es el que retornó el comando `echo` anterior: 38 | 39 | echo "El exit status fue $?" 40 | 41 | # Los comandos en un script se suelen separar con saltos de línea ("\n"), lo 42 | # cual marca el fin de un comando y el comienzo de otro; no obstante esto, 43 | # podemos utilizar el caracter punto y coma (";") como delimitador de líneas. 44 | # Por ejemplo, en lugar de escribir las siguientes tres líneas: 45 | 46 | echo Uno 47 | echo Dos 48 | echo Tres 49 | 50 | # podríamos escribirlas de la siguiente forma: 51 | 52 | echo Uno; echo Dos; echo Tres 53 | -------------------------------------------------------------------------------- /01-exit-status.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Este script retorna un exit status 3, con lo que asumiremos que su 4 | # resultado no fue el esperado. En la documentación de nuestro script 5 | # deberíamos describir qué posibles exit status puede tener, 6 | # indicando qué significado tiene cada uno de los valores. 7 | 8 | exit 3 9 | -------------------------------------------------------------------------------- /02-variables.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Bash no tiene tipado fuerte, por lo que sus variables pueden tomar valores 4 | # de distintos tipos (cadenas de texto, numéricos, arreglos) sin necesidad 5 | # de hacer declaraciones. 6 | # 7 | # Los nombres de variable pueden contener caracteres alfanuméricos y guiones 8 | # bajos ("_"), pero no pueden comenzar con números. Bash es en esencia case 9 | # sensitive, por lo que "A" y "a" son distintas variables. 10 | # 11 | # Las variables se asignan con "=" sin espacios entre el nombre de la variable 12 | # y su valor: 13 | 14 | MI_VARIABLE="tiene un valor" 15 | la_2da_variable="tiene otro valor" 16 | mi_variable="es distinta a MI_VARIABLE" 17 | numerica=8 18 | numerica="me arrepenti, ahora es un string" 19 | arreglo=(1 2 3 4) 20 | 21 | # Los valores de las variables se acceden agregando un "$" al comienzo del 22 | # nombre de la variable: 23 | 24 | echo $MI_VARIABLE 25 | 26 | # Y los valores se pueden interpolar (incluir en medio de un string) siempre 27 | # que usemos comillas dobles ("): 28 | 29 | echo "MI_VARIABLE => $MI_VARIABLE" 30 | echo "la_2da_variable => $la_2da_variable" 31 | echo "mi_variable => $mi_variable" 32 | echo "numerica => $numerica" 33 | 34 | # Con comillas simples no se realiza interpolación alguna: 35 | 36 | echo 'MI_VARIABLE => $MI_VARIABLE' 37 | 38 | # Los valores de la variable se pueden acceder, opcionalmente, delimitando con 39 | # llaves ("{}") el nombre de la variable: 40 | 41 | echo ${MI_VARIABLE} 42 | -------------------------------------------------------------------------------- /02a-parametros.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Los scripts pueden recibir parámetros al momento de ejecutarlos. Para acceder 4 | # a estos parámetros, existe un conjunto de variables especiales: 5 | # - $0 es el nombre del script tal como se lo invocó 6 | # - $1 a $9 son los parámetros en el orden que se los recibió, del primero al 7 | # noveno 8 | # - $* son todos los parámetros que recibió el script 9 | # - $# es la cantidad de parámetros que recibió el script 10 | 11 | echo "Este script fue invocado como: $0" 12 | echo "El primer parámetro recibido fue: $1" 13 | echo "El segundo parámetro recibido fue: $2" 14 | echo "El tercer parámetro recibido fue: $3" 15 | echo "El cuarto parámetro recibido fue: $4" 16 | echo "El quinto parámetro recibido fue: $5" 17 | echo "El sexto parámetro recibido fue: $6" 18 | echo "El séptimo parámetro recibido fue: $7" 19 | echo "El octavo parámetro recibido fue: $8" 20 | echo "El noveno parámetro recibido fue: $9" 21 | echo "Todos los parámetros recibidos fueron: $*" 22 | echo "El script recibió $# parámetros" 23 | 24 | # Podemos probar ejecutar este mismo script pasando distintos parámetros y ver 25 | # qué pasa. Por ejemplo: 26 | # 27 | # ./02a-parametros.sh parametro 1 parametro 2 28 | # ./02a-parametros.sh "parametro 1" "parametro 2" 29 | # ./02a-parametros.sh uno dos tres cuatro cinco 6 siete ocho nueve 10 11 12 13 30 | # bash 02a-parametros.sh 31 | -------------------------------------------------------------------------------- /02b-arreglos.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Un tipo especial de variables es el arreglo (o lista, o array). Un arreglo 4 | # contiene una lista de valores que son básicamente strings separados por 5 | # espacios. En bash, tanto para una lista de valores como para un arreglo o los 6 | # parámetros de un script, el separador de elementos son los caracteres en 7 | # blanco (espacios). 8 | # 9 | # Los arreglos utilizan los paréntesis ("()") para delimitarse: 10 | 11 | arreglo_vacio=() 12 | arreglo_con_elementos=(1 2 "otro elemento" mas elementos) 13 | 14 | # Para acceder a los elementos del arreglo utilizaremos índices numéricos, 15 | # comenzando en 0, con la siguiente sintáxis: 16 | 17 | echo "Primer elemento de arreglo_con_elementos => ${arreglo_con_elementos[0]}" 18 | echo "Tercer elemento de arreglo_con_elementos => ${arreglo_con_elementos[2]}" 19 | 20 | # Todos los elementos del arreglo, en lista, se pueden acceder mediante 21 | # cualquiera de las siguientes opciones (son equivalentes): 22 | 23 | echo "Todos los elementos con * => ${arreglo_con_elementos[*]}" 24 | echo "Todos los elementos con @ => ${arreglo_con_elementos[@]}" 25 | 26 | # y la cantidad de elementos del arreglo se acceden agregando un numeral ("#") 27 | # antes del nombre de variable del arreglo: 28 | 29 | echo "La cantidad de elementos en arreglo_con_elementos es ${#arreglo_con_elementos[*]}" 30 | echo "La cantidad de elementos en arreglo_vacio es ${#arreglo_vacio[*]}" 31 | 32 | # Similarmente a cómo se accede a los valores de los elementos, se les pueden 33 | # asignar nuevos valores o sobreescribir los existentes (recordar que en bash 34 | # el $ hace referencia al valor de la variable, por lo que no lo utilizaremos 35 | # para asignar sobre la variable): 36 | 37 | arreglo_vacio[0]=9 38 | echo "arreglo_vacio => ${arreglo_vacio[*]}" 39 | 40 | # [TIP] Una forma sencilla de agregar elementos al final del arreglo: 41 | 42 | arreglo_vacio=(${arreglo_vacio[*]} "nueve") 43 | echo "arreglo_vacio => ${arreglo_vacio[*]}" 44 | 45 | # Para borrar elementos de un arreglo, usamos el comando `unset` y hacemos 46 | # referencia a la posición del arreglo, pero no a su valor: 47 | 48 | unset arreglo_vacio[0] 49 | echo "arreglo_vacio => ${arreglo_vacio[*]}" 50 | 51 | # Notar que no se realizan corrimientos, si intento borrar nuevamente el primer 52 | # elemento no voy a estar modificando el arreglo porque el elemento en la 53 | # posición 0 ya fue borrado): 54 | 55 | unset arreglo_vacio[0] 56 | echo "arreglo_vacio => ${arreglo_vacio[*]}" 57 | -------------------------------------------------------------------------------- /03-comandos-utiles.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Algunos comandos útiles que podemos utilizar en bash: 4 | # 5 | # - man 6 | # - help 7 | # - info 8 | # 9 | # - echo 10 | # - cat 11 | # - grep 12 | # - test 13 | # - wc 14 | # - read 15 | # - cut 16 | # - tr 17 | # - tar 18 | # - gzip 19 | # - ls 20 | # - find 21 | # - let 22 | # - expr 23 | # 24 | # Para ver la documentación de cualquiera de estos comandos, ejecutar 25 | # 26 | # man # Para los comandos externos 27 | # info # Para los comandos internos 28 | # help # Para los comandos internos 29 | # 30 | # Por ejemplo: 31 | 32 | man man 33 | info info 34 | help help 35 | -------------------------------------------------------------------------------- /04-redirecciones.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # La salida de cualquier comando puede redirigirse a un archivo, haciendo 4 | # posible - por ejemplo - generar fácilmente archivos mientras procesamos datos, 5 | # o a otro comando. 6 | # 7 | # Las redirecciones a archivo pueden ser de dos tipos: destructivas y no 8 | # destructivas. Las primeras "pisan" el contenido del archivo con la salida 9 | # que está siendo redirigida; mientras que las no destructivas agregan al final 10 | # del archivo destino, manteniendo el contenido existente. 11 | 12 | # Redirección destructiva: ">" 13 | echo "Hola!" > /tmp/miarchivo 14 | # Redirección no destructiva: ">>" 15 | echo "¿Cómo va?" >> /tmp/miarchivo 16 | 17 | less /tmp/miarchivo 18 | 19 | echo "Ahora pisé todo" > /tmp/miarchivo 20 | 21 | less /tmp/miarchivo 22 | 23 | # Las redirecciones a otro comando se realizan mediante el uso de pipes ("|"). 24 | # La salida estándar (stdout) de un comando es pasada a otro, el cual la recibe 25 | # en su entrada estándar (stdin): 26 | 27 | ls | grep redirec 28 | 29 | # Todo script tiene 3 file descriptors (archivos abiertos) por defecto: 30 | # - 0 => la entrada estándar (stdin) 31 | # - 1 => la salida estándar (stdout) 32 | # - 2 => la salida de error (stderr) 33 | # 34 | # Podemos utilizar estos file descriptors para redirigir, por ejemplo, la salida 35 | # de error a un archivo para poder ver qué ocurrió: 36 | 37 | find /dev -name stdin 2> /tmp/errores.log 38 | less /tmp/errores.log 39 | 40 | # Y podemos combinar lo anterior: 41 | 42 | ls | grep redirec > /tmp/salida.txt 43 | less /tmp/salida.txt 44 | -------------------------------------------------------------------------------- /05-estructuras-de-control.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Como en otros lenguajes de programación, bash dispone de estructuras de 4 | # control que permiten tomar decisiones condicionales, realizar operaciones 5 | # repetitivas, etc. 6 | # 7 | # Antes de introducir las estructuras de control, es necesario comprender 8 | # cómo funcionan los valores booleanos (verdadero o falso) en bash: el cero es 9 | # considerado verdadero, mientras que cualquier otro valor es considerado falso. 10 | # ¿Se ve la relación con el exit status de los scripts? 11 | # 12 | # Existen dos comandos que simbolizan el verdadero y falso: `true` y `false` 13 | # respectivamente. ¿Cómo funcionan? Utilizan su exit status para representar 14 | # la veracidad o falsedad de su valor: 15 | 16 | true 17 | echo "El comando true retornó el exit status => $?" 18 | 19 | false 20 | echo "El comando false retornó el exit status => $?" 21 | 22 | # Existe un comando en bash que permite evaluar condiciones lógicas: el comando 23 | # `test`. Este comando recibe diferentes parámetros que representan una 24 | # expresión lógica, la evalúa y retorna un valor booleano. Un alias para el 25 | # test son los corchetes ([ ]) -- tener en cuenta que siempre llevan espacios 26 | # alrededor! Para conocer en detalle las posibilidades que brinda este comando, 27 | # se puede ver su documentación mediante el siguiente comando: 28 | 29 | info test 30 | -------------------------------------------------------------------------------- /05a-if-case-select.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # La estructura de control básica es el IF. En bash, un if tiene mínimamente la 4 | # siguiente forma: 5 | 6 | if [ 1 = 1 ] 7 | then 8 | # Cuerpo para la rama if 9 | echo "1 es igual a 1" 10 | fi 11 | 12 | # Agregando un único caso alternativo, la estructura es la siguiente: 13 | 14 | if [ 1 = 1 ] 15 | then 16 | # Cuerpo para la rama if 17 | echo "1 es igual a 1" 18 | else 19 | # Cuerpo para la rama else 20 | echo "WAT?" 21 | fi 22 | 23 | # Y agregando más alernativas: 24 | 25 | if [ 1 = 1 ]; then 26 | # Cuerpo para la rama if 27 | echo "1 es igual a 1" 28 | elif [ 2 -gt 1 ]; then 29 | # Cuerpo para esta rama elif 30 | echo "2 es mayor que 1" 31 | elif [ true ]; then 32 | # Cuerpo para esta rama elif 33 | echo "true es siempre verdadero!" 34 | else 35 | # Cuerpo para la rama else 36 | echo "WAT?" 37 | fi 38 | 39 | # La selección es con el comando `case`. Notar que cada caso es delimitado por 40 | # dos puntos y coma (";;"), y que el último caso presentado funciona como 41 | # un comodín (o "wildcard"), es decir, cualquier opción coincidirá. 42 | 43 | sabor=pistacho 44 | echo "¿Qué opinás del helado sabor $sabor?" 45 | case $sabor in 46 | chocolate) 47 | echo "rico!" 48 | ;; 49 | vainilla) 50 | echo "pasable" 51 | ;; 52 | "dulce de leche") 53 | echo "lo más" 54 | echo "en serio. ¿Y lo probaste granizado?" 55 | ;; 56 | *) 57 | echo "puajjj" 58 | ;; 59 | esac 60 | 61 | # El orden de chequeo de coincidencia para los distintos casos es descendente 62 | # según cómo los declaramos. ¿Qué pasaría si cambiamos el ejemplo anterior por 63 | # el siguiente? 64 | 65 | sabor=chocolate 66 | echo "- ¿Qué opinás del helado sabor $sabor?" 67 | case $sabor in 68 | *) 69 | echo "- puajjj" 70 | ;; 71 | chocolate) 72 | echo "- rico!" 73 | ;; 74 | vainilla) 75 | echo "- pasable" 76 | ;; 77 | "dulce de leche") 78 | echo "- lo más" 79 | echo " en serio. ¿Y lo probaste granizado?" 80 | ;; 81 | esac 82 | 83 | # Otra forma más interactiva del ejemplo anterior puede ser pedir al usuario 84 | # que ingrese por teclado el sabor. Para esto utilizamos el comando `read`: 85 | 86 | echo "Ingresá un sabor de helado: " 87 | read sabor 88 | echo "- ¿Qué opinás del helado sabor $sabor?" 89 | case $sabor in 90 | chocolate) 91 | echo "- rico!" 92 | ;; 93 | vainilla) 94 | echo "- pasable" 95 | ;; 96 | "dulce de leche") 97 | echo "- lo más." 98 | echo " en serio. ¿Y lo probaste granizado?" 99 | ;; 100 | *) 101 | echo "- puajjj" 102 | ;; 103 | esac 104 | 105 | 106 | # El comando select presenta al usuario un conjunto de opciones para que elija 107 | # una y nos devuelve en una variable la opción elegida: 108 | 109 | echo "¿Querés saber qué opino de los sabores de helado? Elegí algunos" 110 | select sabor in chocolate vainilla "dulce de leche" pistacho salir 111 | do 112 | case $sabor in 113 | chocolate) 114 | echo "rico!" 115 | ;; 116 | vainilla) 117 | echo "pasable" 118 | ;; 119 | "dulce de leche") 120 | echo "lo más" 121 | ;; 122 | pistacho) 123 | echo "puajjj" 124 | ;; 125 | salir) 126 | echo "Me alegro haber podido aclararte esas dudas tan existenciales :)" 127 | break 128 | ;; 129 | esac 130 | done 131 | 132 | # Notar en el caso anterior que se incluyó un caso para salir del bucle que 133 | # realiza el `select`, de lo contrario éste se repetirá indefinidamente. 134 | # 135 | # Para cortar la ejecución de un bucle o loop, se utiliza el comando `break`, 136 | # y para saltear la iteración actual de cualquier bucle se utiliza el comando 137 | # `continue`. Pero esto lo veremos más adelante con ejemplos concretos. 138 | -------------------------------------------------------------------------------- /05b-for.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Los bucles for en bash iteran sobre listas de valores, como siempre, separadas 4 | # por espacios: 5 | 6 | for valor in uno dos 3 cuatro cinco seis 7 | do 8 | echo $valor 9 | done 10 | 11 | # Como los arreglos son listas, podemos iterar sobre ellos: 12 | 13 | arreglo=(uno tres 4 seis seventeen las canchas de paddle) 14 | for valor in ${arreglo[*]} 15 | do 16 | echo $valor 17 | done 18 | -------------------------------------------------------------------------------- /05c-while-until.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Los bloques while y until se ejecutan mientras una condición lógica sea 4 | # verdadera o falsa, respectivamente: 5 | 6 | i=1 7 | while [ $i -lt 10 ] 8 | do 9 | echo "Pasada $i" 10 | let i++ 11 | done 12 | echo "Fin del while: i => $i" 13 | 14 | i=1 15 | until [ $i -lt 10 ] 16 | do 17 | echo "Pasada $i" 18 | let i++ 19 | done 20 | echo "Fin del until: i => $i" 21 | -------------------------------------------------------------------------------- /06-funciones.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Las funciones en bash son como comandos nuevos, y al igual que los scripts 4 | # reciben parámetros por posición (se los accede mediante las variables $1, $2, 5 | # ..., $9, $* y $#). 6 | # 7 | # Hay dos formas de declarar las funciones: 8 | 9 | function imprimir { 10 | echo "Imprimiendo desde la función imprimir:" 11 | echo $* 12 | } 13 | 14 | borrar() { 15 | echo "Presioná ENTER para borrar la pantalla" 16 | read 17 | clear 18 | } 19 | 20 | # Las funciones pasan a ser igual que nuevos comandos, por lo que las podemos 21 | # utilizar tal como usamos los comandos propios de bash: 22 | 23 | imprimir uno dos tres 24 | borrar 25 | 26 | # Las funciones también tienen sus propios valores de retorno, que funcionan tal 27 | # como el exit status de los scripts, solo que no cortan la ejecución del script 28 | # entero, sino que solo terminan la función: 29 | 30 | function error { 31 | return 5 32 | } 33 | 34 | error 35 | echo "error devolvió un código de salida $?" 36 | -------------------------------------------------------------------------------- /06a-alcance-de-variables.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Las variables en bash tienen por defecto alcance global: una variable 4 | # definida en una parte del script se podrá acceder desde cualquier otra 5 | # parte del mismo (luego de que se haya ejecutado la porción de código que 6 | # la define). Esto es siempre así, excepto para las variables que se declaran 7 | # con el comando `local`, cuyo alcance será dentro de la función que las 8 | # declara y sus funciones anidadas. 9 | 10 | # Veamos primero un ejemplo del alcance global 11 | 12 | echo "== Globales" 13 | echo 14 | 15 | function pisa_variable_global { 16 | variable_global='Local' 17 | } 18 | 19 | variable_global='Global' 20 | 21 | echo "Antes de llamar a pisa_variable_global => $variable_global" 22 | pisa_variable_global 23 | echo "Después de llamar a pisa_variable_global => $variable_global" 24 | 25 | function define_variable_global2 { 26 | variable_global2="tiene su valor" 27 | } 28 | 29 | echo "Antes de llamar a define_variable_global2 => $variable_global2" 30 | define_variable_global2 31 | echo "Después de llamar a define_variable_global2 => $variable_global2" 32 | 33 | # Ahora un ejemplo similar, pero utilizando variables locales: 34 | 35 | echo 36 | echo "== Locales" 37 | echo 38 | 39 | function tiene_local { 40 | local mi_variable="tiene valor" 41 | 42 | function hija_de_tiene_local { 43 | echo "Adentro de hija_de_tiene_local => $mi_variable" 44 | mi_variable="le cambio el valor" 45 | echo "Adentro de hija_de_tiene_local (luego de cambiar el valor) => $mi_variable" 46 | } 47 | 48 | echo "Adentro de tiene_local => $mi_variable" 49 | hija_de_tiene_local 50 | echo "Adentro de tiene_local (luego de cambiar el valor) => $mi_variable" 51 | } 52 | 53 | tiene_local 54 | echo "Afuera de tiene_local => $mi_variable" 55 | -------------------------------------------------------------------------------- /07-sustitucion-de-comandos.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # La salida de los comandos y funciones puede tomarse para almacenarla en 4 | # variables o para ser interpolada en parte de un string. Para esto existen 5 | # dos formas: una mediante el uso del acento francés o grave ("`"), y otra 6 | # mediante el uso de "$(comando)". 7 | # 8 | # La regla básica es que la salida estándar del comando reemplazará exactamente 9 | # la invocación del mismo: 10 | 11 | archivos=`ls` 12 | directorios=$(find . -type d -maxdepth 0) 13 | 14 | echo "Archivos => $archivos" 15 | echo "Directorios => $directorios" 16 | 17 | echo "Mi nombre de usuario es `whoami`" 18 | 19 | for archivo in `ls | grep sustit`; do 20 | echo "Se encontró que el archivo $archivo coincide con la búsqueda" 21 | done 22 | -------------------------------------------------------------------------------- /08-break-N.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Con "break N" se esta diciendo que corte N niveles de bucles comenzando 4 | # a contar desde el bucle en donde se lo invoca. 5 | 6 | # En este ejemplo se va a imprimir el 1 del bucle I 7 | # y el 1 del bucle de Z. 8 | 9 | for z in `seq 1 10`; do 10 | 11 | echo $z 12 | 13 | for i in `seq 1 10`; do 14 | 15 | if [ $i -eq 2 ]; then 16 | break 2 17 | fi 18 | 19 | echo $i 20 | done; 21 | done; 22 | -------------------------------------------------------------------------------- /08-break.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Con "break" o "break 1" se esta diciendo que corte el bucle 4 | # donde se lo invoca. 5 | 6 | # En este ejemplo se va a imprimir SOLO el 1 del bucle I 7 | # por cada iteración del bucle de Z. 8 | 9 | # En este ejemplo se podría reemplazar "break" por "continue 2" 10 | # y el comportamiento seguiría siendo el mismo. 11 | 12 | for z in `seq 1 10`; do 13 | 14 | echo $z 15 | 16 | for i in `seq 1 10`; do 17 | 18 | if [ $i -eq 2 ]; then 19 | break 20 | fi 21 | 22 | echo $i 23 | done; 24 | done; 25 | -------------------------------------------------------------------------------- /09-continue-N.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Con "continue N" se esta indicando que continue ejecutando en la siguiente 4 | # iteración subiendo N niveles contando desde el bucle donde se lo invoca. 5 | 6 | # En este ejemplo por cada iteración del bucle Z se va a imprimir SOLO el 7 | # el número 1 del bucle I. 8 | 9 | # En este ejemplo se podría reemplazar "continue 2" por "break" 10 | # y el comportamiento seguiría siendo el mismo. 11 | 12 | for z in `seq 1 10`; do 13 | 14 | echo $z 15 | 16 | for i in `seq 1 10`; do 17 | 18 | if [ $i -eq 2 ]; then 19 | continue 2 20 | fi 21 | 22 | echo $i 23 | done; 24 | done; 25 | -------------------------------------------------------------------------------- /09-continue.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Con "continue" o "continue 1" se esta diciendo que salte una iteración 4 | # del bucle en donde se lo invoca. 5 | 6 | # En este ejemplo se va a imprimir del 1 al 10, excepto el 2, del bucle I 7 | # por cada iteración del bucle de Z. 8 | 9 | for z in `seq 1 10`; do 10 | 11 | echo $z 12 | 13 | for i in `seq 1 10`; do 14 | 15 | if [ $i -eq 2 ]; then 16 | continue 17 | fi 18 | 19 | echo $i 20 | done; 21 | done; 22 | -------------------------------------------------------------------------------- /10-dereferenciacion-variables.sh: -------------------------------------------------------------------------------- 1 | for i in `seq 1 2`; do 2 | eval echo "El valor del parametro $i es: " \$$i 3 | done 4 | 5 | uno="Valor de variable 'uno'" 6 | dos="Valor de variable 'dos'" 7 | 8 | echo ${!1} 9 | echo ${!2} 10 | 11 | -------------------------------------------------------------------------------- /11-subshells.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Este ejemplo imprime los valores de las varialbes $$, $PPID, $BASHPID y $BASH_SUBSHELL 4 | # para cada una de las instancias shell, es decir una por la shell principal y una por cada 5 | # shell que generan los pipes. 6 | # Los dos primeros 'echo' imprimiran el mismo valor para $BASH_SUBSHELL debido a que la shell expande el valor de las 7 | # variables correspondiente al primer termino del pipe antes de finalizar el spawn de la subshell. Es decir, la shell 8 | # hace el fork, obtiene el PID de la subshell (por eso si imprime tal valor en $BASHPID y no el de la shell) y luego hace el execv. 9 | 10 | # La subshell más interna ejecuta un 'pstree' para ver la jerarquía de procesos en ese momento. Los 'echo' antecesores 11 | # no se ven porque una vez que imprimieron el valor terminan. 12 | 13 | echo "Shell ---- BASH_SUBSHELL=$BASH_SUBSHELL BASHPID=$BASHPID PID=$$ PPID=$PPID" 14 | 15 | echo "Subshell 1 BASH_SUBSHELL=$BASH_SUBSHELL BASHPID=$BASHPID PID=$$ PPID=$PPID" | \ 16 | ( read v; echo $v; echo "Pirimer anidamiento $BASH_SUBSHELL - $BASHPID"; echo "Subshell 2 BASH_SUBSHELL=$BASH_SUBSHELL BASHPID=$BASHPID PID=$$ PPID=$PPID" | \ 17 | ( read v; echo $v; echo "Segundo anidamiento $BASH_SUBSHELL - $BASHPID"; echo "Subshell 3 BASH_SUBSHELL=$BASH_SUBSHELL BASHPID=$BASHPID PID=$$ PPID=$PPID" | \ 18 | ( read v; echo $v; echo "Tercer anidamiento $BASH_SUBSHELL - $BASHPID"; echo "Subshell 4 BASH_SUBSHELL=$BASH_SUBSHELL BASHPID=$BASHPID PID=$$ PPID=$PPID"; pstree ) ) ) 19 | 20 | 21 | echo -e "\n\n" 22 | 23 | # En este caso de prueba se puede apreciar mejor el comportamiento de las variables en cuestion una vez realizado el spawn de la subshell 24 | imp() { 25 | echo "$1 BASH_SUBSHELL=$BASH_SUBSHELL BASHPID=$BASHPID PID=$$ PPID=$PPID" >&2 #Se redirige al stderr para visualizar en pantalla 26 | } 27 | 28 | imp "Shell ----" 29 | imp "Subshell 1" | ( imp "Subshell 2" | ( imp "Subshell 3" | imp "Subshell 4" ) ) 30 | -------------------------------------------------------------------------------- /12-subshells-scope-variables.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Este ejemplo muestra el comportamiento del alcance de las variables con subshells de por medio. 4 | 5 | i=0 6 | function f1() 7 | { 8 | let i++ 9 | echo "In f1, SUBSHELL: $BASH_SUBSHELL, i=$i" >&2 10 | } 11 | 12 | f1 13 | f1 | f1 | f1 14 | 15 | echo "at the end, i=$i" 16 | -------------------------------------------------------------------------------- /13-subshells-2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Este ejemplo muestra claramente el momento de resolucion de las variables en cuestion, teniendo en cuenta los momentos fork y execv. 4 | # La variable BASH_SUBSHELL es incrementada una vez finalizado el spawn. 5 | 6 | fn() { 7 | echo -e "Recibe BASHPID resuelto por la shell antes de finalizar el spawn de la subshell_$1 $2 (fork) \n" >&2 8 | echo -e "Resuelve BASHPID en la subshell_1 $BASHPID \n" >&2 9 | } 10 | 11 | fn2() { 12 | 13 | echo -e "Recibe BASH_SUBSHELL resuelto por la shell antes de finalizar el spawn de la subshell_$1 $2 (fork) \n" >&2 14 | echo -e "Resuelve BASH_SUBSHELL en la subshell_$1 $BASH_SUBSHELL \n" >&2 15 | } 16 | 17 | 18 | echo -e "BASHPID desde la shell $BASHPID y BASH_SUBSHELL desde la shell $BASH_SUBSHELL \n" 19 | fn "1" "$BASHPID" | fn "2" "$BASHPID" 20 | 21 | fn2 "1" "$BASH_SUBSHELL" | fn2 "2" "$BASH_SUBSHELL" 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Explicación de Práctica 3: Shell scripting 3 | ## Introducción a los Sistemas Operativos 4 | 5 | Scripts de ejemplo para la explicación de práctica de Shell scripting 6 | -------------------------------------------------------------------------------- /ejercicios/iso/tp3-14.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function ayuda { 4 | echo "Este script renombra los archivos del directorio pasado como primer" 5 | echo "parámetro agregando un string (recibido como tercer parámetro) al" 6 | echo "principio o al final del nombre actual del archivo, en función del" 7 | echo "segundo parámetro recibido, con las siguientes posibles opciones para" 8 | echo "éste último parámetro:" 9 | echo 10 | echo " -a agrega el string al final del nombre del archivo" 11 | echo " -b agrega el string al principio del nombre del archivo" 12 | echo 13 | echo "Por ejemplo:" 14 | echo 15 | echo " $0 `pwd` -a _sufijo" 16 | echo " $0 `pwd` -b prefijo_" 17 | } 18 | 19 | if [ $# -ne 3 ]; then 20 | ayuda 21 | exit 1 22 | fi 23 | 24 | if [ ! -d $1 ]; then 25 | echo "El directorio pasado como parámetro ($1) no existe." 26 | exit 2 27 | fi 28 | 29 | if [ "$2" != "-a" ] && [ "$2" != "-b" ]; then 30 | echo "El flag pasado como segundo parámetro ($2) no es válido." 31 | echo "Debería ser -a o -b" 32 | exit 3 33 | fi 34 | 35 | pushd $1 36 | for archivo in $(ls); do 37 | if [ ! -f $archivo ]; then 38 | continue 39 | fi 40 | 41 | if [ $2 = "-a" ]; then 42 | mv $archivo $archivo$3 43 | else 44 | mv $archivo $3$archivo 45 | fi 46 | done 47 | popd -------------------------------------------------------------------------------- /ejercicios/iso/tp3-18.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ $# -ne 1 ]; then 4 | echo Debe pasar el nombre del usuario que desea chequear como parámetro 5 | exit 1 6 | fi 7 | 8 | usuario_valido=`cat /etc/passwd | cut -d: -f1 | grep -w $1 | wc -l` 9 | if [ $usuario_valido -eq 0 ]; then 10 | echo "usuario invalido" 11 | exit 82 12 | fi 13 | 14 | while true; do 15 | entro=`who | cut -d' ' -f 1 | grep $1 | wc -l` 16 | if [ $entro -gt 0 ]; then 17 | echo "El usuario $1 ha ingresado" 18 | exit 0 # o break 19 | fi 20 | sleep 10 21 | done 22 | -------------------------------------------------------------------------------- /tips/01-validacion-parametros.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Para validar la cantidad de parámetros de un script o una función, podemos 4 | # utilizar el comando `test` y algunos de sus test flags: 5 | 6 | if [ $# -ne 3 ]; then 7 | echo El script no recibió 3 parámetros 8 | # Lo más común en este punto sería retornar un exit code > 0 (error) 9 | # exit 1 10 | fi 11 | 12 | if [ $# -lt 2 ]; then 13 | echo El script recibió menos de 2 parámetros 14 | # exit 2 15 | fi 16 | 17 | if [ $# -gt 0 ] && [ -z $1 ]; then 18 | echo El script recibió parámetros, pero el primero está en blanco 19 | # exit 3 20 | fi 21 | -------------------------------------------------------------------------------- /tips/02-validacion-archivos.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # El comando test permite realizar chequeos sobre los archivos del filesystem 4 | # (para el usuario que ejecuta el script). 5 | # 6 | # Entre otros, los flags más comunes son los que permiten chequear los tipos de 7 | # los archivos y los permisos que se tienen sobre éstos: 8 | 9 | if [ $# -eq 0 ]; then 10 | echo Este script espera recibir un parámetro, que será utilizado como path. 11 | echo Por ejemplo, podemos ejecutar: 12 | echo " $0 ." 13 | echo para realizar los chequeos sobre el directorio actual 14 | exit 1 15 | fi 16 | 17 | path=$1 18 | 19 | # Sencilla función que ejecuta el comando test pasándole como test flag el 20 | # primer parámetro recibido (-e, -d, -f, etc) y el valor de la variable global 21 | # $path, e imprime un mensaje utilizando el segundo parámetro como etiqueta. 22 | # 23 | # Por ejemplo: 24 | # chequear -f "Es un archivo" 25 | # # => Imprime "✓ Es un archivo" si $path es un archivo, o " Es un archivo" 26 | # si no lo es. 27 | function chequear { 28 | if [ $1 $path ]; then 29 | echo -n " ✓" 30 | else 31 | echo -n " " 32 | fi 33 | echo " $2" 34 | } 35 | 36 | echo "Analizando $1..." 37 | 38 | chequear -e Existe 39 | chequear -d Directorio 40 | chequear -f Archivo 41 | chequear -L Link 42 | chequear -r Lectura 43 | chequear -w Escritura 44 | chequear -x Ejecución --------------------------------------------------------------------------------