GNU/Linux >> Tutoriales Linux >  >> Linux

Trucos estúpidos de Bash:historial, reutilización de argumentos, archivos y directorios, funciones y más

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. ]


Linux
  1. Encuentra archivos y directorios en Linux como un profesional

  2. Más trucos estúpidos de Bash:variables, búsqueda, descriptores de archivos y operaciones remotas

  3. Encuentra los archivos y directorios más grandes en Linux

  4. Cómo crear y llamar funciones en Bash

  5. bash:elimina todos los directorios (y contenidos) pero no los archivos en pwd

Cómo comprimir archivos y directorios en Linux

Cómo sincronizar archivos y directorios usando Zaloha.sh

Comprimir y archivar archivos y directorios

Encuentre archivos y directorios en Linux fácilmente

Archivos y directorios predeterminados en cPanel

Cómo borrar el historial de Bash en Linux y Mac