Los fundamentos de Bash Scripting

Los scripts de shell son ampliamente utilizados en el mundo UNIX. Son excelentes para acelerar tareas repetitivas y simplificar la lógica de ejecución compleja. Pueden ser tan simples como un conjunto de comandos, o pueden organizar tareas complejas. En este tutorial, aprenderemos más sobre el lenguaje de script Bash escribiendo un script de ejemplo paso a paso.


El problema de Fizz-Buzz

Una de las mejores maneras de aprender sobre un nuevo idioma es con un ejemplo. Vamos a empezar con uno.

El problema de Fizz-Buzz es muy simple. Se hizo famoso después de que un programador, llamado Imran, lo utilizara como prueba de entrevista. Resulta que el 90-99.5% de los candidatos para un trabajo de programación simplemente no pueden escribir el programa más simple. Imran tomó este sencillo juego de Fizz-Buzz y pidió a los candidatos que lo resolvieran. Muchos siguieron el ejemplo de Imran y, hoy, es una de las preguntas más frecuentes para un trabajo de programación. Si está contratando y necesita una forma de filtrar a través del 90% de los candidatos, este es un gran problema para presentar.

Estas son las reglas:

  • Toma e imprime los números entre 1 y 100..
  • Cuando un número es divisible por 3, imprima "Fizz" en lugar del número.
  • Cuando es divisible por 5, imprime "Buzz" en su lugar.
  • Cuando sea divisible tanto por 3 como por 5, imprima "FizzBuzz".

Eso es todo al respecto. Estoy seguro de que la mayoría de ustedes ya puede visualizar los dos o tres Si Declaraciones para resolver esto. Vamos a trabajar a través de esto usando el lenguaje de scripts Bash.


El asunto

Un shebang se refiere a la combinación de los caracteres hash y de exclamación: #!. El cargador de programas buscará un shebang en la primera línea del script y usará el intérprete especificado en él. Un shebang consiste en la siguiente sintaxis: #!intérprete [parámetros]. El intérprete es el programa que se utiliza para interpretar nuestro idioma. Para bash scripting, eso sería / bin / bash. Por ejemplo, si desea crear un script en PHP y ejecutarlo en la consola, probablemente quiera usar / usr / bin / php (o la ruta al ejecutable de PHP en su máquina) como intérprete.

#! / usr / bin / php  

Sí, eso realmente funcionará! ¿No es simple? Solo asegúrese de hacer su archivo ejecutable primero. Una vez que lo haga, esta secuencia de comandos generará su información PHP como cabría esperar..

Propina: Para asegurarse de que su script funcione en tantos sistemas como sea posible, puede usar / bin / env en el shebang. Como tal, en lugar de / bin / bash, podrías usar / bin / env bash, que funcionará en sistemas donde el ejecutable de bash no está dentro /compartimiento.


Salida de texto

La salida de una secuencia de comandos será igual a, como es de esperar, lo que se obtenga de su comando. Sin embargo, si queremos explícitamente escribir algo en la pantalla, podemos usar eco.

#! / bin / bash echo "Hello World"

Al ejecutar este script se imprimirá "Hello World" en la consola..

csaba @ csaba ~ $ ./helloWorld.sh Hola mundo csaba @ csaba ~ $

Introduciendo Variables

Al igual que con cualquier lenguaje de programación, al escribir scripts de shell, puede utilizar variables.

#! / bin / bash message = "Hello World" echo $ message

Este código produce exactamente el mismo mensaje "Hello World". Como puede ver, para asignar un valor a una variable, simplemente escriba su nombre - excluya el signo de dólar que está delante de ella. Además, ten cuidado con los espacios; no puede haber espacios entre el nombre de la variable y el signo igual. Asi que mensaje = "Hola" en lugar de mensaje = 'Hola'

Cuando desee utilizar una variable, puede tomar el valor de ella tal como lo hicimos en el eco mando. Preponer un PS al nombre de la variable devolverá su valor.

Propina: No se requieren puntos y coma en las secuencias de comandos de bash. Puede usarlos en la mayoría de los casos, pero tenga cuidado: pueden tener un significado diferente al que usted espera..


Imprimiendo los números entre 1 y 100

Continuando con nuestro proyecto de demostración, necesitamos recorrer todos los números entre 1 y 100. Para esto, necesitamos usar un para lazo.

