Una vez que haya adquirido un conocimiento sólido de Linux y haya dominado los diversos comandos disponibles, su próxima gran victoria vendrá en forma de funciones de shell. El código incluido en una función puede ser reutilizado por sus scripts de shell, pero también puede hacerlo disponible en su línea de comando, tal como usaría cualquier programa, comando integrado o alias.
Las funciones breves que llevan a cabo tareas comunes pueden ahorrarle mucho tiempo y es satisfactorio ensamblarlas. Estas son algunas de las funciones de shell que he encontrado más útiles.
1
mkd: crea un directorio e ingresalo
Estoy empezando con un ejemplo de una función de shell ideal: es útil y fácil de entender, pero debería ayudar a ilustrar algunos conceptos clave. En su forma más básica, una función de shell envuelve uno o dos comandos que a menudo es posible que desee ejecutar juntos, lo que brinda comodidad.
¿Cuándo fue la última vez que ejecutó el comando mkdir para crear un directorio y no ejecutó cd inmediatamente para ingresar a ese directorio? Supongo que probablemente nunca sea así, así que ¿por qué no complementar mkdir con una versión que haga precisamente esto? Aquí está la función:
mkd() {
mkdir -p -- "$1" && cd -P -- "$1"
}
Puede mantener todas sus funciones de shell personalizadas en un archivo y obtenerlas al inicio. Guardo los míos en un directorio oculto en mi casa, así que ejecuto este comando desde mi archivo ~/.zshrc para cargarlos en mi shell: . ~/.config/shell_funcs.sh.
El && aquí es un truco inteligente de terminal que ejecuta el segundo comando después del primero, pero sólo si el primero tiene éxito. En este caso, no tiene sentido cambiar al directorio si no se creó, por un problema de permisos, por ejemplo.
La opción mkdir -p le permite crear más de un nivel de directorio: mkd docs/cartas/urgentePor ejemplo. Tiene un efecto secundario agradable: si ejecuta esta función y le pasa un directorio que ya existe, no verá ningún error, pero de todos modos terminará cambiando de directorio.
Lo último a tener en cuenta son esos argumentos «–«. Este es un argumento especial que indica el final de las opciones y el comienzo de otros argumentos: nombres de directorio, en este caso. Este es un ejemplo de programación defensiva para evitar problemas si intentas crear un directorio que comience con un carácter “-”. Puede parecer poco probable, pero siempre es mejor prevenir que lamentar.
2
mensaje: configure su mensaje para varias cosas
Una de las primeras cosas que todos aprenden sobre su shell de Linux es cómo personalizar el mensaje. Al comienzo de cada línea de comando, su shell imprimirá texto para informarle que está listo para ingresar:
El mensaje anterior muestra el directorio actual entre paréntesis, seguido de un signo de dólar, pero el mensaje puede ser tan mínimo o informativo como desee. Su valor está controlado por una variable de entorno denominada PS1. Puedes establecer esto en una cadena fija, como “$”, solo recuerda escapar del $, que tiene un significado especial para el shell:
PS1="\$ "
La siguiente función establece el mensaje en algo un poco más dinámico. Esta versión comprueba si estás ejecutando bash o zsh, por lo que puede ser utilizada por cualquiera de los shells. Se diferencian ligeramente en sus capacidades: cada uno intenta mostrar solo las últimas tres partes de su ruta actual, pero bash agrega su propia lógica encima.
prompt() {
NEWLINE=$'\n' if [ ! "${BASH_VERSINFO}" == "" ]; then
PROMPT_DIRTRIM=3
PS1="${NEWLINE}[\w] \$ "
fi
if [ ! "${ZSH_VERSION}" == "" ]; then
PS1="${NEWLINE}[%3~] \$ "
fi
}
Este mensaje también incluye un carácter de nueva línea, que es más fácil de agregar definiendo primero una variable. Esta nueva línea crea una línea vacía entre la salida del comando anterior y el siguiente mensaje, lo que facilita agrupar comandos y su salida de un vistazo.
Aunque bash y zsh manejan el mensaje más o menos de la misma manera, otros shells no son tan consistentes. Algunas conchas más modernas, como las de pescado, adoptan un enfoque funcional para el aviso, ofreciendo mucha más flexibilidad. Si no está utilizando bash o zsh, consulte el manual de su shell para descubrir cómo puede configurar el mensaje.
Pero, ¿por qué tener una función para hacer esto, en lugar de simplemente configurarla una vez en su archivo ~/.bashrc o ~/.zshrc? Bueno, me resulta muy útil tener diferentes mensajes a los que puedo cambiar fácilmente:
Esto es realmente útil para tomar capturas de pantalla de mi terminal cuando quiero ocultar mi ruta actual. Pero podría usarlo en otras situaciones: para diferenciar entre dos sesiones de terminal, por ejemplo, o para guardar el espacio de la pantalla en ventanas más pequeñas.
3
recortar: eliminar los espacios en blanco iniciales y finales
Esta función elimina los espacios en blanco del principio y del final de cada línea:
trim() {
sed 's/^[ \t]*//;s/[ \t]*$//'
}
La línea de comandos de Linux consiste en pasar datos de un comando a otro, a través de canalizaciones, archivos, etc. Pero eso requiere datos limpios y predecibles, y los comandos no siempre son buenos para producirlos. Tomemos como ejemplo el baño:
Esta salida usa alineación para producir una lista agradable y legible, pero es incómodo usarla en una canalización ya que otros comandos tendrán que tener en cuenta ese espacio en blanco inicial. En cambio, canalizar la salida de la tubería para recortar eliminará los espacios en blanco adicionales, lo que hará que el procesamiento posterior sea más sencillo:
Esta función de recorte utiliza sed, la utilidad de edición de secuencias. Especifica dos reemplazos, separados por un punto y coma. El primero busca una serie de caracteres de espacio/tabulador al principio de cada línea y lo reemplaza por nada, es decir, lo elimina. El segundo reemplazo hace lo mismo, pero al final de la línea.
Tenga en cuenta que sed opera con una entrada estándar si no se le pasa ningún nombre de archivo, y llamarlo a través de una función como esta hace lo mismo, pasando la entrada estándar al comando.
4
rgrep: una abreviatura de grep recursivo
A veces, una función no es más simple que un solo comando, preparado con opciones específicas. Si bien puede existir un comando rgrep en su sistema y hacer prácticamente lo mismo que esta función, no está presente en macOS, por lo que uso esta alternativa:
rgrep() {
grep -Id recurse "$@"
}
La opción -I evita que grep busque en archivos binarios, lo que normalmente sólo produce errores. El indicador -d especifica cómo grep debe tratar los directorios; en este caso, «recurse» hace que se lean de forma recursiva.
Tenga en cuenta el uso de «$@» para pasar argumentos adicionales a grep. Esto es importante porque significa que puede ejecutar «rgrep» exactamente como ejecutaría «grep», solo que tendrá su comportamiento personalizado. Por ejemplo, rgrep -yo todo se expandirá a grep -Id recursivo -yo todo.
Puede confirmar cómo funciona esto agregando «set -o xtrace» al comienzo de su función para imprimir los comandos a medida que se ejecutan. Esta es una de las mejores formas de pulir sus scripts de shell porque le ayuda a depurar exactamente lo que sucede bajo el capó.
5
ucase/lcase: convertir entre casos
La mayoría de los lenguajes de programación tienen una función de mayúsculas/minúsculas; Incluso aplicaciones como LibreOffice tienen la función incorporada porque suele ser muy útil. Puedes tener fácilmente el equivalente en tu línea de comando de Linux, donde es aún más poderoso, ya que puede operar en cada línea de un archivo que envías:
ucase() {
tr '[:lower:]' '[:upper:]'
}lcase() {
tr '[:upper:]' '[:lower:]'
}
Este par de funciones complementarias utiliza el comando tr, que realiza transformaciones simples en un flujo de texto.
tr toma dos argumentos: una lista de caracteres para buscar y una lista de caracteres para reemplazarlos. Cada carácter de reemplazo actúa sobre el correspondiente en la cadena de búsqueda, por lo que «tr ‘AB’ ‘ab'» convertirá «A» en «a» y «B» en «b». Las clases de caracteres especiales”[:upper:]» y «[:lower:]”expandir al conjunto completo de caracteres en mayúsculas y minúsculas en la configuración regional actual.
6
hoy: obtiene la fecha actual en formato ISO corto
today() {
date '+%Y-%m-%d'
}
Olvídese de la diferencia entre las fechas de EE. UU. y el resto del mundo por el momento: nadie usa este formato de fecha, pero es sin duda mi favorito. La razón es simple: las fechas en este formato se ordenarán naturalmente, y eso es una gran ventaja en mi opinión. Utilizo el formato en muchos lugares: nombres de archivos, encabezados de documentos, bases de datos, etc.
Este puede ser otro atajo de comando simple, pero es muy conveniente. Es molesto recordar el formato de fecha exacto: el prefijo “+”, los símbolos “%” y la corrección entre mayúsculas y minúsculas de cada parte. Prefiero olvidarme de todo eso y simplemente escribir hoy en cambio: ¡muy satisfactorio!
7
tamaño de archivo: muestra los archivos en el directorio actual, ordenados por tamaño
Una tarea de mantenimiento común que realizo implica limpiar archivos grandes. Si bien ncdu es una excelente manera de localizar archivos grandes, a veces es excesivo. Lo que a menudo quiero es ver qué archivos de mi directorio actual son los más grandes.
filesize() {
du -sk * | sort -n
}
du es la herramienta de uso del disco; informa cuánto espacio ocupa cada archivo, incluidos los directorios, sobre los que informa de forma recursiva. Por lo tanto, esta función le indicará el tamaño total de los directorios presentes, lo que facilitará la exploración en profundidad de su sistema de archivos y la eliminación de archivos no deseados.
8
rutas: imprime la variable PATH, muy bien
paths() {
echo $PATH | tr ':' '\n'
}
Otra tarea de mantenimiento que me gusta realizar de vez en cuando es limpiar mi PATH. macOS tiene la desagradable costumbre de agregar directorios adicionales a la variable de entorno, ¡incluidos algunos que en realidad no existen! Pero debido a que esta variable usa dos puntos para separar cada ruta, puede ser bastante complicado de leer:
Esta función hace que el resultado sea mucho más aceptable:
Es otro gran uso para ese brillante programa tr, y podrías extenderlo aún más: verificando que cada directorio en tu RUTA exista, por ejemplo:
check_paths() {
paths | while read path
do
if [ ! -d "$path" ]; then
echo "bad PATH: dir does not exist: $path" >&2
fi
done
}
