Cinco consejos a la hora de hacer scripts de Bash en Linux.

Llevamos unos días donde he hablado mucho de PowerShell y parece que nos estemos olvidando un poco del mundo Linuxero, así que hoy vamos a ver una serie de consejillos para hacer scripts con Bash en nuestro sistema Linux de forma más efectiva:

Usa comillas dobles a la hora de referenciar variables:

Trabajar con cadenas de texto con espacios puede ser problemático. A la hora de referenciar una variable para usar su valor es recomendable hacerlo poniéndola entre comillas dobles: evitará que la cadena se corte por el uso de espacios en blanco y también mejorará el rendimiento, evitando hacer rastreos de comodines innecesariamente. Si ejecutas este script lo verás:

empresas="Tecnológicas Camarasa SL"
echo "Sin comillas:"
echo
for nombre in $empresas; do
        echo "$nombre"
done
echo
echo "Con comillas:"
echo
for nombre in "$empresas"; do
        echo "$nombre"
done
exit 0

Si lo ejecutas verás como en el primer caso va a imprimir tres nombres, pues se confundirá con los espacios, mientras que en el ejemplo con comillas lo sacará en una sola línea respentado los espacios.

Forzar la salida del script en caso de error:

A veces un script puede seguir ejecutándose aunque un comando falle, y eso puede derivar en problemas más adelante. Con la siguiente línea podemos forzar la salida del script en caso de que un comando no funcione:

set -o errexit 

Forzar la salida del script en caso de usar una variable no declarada:

Como en el caso anterior, esto puede derivar en errores más graves más adelante, así que es recomendable que el script se detenga si se usa una variable que está sin declarar:

set -o nounset

Declara las constantes como readonly:

Las constantes en esencia son un variable con un valor estático. A la hora de declarar una constante en nuestro script, tanto por seguridad como por rendimiento, lo mejor es hacerlo con el atributo readonly, puesto que no va a vaciarse ni sobrescribirse el valor.

readonly fichero_host="/etc/hosts"
readonly pi=3.14159265359

Usa la sintaxis $(comando) en lugar del antiguo `comando` :

Si queremos asignar la salida de un comando a una variable antiguamente lo haríamos invocando al comando entre dos acentos. Hoy por hoy la sintaxis sería utilizando el comando entre paréntesis precedido por el carácter $, tal que así:

#Sintaxis antigua
usuario=`echo “$UID”`
#Sintaxis modernizada recomendada
usuario=$(echo “$UID”)

Utilizar el comando expr para hacer operaciones

El comando expr del terminal de Linux nos permite evaluar una expresión y pintar su resultado. De esta forma podemos usarlo para ejecutar operaciones aritméticas o de comparación.

$ expr 15 + 3
$ expr 15 % 3
$ expr 15 \* 3
$ expr 15 – 3
$ expr 15 / 3

En el ejemplo de arriba puedes ver los ejemplos para suma, operación de módulo, multiplicación (ese caso requiere usar el carácter \ para escapar el asterisco que usamos como símbolo de multiplicación), resta y división.

¿Limitaciones? Pues que solo nos permite operar con números enteros: únicamente acepta enteros como parámetros y tan solo devuelve enteros como resultado

Otra cosa que nos permite expr es ejecutar operaciones de comparación:

$ expr 15 = 3
$ expr 15 != 5
$ expr 15 \> 3
$ expr 15 \< 3
$ expr 15 \<= 3

Devolverá 1 en caso de que la comparación sea verdadera y 0 en el caso contrario. De nuevo el carácter \ será necesario para escapar los caracteres de mayor y menor en las comparaciones.

No solo podemos usarlo desde el terminal para operar, expr también puede resultar de utilidad invocado dentro de alguno de nuestros scripts.

Diferentes formas de contar el número de líneas de un fichero desde la consola de Linux.

Si queremos contar el número de líneas que tiene un fichero desde la consola de Linux tenemos, al menos, cuatro métodos para conseguirlo. Os pongo los ejemplos de código:

Usando los comandos cat y wc:

cat fichero | wc -l

Con grep le podemos decir que cuente todo el documento de esta forma:

grep -c '.*' file

La opción -n de sed junto al operador = también nos hace el servicio:

sed -n '$=' file

Y para terminar vamos a ver cómo hacerlo con awk:

awk 'END {print NR}' file

Distintas formas de invertir una cadena de texto en Linux.

Vamos a hablar un poco más sobre programar scripts en Linux y veamos cómo podemos invertir una cadena con distintos métodos:

Empezamos con lo más sencillo, usar la función rev:

echo holiii | rev

Usando el comando sed dos veces, junto con tac, también podemos hacerlo:

echo holiii | sed 's/./&\n/g' | tac | sed -e :a -e 'N;s/\n//g;ta'

Ya os podéis imaginar que si podemos hacerlo con sed es que también podemos hacerlo con awk, en este caso con la función substr() y un bucle for con el que recorreremos la cadena de fin a principio pintando cada letra:

echo holiii | awk '{ for(i=length;i!=0;i--)x=x substr($0,i,1);}END{print x}'

Y no podría faltar el tradicional script de shell. El principio es el mismo que con awk: recorrer la cadena del final hacia el inicio pintando cada elemento

#!/bin/bash
a="holiii"
len=`echo ${#a}`
while [ $len -ne 0 ]
do
        b=$b`echo $a | cut -c $len`
        ((len--))
done
echo $b

Cómo hacer un Hello World! en distintos lenguajes de programación.