#! / bin / bash para el número en 1… 100; hacer eco $ numero hecho

Hay varias cosas nuevas que vale la pena destacar en este ejemplo, que por cierto imprime todos los números del 1 al 100, un número a la vez..

  • los para La sintaxis en Bash es: para VARIABLE en RANGO; hacer el comando hecho.
  • Las llaves se transformarán. 1… 100 en un rango en nuestro ejemplo. También se utilizan en otros contextos, que revisaremos en breve..
  • hacer y para En realidad son dos comandos separados. Si desea colocar dos comandos en una sola línea, deberá separarlos de alguna manera. Una forma es usar el punto y coma. Alternativamente, puede escribir el código sin un punto y coma moviendo hacer a la siguiente linea.
#! / bin / bash para el número en 1… 100 do echo $ número finalizado

La primera decision

Ahora que sabemos cómo imprimir todos los números entre 1 y 100, es hora de tomar nuestra primera decisión.

#! / bin / bash para el número en 1… 100; hacer si [$ ((número% 3)) -eq 0]; luego echo "Fizz" más echo $ número fi hecho

Este ejemplo mostrará "Fizz" para los números divisibles entre 3. Una vez más, tenemos que lidiar con un poco de nueva sintaxis. Vamos a tomarlos uno por uno.

  • si ... entonces ... otra cosa ... fi - Esta es la sintaxis clásica para una Si Declaración en Bash. Por supuesto, el más La parte es opcional, pero es necesaria para nuestra lógica en este caso..
  • si COMMAND-RETURN-VALUE; entonces… - Si se ejecutará si el valor de retorno del comando es cero. Sí, la lógica en Bash se basa en cero, lo que significa que los comandos que se ejecutan con éxito salen con un código de 0. Si algo sale mal, por otro lado, se devolverá un entero positivo. Para simplificar las cosas: cualquier otra cosa que no sea 0 es considerada falso.
  • Las expresiones matemáticas en Bash se especifican con paréntesis dobles. $ ((número% 3)) devolverá el valor restante de dividir la variable, número, por 3. Tenga en cuenta que no usamos PS Dentro del paréntesis, sólo delante de ellos..

Quizás se esté preguntando dónde está el comando en nuestro ejemplo. ¿No hay solo un corchete con una expresión extraña en él? Bueno, resulta que El Es en realidad un comando ejecutable. Para jugar con esto, pruebe los siguientes comandos en su consola.

