Como administrador del sistema, los shells son parte de las operaciones diarias. Los shells a menudo brindan más opciones y flexibilidad que una interfaz gráfica de usuario (GUI). Las tareas repetitivas diarias se pueden automatizar fácilmente mediante secuencias de comandos, o las tareas se pueden programar para que se ejecuten en determinados momentos del día. Un shell proporciona una manera conveniente de interactuar con el sistema y le permite hacer más en menos tiempo. Hay muchos shells diferentes, incluidos Bash, zsh, tcsh y PowerShell.
En esta publicación de blog de dos partes, comparto algunas de las frases ingeniosas de Bash que uso para acelerar mi trabajo y dejar más tiempo para tomar café. En esta publicación inicial, cubriré la historia, los últimos argumentos, el trabajo con archivos y directorios, la lectura del contenido de los archivos y las funciones de Bash. En la segunda parte, examinaré las variables de shell, el comando de búsqueda, los descriptores de archivos y la ejecución de operaciones de forma remota.
Usar el comando de historial
La history
El comando es útil. History
me permite ver qué comandos ejecuté en un sistema en particular o qué argumentos se pasaron a ese comando. Yo uso history
para volver a ejecutar comandos sin tener que recordar nada.
El registro de comandos recientes se almacena de forma predeterminada en ~/.bash_history.
Esta ubicación se puede cambiar modificando la variable de shell HISTFILE. Hay otras variables, como HISTSIZE (líneas para almacenar en la memoria para la sesión actual) y HISTFILESIZE (cuántas líneas mantener en el archivo de historial). Si quieres saber más sobre la history
, ver man bash
.
Digamos que ejecuto el siguiente comando:
$> sudo systemctl status sshd
Bash me dice que el servicio sshd no se está ejecutando, por lo que lo siguiente que quiero hacer es iniciar el servicio. Había comprobado su estado con mi comando anterior. Ese comando se guardó en history
, para que pueda hacer referencia a él. Simplemente ejecuto:
$> !!:s/status/start/
sudo systemctl start sshd
La expresión anterior tiene el siguiente contenido:
- !! - repetir el último comando del historial
- :s/status/start/ - sustituye status con inicio
El resultado es que se inicia el servicio sshd.
A continuación, aumento el valor predeterminado de HISTSIZE de 500 a 5000 con el siguiente comando:
$> echo “HISTSIZE=5000” >> ~/.bashrc && source ~/.bashrc
¿Qué pasa si quiero mostrar los últimos tres comandos en mi historial? ingreso:
$> history 3
1002 ls
1003 tail audit.log
1004 history 3
Ejecuto tail
en audit.log
haciendo referencia al número de línea de historial. En este caso, uso la línea 1003:
$> !1003
tail audit.log
..
..
Imagina que has copiado algo de otro terminal o de tu navegador y accidentalmente pegas la copia (que tienes en el búfer de copia) en el terminal. Esas líneas se almacenarán en el historial, que aquí es algo que no desea. Así que ahí es donde desestablecer HISTFILE &&exit es útil
$> unset HISTFILE && exit
o
$> kill -9 $$
Hacer referencia al último argumento del comando anterior
Cuando quiero enumerar los contenidos de los directorios para diferentes directorios, puedo cambiar entre directorios con bastante frecuencia. Hay un buen truco que puedes usar para referirte al último argumento del comando anterior. Por ejemplo:
$> pwd
/home/username/
$> ls some/very/long/path/to/some/directory
foo-file bar-file baz-file
En el ejemplo anterior, /some/very/long/path/to/some/directory
es el último argumento del comando anterior.
Si quiero cd
(cambiar directorio) a esa ubicación, ingreso algo como esto:
$> cd $_
$> pwd
/home/username/some/very/long/path/to/some/directory
Ahora simplemente use un guión para volver a donde estaba:
$> cd -
$> pwd
/home/username/
Trabajar en archivos y directorios
Imagine que quiero crear una estructura de directorios y mover un montón de archivos con diferentes extensiones a estos directorios.
Primero, creo los directorios de una sola vez:
$> mkdir -v dir_{rpm,txt,zip,pdf}
mkdir: created directory 'dir_rpm'
mkdir: created directory 'dir_txt'
mkdir: created directory 'dir_zip'
mkdir: created directory 'dir_pdf'
A continuación, muevo los archivos según la extensión del archivo a cada directorio:
$> mv -- *.rpm dir_rpm/
$> mv -- *.pdf dir_pdf/
$> mv -- *.txt dir_txt/
$> mv -- *.zip dir_txt/
Los caracteres de doble guión --
significa fin de opciones. Esta marca evita que los archivos que comienzan con un guión se traten como argumentos.
A continuación, quiero reemplazar/mover todos los archivos *.txt a archivos *.log, así que ingreso:
$> for f in ./*.txt; do mv -v ”$file” ”${file%.*}.log”; done
renamed './file10.txt' -> './file10.log'
renamed './file1.txt' -> './file1.log'
renamed './file2.txt' -> './file2.log'
renamed './file3.txt' -> './file3.log'
renamed './file4.txt' -> './file4.log'
En lugar de usar for
bucle anterior, puedo instalar el prename
comando y lograr el objetivo anterior de esta manera:
$> prename -v 's/.txt/.log/' *.txt
file10.txt -> file10.log
file1.txt -> file1.log
file2.txt -> file2.log
file3.txt -> file3.log
file4.txt -> file4.log
A menudo, cuando modifico un archivo de configuración, hago una copia de seguridad del original usando un comando de copia básico. Por ejemplo:
$> cp /etc/sysconfig/network-scripts/ifcfg-eth0 /etc/sysconfig/network-scripts/ifcfg-eth0.back
Como puede ver, repetir toda la ruta y agregar .back al archivo no es tan eficiente y probablemente sea propenso a errores. Hay una forma más corta y ordenada de hacer esto. Aquí viene:
$> cp /etc/sysconfig/network-scripts/ifcfg-eth0{,.back}
Puede realizar diferentes comprobaciones en archivos o variables. Ejecutar help test
para más información.
Utilice el siguiente comando para descubrir si un archivo es un enlace simbólico:
$> [[ -L /path/to/file ]] && echo “File is a symlink”
Aquí hay un problema que encontré recientemente. Quería comprimir/descomprimir un montón de archivos de una sola vez. Sin pensar, escribí:
$> tar zxvf *.gz
El resultado fue:
tar: openvpn.tar.gz: Not found in archive
tar: Exiting with failure status due to previous errors
Los archivos tar fueron:
iptables.tar.gz
openvpn.tar.gz
…..
¿Por qué no funcionó y por qué ls -l *.gz
trabajar en su lugar? Debajo del capó, se ve así:
$> tar zxvf *.gz
Se transforma de la siguiente manera:
$> tar zxvf iptables.tar.gz openvpn.tar.gz
tar: openvpn.tar.gz: Not found in archive
tar: Exiting with failure status due to previous errors
El tar
Se esperaba que el comando encontrara openvpn.tar.gz dentro de iptables.tar.gz. Resolví esto con un simple for
bucle:
$> for f in ./*.gz; do tar zxvf "$f"; done
iptables.log
openvpn.log
¡Incluso puedo generar contraseñas aleatorias usando Bash! He aquí un ejemplo:
$> alphanum=( {a..z} {A..Z} {0..9} ); for((i=0;i<=${#alphanum[@]};i++)); do printf '%s' "${alphanum[@]:$((RANDOM%255)):1}"; done; echo
Aquí hay un ejemplo que usa OpenSSL:
$> openssl rand -base64 12
JdDcLJEAkbcZfDYQ
Leer un archivo línea por línea
Supongamos que tengo un archivo con muchas direcciones IP y quiero operar con esas direcciones IP. Por ejemplo, quiero ejecutar dig
para recuperar información de DNS inverso para las direcciones IP enumeradas en el archivo. También quiero omitir las direcciones IP que comienzan con un comentario (# o hashtag).
Usaré el archivo A como ejemplo. Sus contenidos son:
10.10.12.13 some ip in dc1
10.10.12.14 another ip in dc2
#10.10.12.15 not used IP
10.10.12.16 another IP
Podría copiar y pegar cada dirección IP y luego ejecutar dig
manualmente:
$> dig +short -x 10.10.12.13
O podría hacer esto:
$> while read -r ip _; do [[ $ip == \#* ]] && continue; dig +short -x "$ip"; done < ipfile
¿Qué pasa si quiero intercambiar las columnas en el archivo A? Por ejemplo, quiero poner las direcciones IP en la columna más a la derecha para que el archivo A se vea así:
some ip in dc1 10.10.12.13
another ip in dc2 10.10.12.14
not used IP #10.10.12.15
another IP 10.10.12.16
Ejecuto:
$> while read -r ip rest; do printf '%s %s\n' "$rest" "$ip"; done < fileA
Usar las funciones de Bash
Las funciones en Bash son diferentes de las escritas en Python, C, awk u otros lenguajes. En Bash, una función simple que acepta un argumento e imprime "Hola mundo" se vería así:
func() { local arg=”$1”; echo “$arg” ; }
Puedo llamar a la función así:
$> func foo
A veces, una función se invoca a sí misma recursivamente para realizar una determinada tarea. Por ejemplo:
func() { local arg="$@"; echo "$arg"; f "$arg"; }; f foo bar
Esta recursión se ejecutará para siempre y utilizará una gran cantidad de recursos. En Bash, puede usar FUNCNEST para limitar la recursividad. En el siguiente ejemplo, configuro FUNCNEST=5 para limitar la recursividad a cinco.
func() { local arg="$@"; echo "$arg"; FUNCNEST=5; f "$arg"; }; f foo bar
foo bar
foo bar
foo bar
foo bar
foo bar
bash: f: maximum function nesting level exceeded (5)
Utilice una función para recuperar el archivo más reciente o más antiguo
Aquí hay una función de muestra para mostrar el archivo más reciente en un directorio determinado:
latest_file()
{
local f latest
for f in "${1:-.}"/*
do
[[ $f -nt $latest ]] && latest="$f"
done
printf '%s\n' "$latest"
}
Esta función muestra el archivo más antiguo en un directorio determinado:
oldest_file()
{
local f oldest
for file in "${1:-.}"/*
do
[[ -z $oldest || $f -ot $oldest ]] && oldest="$f"
done
printf '%s\n' "$oldest"
}
Estos son solo algunos ejemplos de cómo usar funciones en Bash sin invocar otros comandos externos.
A veces me encuentro escribiendo un comando una y otra vez con muchos parámetros. Un comando que uso a menudo es kubectl
(CLI de Kubernetes). ¡Estoy cansado de ejecutar este largo comando! Aquí está el comando original:
$> kubectl -n my_namespace get pods
o
$> kubectl -n my_namespace get rc,services
Esta sintaxis requiere que incluya manualmente -n my_namespace
cada vez que ejecuto el comando. Hay una manera más fácil de hacer esto usando una función:
$> kubectl () { command kubectl -n my_namespace ”$@” ; }
Ahora puedo ejecutar kubectl
sin tener que escribir -n namespace
cada vez:
$> kubectl get pods
Puedo aplicar la misma técnica a otros comandos.
Resumir
Estos son solo algunos trucos excelentes que existen para Bash. En la segunda parte, mostraré algunos ejemplos más, incluido el uso de búsqueda y ejecución remota. Te animo a que practiques estos trucos para que tus tareas de administración de línea de comandos sean más fáciles y precisas.
[ Curso gratuito en línea:Descripción general técnica de Red Hat Enterprise Linux. ]