README.md: Archivo nuevo

Consejos útiles para el uso del shell
This commit is contained in:
Tuxliban Torvalds 2023-02-13 14:32:14 -06:00
commit 608e819f07
1 changed files with 274 additions and 0 deletions

274
README.md Normal file
View File

@ -0,0 +1,274 @@
# **Consejos y trucos de shell**
## Autor
### Telegram
* @tenshalito Tuxliban Torvalds
### Mail
* o-sanchez@linuxmail.org
-----
## **ÍNDICE**
-----
## Suspender y reanudar un comando
Si están utilizando una consola virtual (tty) y tienen un programa ejecutándose en primer plano, no es necesario finalizar esa tarea o por ejemplo abrir otra tty para poder realizar otra tarea, sólo se necesita teclear `^Z` (control-z) para suspender temporalmente la tarea actual y estará esperando a que sea reanudado nuevamente. En este momento obtendrá un prompt de shell y podrá emitir cualquier comando.
Cuando se esté listo para continuar, puede reanudar la tarea anterior `fg %1` (*"primer plano: el primer comando en espera"*).
Si por el contrario, se quiere ejecutar el comando en segundo plano, se puede emitir también un "bg %1" ("*segundo plano: primer comando en espera*").
Por último, con el comando "jobs" es posible listar todos los comandos que se encuentran en espera.
## Uso de "heredoc" en lugar de múltiples "echo"
Los comandos "echo" múltiples pueden ser sustituidos por "heredoc", esto hace que el script sea más rápido y fácil de leer.
Ejemplo:
```
echo "Por favor, introduzca su elección:"
echo "1 - listar el directorio actual"
echo "2 - listar los usuarios actuales"
echo "3 - cerrar la sesión"
```
Lo anterior puede ser sustituido por:
```
cat << EOF
Por favor, introduzca su elección
1 - listar el directorio actual
2 - listar los usuarios actuales
3 - cerrar la sesión
EOF
```
> NOTA: "heredoc" no necesariamente debe de ser EOF, puede ser cualquier caracter que sirva como delimitador. Por ejemplo: !, ECHO, end, END_OF_TEXT
Otra opción es que si se desea que las líneas no se vean afectadas por la sintaxis del shell y no se quiere generar un proceso adicional al invocar al comando `cat`, se puede optar por hacerlo así:
```
# Opción 1: usar `echo` para el estandar POSIX o `print` para ksh
echo "Por favor, introduzca su elección:
1 - listar el directorio actual
2 - listar los usuarios actuales
3 - cerrar la sesión"
# Opción 2: usar printf (más eficiente)
printf %s "\
Por favor, introduzca su elección:
1 - listar el directorio actual
2 - listar los usuarios actuales
3 - cerrar la sesión
```
En el ejemplo de `printf`, el **\** en la primera línea evita una nueva línea extra al principio del bloque de texto.
## Uso de built-in en lugar de comandos externos
Muchas veces, es común utilizar comandos externos como `basename`, `dirname` y `tr` por comodidad o porque no se dan cuenta de que pueden utilizar los módulos integrados de ksh.
Una ventaja añadida es que los built-in son más rápidos y requieren menos recursos del sistema porque no se genera ningún subproceso.
### Reemplazo de basename:
```
fullfile="/foo_dir1/foo_dir2/archivo.txt"
# Reemplazo de file=$(basename $fullfile)
file=${fullfile##*/}
echo $file archivo.txt
```
## Reemplazo de dirname:
```
fullfile="/foo_dir1/foo_dir2/archivo.txt"
# Reemplazo de dir=$(dirname $fullfile)
dir=${fullfile%/*}
echo $dir
/foo_dir1/foo_dir2
```
## Reemplazo tr:
```
foo="EjEmPlO"
#Reemplazo de: echo $foo | tr [A-Z] [a-z]
typeset -l foo
echo $foo
ejemplo
#Reemplazo de: echo $foo | tr [a-z] [A-Z]
typeset -u foo
echo $foo
EJEMPLO
```
## Forma correcta para listar nombre de archivos en un bucle
No se recomienda el uso del comando `ls` en un bucle para listar el nombre de archivos debido a que el output de `ls` puede ser confuso y difícil de manipular en scripts de shell. El output de `ls` es una secuencia de caracteres que puede incluir espacios y otros caracteres especiales que no son fácilmente procesables por scripts de shell.
Además, el comportamiento de `ls` puede variar entre sistemas y es posible que no se comporte de la misma manera en diferentes sistemas operativos. Por estas razones, es mejor evitar el uso de `ls` en scripts de shell y optar por soluciones más fiables y portables.
El siguiente ejemplo es uno de los errores comunes al escribir un bucle en un script de shell:
```
for i in $(ls *.xyz); do
<comando> $i
done
```
En el ejemplo anterior, si el nombre de archivo contiene espacios en blanco al analizarlo tendrá una división de palabras. Suponiendo que hay un archivo llamado *Tesis - Version 2.odt* en el directorio actual, el ciclo `for` repetirá cada palabra en el nombre del archivo resultante: *Tesis, -, Version, 2.odt*
Tampoco se puede simplemente citar la sustitución ya que eso hará que toda la salida de `ls` se trate como una sola palabra. En lugar de iterar sobre cada nombre de los archivos, el bucle solo se ejecutará una vez asignando a `i` una cadena con todos los nombres de archivo juntos.
Por lo tanto, algunas las soluciones a este tipo de problemas son las siguiente:
### Usar un `glob` simple (en caso de no necesitar recursividad):
```
for i in ./*.mp3; do
<comando> "$i"
done
```
> Nota: Observen que es necesario utilizar la comilla doble para evitar la división de palabras
Asimismo, si en el directorio que se analiza no existen coincidencias, para evitar que el bucle no lea esos archivos basta con añadir lo siguiente:
```
# Estándar POSIX
for i in ./*.mp3; do
[ -e "$i" ] || continue
<comando> "$i"
done
# ksh y shells similares
for i in ./*.mp3; do
[[ -e $i ]] || continue
<comando> "$i"
done
```
### Usar `find` (si se necesita recursividad)
```
# Estándar POSIX
find . -type f -name '*.xyz' -exec <comando> {} \;
```
En el caso de ksh (y shells similares) se puede usar la opción `-print0` de find de GNU o BSD, junto con la opción `read -d ''` para lograr la recursividad:
```
while IFS= read -r -d '' file; do
<comando> "$file"
done < <(find . -type f -name '*.xyz' -print0)
```
La ventaja aquí es que *<comando>* (técnicamente todo el cuerpo del bucle while) se ejecuta en el shell actual. Por lo tanto, es posible establecer variables y hacer que persistan una vez finalizado el bucle.
## Evaluación de expresiones
Cuando la variable contiene espacios en blanco internos, entonces se dividirá en palabras separadas antes de que el comando `[` (test) pueda leerlo. Por ejemplo:
```
foo="texto de ejemplo"
[ $foo = "texto de ejemplo" ] && echo $foo
```
El código de ejemplo anterior tiene un error de sintaxis en lo que respecta a `[` , así que para corregir el código basta con encerrar en comillas dobles la variable:
```
# Estándar POSIX
foo="texto de ejemplo"
[ "$foo" = "texto de ejemplo" ] && echo "$foo"
```
> Nota: observen que **siempre** es necesario añadir un espacio después de `[` y un espacio antes de `]`, de lo contrario tendrán un error de sintaxis
En el caso de **ksh** y shell similares, es posible utilizar la versión mejorar de `[`
```
foo="texto de ejemplo"
[[ $foo == "texto de ejemplo" ]] && print "$foo"
```
Como se puede observar, no es necesario citar la variable que se ubica del lado izquierdo de la evaluación (==) dentro de `[[ ]]` porque con esta versión mejorara de `[` no se dividen ni agregan palabras, incluso las variables en blanco se manejarán correctamente. Como se puedo observar, para hacer que la cadena de la derecha se interprete literalmente, hay que citarla si se utilizan caracteres que tienen un significado especial como por ejemplo **!, #, $**, etc..
## Evaluación múltiple de expresiones
Otro error que se comete cuando comenzamos a crear nuestros propios scripts es que puede suceder que queramos utilizar `&&` dentro `[`. Al realizar esto nuestro shell nos indicará que hay un error de sintaxis y por lo tanto, nuestro script no podrá ejecutarse correctamente.
Código incorrecto:
```
[ -e "$foo" && "$foo" = "ejemplo" ] && <comandos>
```
Código correcto:
```
# Estándar POSIX
[ -e "$foo" ] && [ "$foo" = "ejemplo" ] && <comandos>
# ksh y shells similares
[[ -e "$foo" && "$foo" = "ejemplo" ]] && <comandos>
```
## Expresiones aritmétricas
Si se desea comparar números por valor evite usar `< >` ya que estos son utilizados para comparaciones de cadenas o en el peor de los casos, como redirección de la salida estándar. Tampoco utilice `=> =<`
Código incorrecto
```
foo=5
if [ "$foo" > 3 ]; then
echo "$foo es mayor que 3"
fi
```
Para corregir el error arriba mencionado, utilice los operadores de comparación numérica `-eq -gt -ge -lt -le` que corresponden a *igual que, mayor que, mayor o igual que, menor que* y *menor o igual que*
Código correcto
```
# Estándar POSIX
foo=5
if [ "$foo" -ge 3 ]; then
echo "$foo es mayor que 3"
fi
```
### ksh y shells similares
Si solo desea hacer una comparación numérica (o cualquier otra aritmética de shell), es mucho mejor usar `(( ))` en lugar de `[[ ]]`
Código correcto
```
foo=5
if ((foo > 3)); then
print "$foo es mayor que 3"
fi
# Este código también es válido, aunque no es muy común
if [[ $foo -gt 3 ]]; then
print "$foo es mayor que 3"
fi
```
> Nota: Si se usa el operador `>` dentro de `[[ ]]`, la expresión será tratada como una comparación de *strings* y no como una comparación de enteros. Si usa > dentro de `[ ]` se interpretará como una redirección de salida obteniendo así un archivo llamado 3 en su directorio, y la prueba tendrá éxito siempre que `$foo` no esté vacío.