csaba @ csaba ~ $ el cual [/ usr / bin / [csaba @ csaba ~ $ [0 -eq 1] csaba @ csaba ~ $ echo $? 1 csaba @ csaba ~ $ [0 -eq 0] csaba @ csaba ~ $ echo $? 0

Propina: El valor de salida de un comando siempre se devuelve a la variable, ? (signo de interrogación). Se sobrescribe después de la ejecución de cada nuevo comando..


Buscando Buzz

Lo estamos haciendo bien hasta ahora. Tenemos "Fizz"; Ahora vamos a hacer la parte "Buzz".

#! / bin / bash para el número en 1… 100; hacer si [$ ((número% 3)) -eq 0]; luego haga eco en elif "Fizz" [$ ((número% 5)) -eq 0]; luego echo "Buzz" más echo $ número fi hecho

Anteriormente, hemos introducido otra condición para la divisibilidad por 5: la elif declaración. Esto, por supuesto, se traduce en si no, y se ejecutará si el comando siguiente vuelve cierto (o 0). Como se puede observar, las declaraciones condicionales dentro de [] Usualmente son evaluados con la ayuda de parámetros, tales como -eq, que significa "iguales".

Por la sintaxis, arg1 OP arg2, OP es uno de -eq, -Nebraska, -es, -le, -gt, o -ge. Estos operadores binarios aritméticos vuelven. cierto Si arg1 es igual a, no igual a, menor que, menor o igual que, mayor que, o mayor o igual que arg2, respectivamente.arg1 y arg2 pueden ser enteros positivos o negativos. - Manual de Bash

Cuando intente comparar cadenas, puede utilizar el conocido == Signo, o incluso un solo signo igual hará. != devoluciones cierto cuando las cuerdas son diferentes.


Pero el código no es muy correcto

Hasta ahora, el código se ejecuta, pero la lógica no es correcta. Cuando el número es divisible entre 3 y 5, nuestra lógica solo hará eco en "Fizz". Modifiquemos nuestro Código para satisfacer el último requisito de FizzBuzz..

#! / bin / bash para el número en 1… 100; hacer si [$ ((número% 3)) -eq 0]; luego output = "Fizz" fi si [$ ((número% 5)) -eq 0]; luego output = "$ output Buzz" fi si [-z $ output]; luego echo $ número o echo $ salida; fi hecho

Una vez más, hemos tenido que hacer un puñado de cambios. La más notable es la introducción de una variable, y luego la concatenación de "Buzz" a ella, si es necesario. Las cadenas en bash normalmente se definen entre comillas dobles ("). Las comillas simples también se pueden usar, pero para una concatenación más fácil, las dobles son la mejor opción. Dentro de estas comillas dobles, puede hacer referencia a las variables: algún texto $ variable otro texto"reemplazará $ variable con sus contenidos. Cuando desee concatenar variables con cadenas sin espacios entre ellas, puede preferir poner el nombre de la variable entre llaves. En la mayoría de los casos, como PHP, no está obligado a hacerlo, pero ayuda mucho cuando se trata de la legibilidad del código..

Propina: No puedes comparar cuerdas vacías. Eso devolvería un parámetro faltante.

Porque los argumentos dentro [] son tratados como parámetros, por "[", deben ser diferentes de una cadena vacía. Entonces esta expresión, aunque lógica, producirá un error: [$ output! = ""]. Por eso hemos usado [-z $ salida], que devuelve cierto si la cadena tiene una longitud de cero.


Método de extracción para la expresión lógica

Una forma de mejorar nuestro ejemplo es extraer en funciones la expresión matemática del Si declaraciones, como así:

La función #! / bin / bash es DivisibleBy return $ (($ 1% $ 2)) para el número en 1 ... 100; do si es divisible por $ número 3; luego output = "Fizz" fi si es DivisibleBy $ número 5; luego output = "$ output Buzz" fi si [-z $ output]; luego echo $ número o echo $ salida; fi hecho

Tomamos las expresiones comparando el resto con cero y las movimos a una función. Aún más, eliminamos la comparación con cero, porque cero significa verdadero para nosotros. Solo tenemos que devolver el valor de la expresión matemática - muy simple!

Propina: La definición de una función debe preceder a su llamada..

En Bash, puedes definir un método como función func_name comandos; . Opcionalmente, hay una segunda sintaxis para declarar funciones: func_name () comandos; . Entonces, podemos soltar la cuerda., función y añadir "()" después de su nombre. Personalmente prefiero esta opción, como se ejemplifica en el ejemplo anterior. Es más explícito y se asemeja a los lenguajes de programación tradicionales..

No es necesario especificar los parámetros para una función en Bash. El envío de parámetros a una función se realiza simplemente enumerando sobre ellos después de la llamada a la función separada por espacios en blanco. No coloque comas ni paréntesis en la llamada de función, no funcionará.

Los parámetros recibidos se asignan automáticamente a las variables por número. El primer parámetro va a $ 1, el segundo para $ 2, y así. La variable especial, $ 0 refiere el nombre del archivo del script actual.

Juguemos con parámetros

#! / bin / bash function exampleFunc echo $ 1 echo $ 0 IFS = "X" echo "$ @" echo "$ *" exampleFunc "one" "two" "three"

Este código producirá la siguiente salida:

csaba @ csaba ~ $ ./parametersExamples.sh one ./parametersExamples.sh one two thre oneXtwoXthre

Analicemos la fuente, línea por línea..

  • La última línea es la llamada a la función. Lo llamamos con tres parámetros de cadena..
  • La primera línea después del shebang es la definición de la función.
  • La primera línea en la función genera el primer parámetro: "uno". Hasta ahora tan simple.
  • La segunda línea muestra el nombre del archivo del script actual. De nuevo, muy simple.
  • La tercera línea cambia el separador de caracteres predeterminado a la letra "X". Por defecto, esto es "" (un espacio). Así es como Bash sabe cómo se separan los parámetros..
  • La cuarta línea produce una variable especial., PS. Representa todos los parámetros como una sola palabra, exactamente como se especifica en la llamada a la función.
  • La línea final produce otra variable especial., PS. Representa todos los parámetros, tomados uno por uno y concatenados con la primera letra de la variable IFS. Por eso el resultado es. oneXtwoXthre.

Devolviendo cadenas desde funciones

Como señalé anteriormente, las funciones en Bash solo pueden devolver enteros. Como tal, escribiendo devuelve "una cadena" sería un código inválido Aún así, en muchas situaciones, necesita más que solo un cero o uno. Podemos refactorizar nuestro ejemplo de FizzBuzz para que, en el para declaración, sólo haremos una llamada de función.

La función #! / bin / bash isDivisibleBy return $ (($ 1% $ 2)) fizzOrBuzz if isDivisibleBy $ 1 3; luego output = "Fizz" fi si es DivisibleBy $ 1 5; luego output = "$ output Buzz" fi si [-z $ output]; luego echo $ 1 en caso contrario echo $ salida; fi para el número en 1 ... 100; hacer fizzOrBuzz $ número hecho

Bueno, este es el primer paso. Acabamos de extraer todo el código en una función, llamada FizzOrBuzz, y luego reemplazado $ numero con $ 1. Sin embargo, todos los resultados se producen en el FizzOrBuzz función. Queremos salir de la para bucle con un eco instrucción, de modo que podamos anteponer cada línea con otra cadena. Tenemos que capturar el FizzOrBuzz salida de la función.

# […] Para el número en 1… 100; do echo "-'fizzOrBuzz $ number '" fizzBuzzer = $ (fizzOrBuzz $ number) echo "- $ fizzBuzzer" hecho

Hemos actualizado nuestro para bucle sólo un poco (no hay otros cambios). Ahora hemos repetido todo dos veces de dos maneras diferentes para ejemplificar las diferencias entre las dos soluciones al mismo problema.

La primera solución para capturar la salida de una función u otro comando es usar backticks. En el 99% de los casos, esto funcionará bien. Simplemente puede hacer referencia a una variable dentro de backticks por sus nombres, como hicimos con $ numero. Las primeras líneas de la salida ahora deberían verse como:

csaba @ csaba ~ / Personal / Programming / NetTuts / Lo básico de BASH Scripting / Sources $ ./fizzBuzz.sh -1 -1 -2 -2 -Fizz -Fizz -4 -4 -Buzz -Buzz -Fizz -Fizz -7 -7

Como puedes ver, todo está duplicado. Misma salida.

Para la segunda solución, hemos elegido asignar primero el valor de retorno a una variable. En esa tarea, usamos PS, el cual, en este caso, divide el script, ejecuta el código y devuelve su salida.


;, && y ||

¿Recuerdas que usamos punto y coma aquí y allá? Se pueden utilizar para ejecutar varios comandos escritos en la misma línea. Si los separas por punto y coma, simplemente se ejecutarán.

Un caso más sofisticado es usar && entre dos órdenes. Sí, eso es un AND lógico; significa que el segundo comando se ejecutará solo si el primero devuelve cierto (sale con 0). Esto es útil; podemos simplificar el Si declaraciones en estos taquigrafes:

#! / bin / bash function isDivisibleBy return $ (($ 1% $ 2)) function fizzOrBuzz isDivisibleBy $ 1 3 && output = "Fizz" isDivisibleBy $ 1 5 && output = "$ output Buzz" si [-z $ output ]; luego echo $ 1 en caso contrario echo $ salida; fi para el número en 1 ... 100; hacer eco "-'fizzOrBuzz $ number '" hecho

Como nuestra funcion, isDivisibleBy devuelve un valor de retorno adecuado, entonces podemos usar && para establecer la variable que queremos. Que hay despues && será ejecutado sólo si la condición es cierto. De la misma manera, podemos utilizar || (carácter de doble canalización) como un OR lógico. Aquí hay un ejemplo rápido a continuación.

csaba @ csaba ~ $ echo "bubu" || echo "bibi" bubu csaba @ csaba ~ $ echo false || echo "bibi" false csaba @ csaba ~ $

Pensamientos finales

Así que eso lo hace para este tutorial! Espero que hayas aprendido algunos consejos y técnicas nuevas para escribir tus propios scripts de Bash. Gracias por leer y estad atentos para artículos más avanzados sobre este tema..