El Hello World es un ejercicio básico de programación. Casi en cualquier lenguaje de programación que estudies empezarás en la primera lección programando uno. Aquí te dejo diversos ejemplos en distintos lenguajes
, lo que además te permitirá ver algunas de las pequeñas diferencias que hay entre ellos.

ASP:

Response.Write "Hello World!" 

Script de Bash:

#!/bin/bash
echo "Hello, World!" 

C:

#include 
main(){
  printf ("Hello World!\n");
}

C++:

#include 
using namespace std;
void main(){
  cout << "Hello World!" << endl;
}

C#:

using System;
namespace HelloWorld
{
    class Hello 
    {
        static void Main() 
        {
            Console.WriteLine("Hello World!");            
        }
    }
}

Java:

class hellWorldJava{
  public static void main(String args[]){
    System.out.println("Hello World!");
  }
}

Javascript:

window.alert( 'Hello, world!' );

Objective C:

#import 

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
    }
    return 0;
}

Perl:

#!/usr/bin/perl
print “Hello World.\n”;

PHP:

echo "Hello World!";

Script de Powershell:

$strString = "Hello World"
write-host $strString

Python:

print "Hello, World!"

R:

print("Hello World!", quote = FALSE)

Ruby:

puts 'Hello world'

Linux: script para crear usuarios automáticamente con contraseñas aleatorias desde una lista dada en CSV.

Vamos con un script para automatizar tareas. Tenemos un CSV, le llamaremos user.csv, con los siguientes datos: Nombre, Primer Apellido y Usuario. Algo tal que así

Manuel,Garcia,ManuG1
Pedro,Rodriguez,PredroR2
Maria,Abalo,MariaA45
Josefa,Perez,JosefP21
Marta,Rios,MartaR91

La idea del script es la siguiente: recorre el fichero csv y crea a los usuarios con ese nombre y apellido y ese código de usuario. Asigna una contraseña generada de forma aleatoria y redirige la salida a un fichero para poder notificar a los usuarios creados su usuario y contraseña.

#!/bin/bash

OLDIFS=$IFS
IFS=","

while read firstname lastname userid 
do 
     PS=$(openssl rand -base64 12)   
     useradd -c "${firstname} ${lastname}" -p $PS -d /home/"${userid}" -s /bin/bash "${userid}"            
     echo "Usuario: ${userid} - Contraseña: $PS ....." >> resultado.txt 
done < user.csv

La opción de crear los usuarios con contraseña no es recomendable en todos los ámbitos por temas de seguridad, lo he hecho en este caso porque el ejemplo real en el que lo hice lo había requerido así el cliente, pero según la política y el contexto de seguridad no es algo recomendable en todos los casos porque podrían ser vistas por algún usuario no autorizado, así que aunque la generes así es mejor obligar a los usuarios a cambiarla. Para crearlo sin contraseña en la instrucción useradd quita el parámetro -p $PS, la línea de encima que genera el password (que ya sería inútil) y el $PS en la salida.

Redirección de datos en Bash

La redirección de datos es un elemento básico de Bash ya que nos permite controlar hacia dónde se volcarán los datos de salida de un comando o script, o de dónde obtenemos los datos de entrada.

Tenemos tres descriptores:

  • stdin: Entrada estándar.
  • stdout: Salida estándar. También se abrevia con el número 1.
  • stderr: Error estándar. También se abrevia con el número 2.

Esto nos dará varias opciones. La primera, redirigir la salida estándar a un fichero, para lo que usamos el operador >:

ls -l > fichero.txt

En el ejemplo de arriba ejecutamos el comando ls -l pero le decimos que nos guarde el resultado en un fichero. Ahora vamos a redirigir el error estándar a un fichero, usando el operador 2>:

grep pri * 2> error.txt

En este caso ejecutamos el comando grep pri * y lo que sacamos al fichero son los errores. Luego podemos combinar la salida y sacar la salida el estándar por el error estándar o viceversa:

#Sacamos el error estándar por la salida estándar
grep pri * 2>&1
#Sacamos la salida estándar por el error estándar
grep pri * 1>&2

Finalmente vamos a ver cómo sacar la salida y el error estándar juntos a un fichero:

grep pri * &> todo.txt

Esto es muy útil si queremos ejecutar un comando o un script en silencio, por ejemplo uno que se ejecute en una tarea programada con cron, ya que no veremos nada en pantalla pero tendremos nuestro propio log con los datos de lo ejecutado.

Scripts para parar, arrancar y reiniciar Apache y MySQL en Ubuntu

Muchos desarrolladores tenemos en nuestro equipo un entorno LAMPP para testear nuestros trabajos web. Como generalmente uso el ordeador para trabajar tengo configurado que arranquen con el equipo por defecto tanto MySQL como Apache, ya que rara vez estoy ON en mi equipo y no estoy trasteando con algo de código.

Pero a veces, para virtualizar o para trabajar con algún editor de vídeo me veo obligado a tener toda la memoria posible para que la cosa no se torne inusable
. Cierto que son sólo dos líneas en el terminal… pero con cada script las dejo en una sola:

Script para parar Apache 2 y MySQL:

#!/bin/bash/

sudo apache2ctl -k stop
sudo /etc/init.d/mysql stop

Script para arrancar Apache2 y MySQL

#!/bin/bash/

sudo apache2ctl -k start
sudo /etc/init.d/mysql start

Y finalmente, script para reiniciarlos

#!/bin/bash/

sudo apache2ctl -k restart
sudo /etc/init.d/mysql restart

Te dará una serie de warnings, pero ni caso, funciona (compruébalo intentando conectar con MySQL o intentando ver algunha página en local).

En fin, por hoy nada más.