Programación del Shell LinuxAquí tenemos un libro libre y completo sobre ShellLa sed del "conocimiento libre" es muy bienvenida. |
Comandos Shell Script |
![]() |
|
Home |
Artículos ![]() ![]() |
Comprar el libroChangelogs |
|
Acompañamientos para el aperitivoEn construcción para siempre! Esta página, a pesar de estar dentro del contenido de Conversaciones de Bar, nunca fue publicada en la Linux Magazine. Trata de artículos que escribí para otras publicaciones, ayudas útiles que leí navegando por la internet (y en este caso con los debidos créditos), contribuciones de estas personas maravillosas y siempre dispuestas a ayudar del Software Libre, y de la imprescindible "Lista de Shell Script" Pasando parámetros con xargs
Existe un comando, cuya función principal es construir listas de parámetros y pasarlas para la ejecución de otros programas o instrucciones. Este comando es el xargs [comando [argumento inicial]]
En el caso de que el comando (que puede ser inclusive un script Shell), sea omitido, será usado por default el
El Ejemplo:
Vamos a buscar una cadena de caracteres en todos los archivos dentro de un determinado directorio, usaremos el comando
$ cat grepr
#
# Grep recursivo
# Busca la cadena de caracteres definida en $2 a partir del directorio $1
#
find $1 -type f -print|xargs grep -l "$2"
Ejecutando este script buscamos, a partir del directorio definido en la variable Exactamente lo mismo podría hacerse si la línea del programa fuera la siguiente: find $1 -type f -exec grep -l "$2" {} \; El primer proceso tiene dos grandes desventajas sobre el anterior:
![]() ls colorido como estándar: en los ejemplos siguientes que incluyen esta instrucción, deben usar la opción --color=none , en caso contrario, existen grandes posibilidades de que los resultados no sean los esperados (;-).
Vamos ahora a analizar un ejemplo que es más o menos lo contrario de este que acabamos de ver. Esta vez, vamos a hacer un script para borrar todos los archivos del directorio actual y que pertenezcan a un determinado usuario.
La primera idea que surge es, como en el caso anterior, usar un comando find . -user cara -exec rm -f {} \;
Casi estaría correcto, el problema es que de esta forma se estarían borrando no solamente los archivos de ls -l | grep " cara " | cut -c55- | xargs rm
De esta forma, el
El xargs es también una excelente herramienta de creación de one-liners (scripts de solamente una línea). Mira éste para listar todos los propietarios de archivos (inclusive sus links) "colgados" en el directorio $ find /bin -type f -follow | \
xargs ls -al | tr -s ' ' | cut -f3 -d' ' | sort -u
Muchas veces el Opciones del xargsLas opciones delxargs pueden se usadas para construir comandos extremamente poderosos.
Opción -i
Para ejemplificar esto y comenzar a entender las principales opciones de esta instrucción, vamos a suponer que tenemos que borrar todos los archivos con extensión $ find . -type f -name "*.txt" | \
xargs -i bash -c "echo borrando {}; rm {}"
La opción Opción -n
Veamos este pequeño juego que vamos a hacer con el $ ls | xargs echo > arch.ls
$ cat arch.ls
arch.ls arch1 arch2 arch3
$ cat arch.ls | xargs -n1
arch.ls
arch1
arch2
arch3
Cuando mandamos la salida del $ cat arch.ls | xargs -n 2
arch.ls arch1
arch2 arch3
Sin embargo, la línea de arriba podría (y debería) ser escrita sin usar el pipe $ xargs -n 2 < arch.ls
Opción -p
Otra excelente opción del $ ls dir
arch1.bug
arch1.ok
arch2.bug
arch2.ok
...
arch9.bug
arch9.ok
Para comparar los archivos buenos con los defectuosos, hacemos: $ ls | xargs -p -n2 diff -c
diff -c arch1.bug arch1.ok ?...y
....
diff -c arch9.bug arch9.ok ?...y
Opción -t
Para finalizar, el ResumenEntonces podemos resumir el comando de acuerdo con la siguiente tabla:
Here Strings
Primero un programador con complejo de inferioridad creó el redireccionamiento de entrada y lo representó con un signo de menor Bromas a parte, el here strings es utilísimo y, no sé porque, es un perfecto desconocido. En la poquísima literatura que hay sobre el tema, se nota que el here strings es frecuentemente citado como una variante del here document, teoría con la que discrepo pues su aplicabilidad es totalmente diferente de aquella. Su sintaxis es simple: $ comando <<< $cadena
Donde Como siempre, vamos directo a los ejemplos de los dos usos más comunes para que vosotros mismos saquéis las conclusiones.
Ejemplos: $ a="1 2 3"
$ cut -f 2 -d ' ' <<< $a # Normalmente se hace: echo $a | cut -f 2 -d ' '
2
$ echo $Nomearch
Mis Documentos # Arrrghhh!
$ tr "A-Z " "a-z_" <<< $Nomearch # Substituyendo el echo $Nomearch | tr "A-Z " "a-z_"
mis_documentos
$ bc <<<"3 * 2"
6
$ bc <<<"scale = 4; 22 / 7"
3.1428
Para mostrar la mejoría en el desempeño, vamos a hacer un loop de 500 veces usando el ejemplo dado para el comando $ time for ((i=1; i<= 500; i++)); { tr "A-Z " "a-z_" <<< $Nomearch >/dev/null; }
real 0m3.508s
user 0m2.400s
sys 0m1.012s
$ time for ((i=1; i<= 500; i++)); { echo $Nomearch | tr "A-Z " "a-z_" >/dev/null; }
real 0m4.144s
user 0m2.684s
sys 0m1.392s
Veamos ahora esta otra secuencia de comandos con medidas de tiempo: $ time for ((i=1;i<=100;i++)); { who | cat > /dev/null; }
real 0m1.435s
user 0m1.000s
sys 0m0.380s
$ time for ((i=1;i<=100;i++)); { cat <(who) > /dev/null; }
real 0m1.552s
user 0m1.052s
sys 0m0.448s
$ time for ((i=1;i<=100;i++)); { cat <<< $(who) > /dev/null; }
real 0m1.514s
user 0m1.056s
sys 0m0.412s
Observando este cuadro verás que en el primero, usamos la forma convencional, en el segundo usamos un named pipe temporal para ejecutar una substitución de procesos y en el tercero usamos here strings. Notaras también que al contrario del ejemplo anterior, aqui el uso de here strings no fue lo más veloz. Pero observad también que en este último caso el comando Veamos una forma rápida de insertar una línea como encabezamiento de un archivo: $ cat num
1 2
3 4
5 6
7 8
9 10
$ cat - num <<< "Impares Pares"
Impares Pares
1 2
3 4
5 6
7 8
9 10
Ejemplos: $ echo "$línea"
Leonardo Mello (21)3313-1329
$ cat -vet <<< "$línea"
Leonardo Mello^I(21)3313-1329$ # Los separadores son blanco y <TAB> (^I)
$ read Nom SNom Tel <<< "$línea"
$ echo "${Nom}_$S{Nom}_$Tel" # Vamos a ver si leyó cada uno de los campos
Leonardo_Mello_(21)3313-1329 # Leyó porque los separadores eran iguales al IFS
También podemos leer directamente de un vector (array) vedlo: $ echo $Frutas
Pera:Uva:Manzana
$ IFS=:
$ echo $Frutas
Pera Uva Manzana # Sin las comillas el shell muestra el IFS como blanco
$ echo "$Frutas"
Pera:Uva:Manzana # Ahhh, ahora sí!
$ read -a aFrutas <<< "$Frutas" # La opción -a del read, lee un vector
$ for i in 0 1 2
> do
> echo ${aFrutas[$i]} # Imprimiendo cada elemento del vetor
> done
Pera
Uva
Manzana
Rotatório de TiagoEstaba, como lo hago todos los días dandole un repaso a los e-mails de la "Lista de Shell Script" , cuando descubro algo totalmente inusitado de Tiago Barcellos Peczenyj.
Cuando resolví crear este conjunto de consejos, me acordé de eso y le pedí para que me enviara aquel e-mail nuevamente. El texto siguiente es el e-mail que me mandó, solo inserté el último ejemplo y saqué las abrebiaturas. Julio descubrí una forma en la que el Shell crea combinaciones haciendo rotación con los elementos estipulados. Podemos generar todos los binarios de 0000 a 1111 de la siguiente forma: $ A={0,1}
$ eval echo $A$A$A$A
0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111
Una aplicación práctica que veo es para combinar valores diferentes sin tener que encadenar loops ni usar el =seq $ A={`seq -s , -f "_%g" 3`}
$ eval echo -e $A$A$A |tr ' _' '\n ' | grep -vE '.+?(\b[0-9]+\b).+?\1'
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
En este caso combiné los números de 1 a 3 eliminando repeticiones con el 1 1 2 1 2 1 1 1 1no son impresos.
Ahora va mi ejemplo: el one-liner siguiente genererá todos los permisos posibles (en octal) para el archivo $ A={`seq -s , 0 7`}
$ eval echo -e $A$A$A | tr ' ' '\n' | xargs -i bash -c "chmod {} arq; ls -l arq"
---------- 1 julio julio 100 2006-11-27 11:50 arq
---------x 1 julio julio 100 2006-11-27 11:50 arq
--------w- 1 julio julio 100 2006-11-27 11:50 arq
--------wx 1 julio julio 100 2006-11-27 11:50 arq
-------r-- 1 julio julio 100 2006-11-27 11:50 arq
-------r-x 1 julio julio 100 2006-11-27 11:50 arq
-------rw- 1 julio julio 100 2006-11-27 11:50 arq
-------rwx 1 julio julio 100 2006-11-27 11:50 arq
. . . . . . . . . . . . . . . . . .
-rwxrwxrw- 1 julio julio 100 2006-11-27 11:50 arq
-rwxrwxrwx 1 julio julio 100 2006-11-27 11:50 arq
Veamos este ejemplo paso a paso para entenderlo: $ echo $A
{0,1,2,3,4,5,6,7}
$ eval echo -e $A$A$A
000 001 002 003 004 005 006 007 010 ... ... 767 770 771 772 773 774 775 776 777
$ eval echo -e $A$A$A | tr ' ' '\n' # El tr cambiará cada espacio en blanco por um <ENTER>
000
001
002
003
. . .
774
775
776
777
A continuación el
Aritmética en Shell
Antiguamente usábamos el comando Ejemplo: $ expr 7 \* 5 / 3 # 7 veces 5 = 35 dividido por 3 = 11
14
En este articulo podremos ver otras formas no tan conocidas, sin embargo más simple de usar, más elaboradas y con mayor precisión. El uso de bcUna forma fantástica de hacer cálculos en Shell – usada normalmente cuando la expresión aritmética es más compleja, o cuando es necesario trabajar con cifras decimales – es usar la instrucción de calculo del UNIX/LINUX. Elbc . Mira como:
Ejemplo: $ echo "(2 + 3) * 5" | bc # Paréntesis usados para dar preferencia
25
Para trabajar con números reales (números no necesariamente enteros), especifique la precisión (cantidad de decimales) con la opción $ echo "scale=2; 7*5/3" | bc
11.66
Otros ejemplos:
$ echo "scale=3; 33.333*3" | bc
99.999
$ num=5
$ echo "scale=2; ((3 + 2) * $num + 4) / 3" | bc
9.66
Obviamente todos los ejemplos de arriba en el caso de linux, podrían (y deberían) ser escritos usando Here Strings. Veamos los últimos como quedarían: $ bc <<< "scale=3; 33.333*3"
99.999
$ num=5
$ bc <<< "scale=2; ((3 + 2) * $num + 4) / 3"
9.66
Una vez apareció en la lista (excelente a propósito) de Shell script en Yahoo ( "Lista de Shell Script" ) un persona con la siguiente duda: "yo tengo un archivo cuyos campos están separados por Yo envié la respuesta siguiente: $ echo $(cut -f3 num | tr '\n' +)0 | bc
20.1
Vamos por partes para entenderlo mejor y primero vamos ver como era el archivo que hice para comprobarlo: $ cat num
a b 3.2
a z 4.5
w e 9.6
q w 2.8
Como se puede ver, está dentro del patrón del problema, donde yo tengo como tercer campo números reales. A ver lo que haría la primera parte de la línea de comandos, donde yo transformo los caracteres $ cut -f3 num | tr '\n' +
3.2+4.5+9.6+2.8+
Si yo mandase de esa manera hacia el bc, él me devolvería un error por causa de aquel signo de más ( $ echo $(cut -f3 num | tr -s '\n' +)0
3.2+4.5+9.6+2.8+0
Eso es lo que se acostumbra llamar one-liner, esto es, códigos que serían complicados en otros lenguajes (normalmente sería necesario crear contadores y hacer uno loop de lectura sumando el tercer campo al contador) y en Shell son escritos en una única línea.
Hay también gente que llama eso de método KISS, que es el acrónimo de Keep It Simple Stupid. Pero el uso potencial de esta calculadora no se acaba ahí, existen diversas facilidades proporcionadas por ella. Veamos sólo este ejemplo: $ echo "obase=16; 11579594" | bc
B0B0CA
$ echo "ibase=16; B0B0CA " | bc # B, zero, B, zero, C, e A
11579594
En estos ejemplos vimos como hacer cambios de base de numeración con el uso del Otras formas de trabajar con enteros
Otra forma mucho mejor de hacer cálculos es usar la notación Ejemplo: Usando el mismo ejemplo que ya habíamos usado: $ echo $(((2+3)*5)) # Los paréntesis mas internos priorizaran o 2+3
25
Fíjate ahora en esta locura: $ tres=3
$ echo $(((2+tres)*5)) # Variable tres no precedida por $
25
$ echo $(((2+$tres)*5)) # Variable tres precedida por $
25
Ei!! No es el signo ( Presta atención a esta secuencia: $ unset i # $i mooorreu!
$ echo $((i++))
0
$ echo $i
1
$ echo $((++i))
2
$ echo $i
2
Fíjate que a pesar que la variable no esta definida, pues fue hecho uno
Fíjate que el También son válidos: $ echo $((i+=3))
5
$ echo $i
5
$ echo $((i*=3))
15
$ echo $i
15
$ echo $((i%=2))
1
$ echo $i
1
Estas tres operaciones serian lo mismo que: i=$((i+3)) i=$((i*3)) i=$((i%2)) Y esto seria válido para todos los operadores aritméticos, lo que en resumen produciría la siguiente tabla:
Pero el máximo exponente de esta forma de construcción con doble paréntesis es la siguiente: $ echo $var
50
$ var=$((var>40 ? var-40 : var+40))
$ echo $var
10
$ var=$((var>40 ? var-40 : var+40))
$ echo $var
50
Este tipo de construcción debe ser usado de la siguiente forma: en caso que la variable
Al igual que usamos la expresión
Los operadores son los mismos para estas tres formas de construcción, lo que varía un poco es la operación aritmética condicional con el uso del
$ echo $var
50
$ let var='var>40 ? var-40 : var+40'
$ echo $var
10
$ let var='var>40 ? var-40 : var+40'
$ echo $var
50
BaseandoSi quieres trabajar con bases diferentes de la decimal, basta con usar el formato: base#numero
Donde
Si $ echo $[2#11]
3
$ echo $((16#a))
10
$ echo $((16#A))
10
$ echo $((2#11 + 16#a))
13
$ echo $[64#a]
10
$ echo $[64#A]
36
$ echo $((64#@))
62
$ echo $((64#_))
63
En estos ejemplos usé las notaciones
Funciona también un cambio automático para la base decimal, si estas usando la convención numérica de C, esto es, en Ejemplo $ echo $((10)) # decimal
10
$ echo $((010)) # octal
8
$ echo $((0x10)) # hexadecimal
16
$ echo $((10+010+0x10)) # Decimal + octal + hexadecimal
64
Ah, se me olvidaba! Las expresiones aritméticas con los formatos Pruebas usando expresiones regularesEn las Conversaciones de Bar IV, lo comentamos todo sobre comandos condicionales, pero faltó uno que no existía a aquella época. En esta misma Conversación de Bar, en la sección Y toma de test llegamos a hablar de una construcción del tipo: [[ Expresión ]] && cmd
Donde el comando Ejemplo: $ echo $BASH_VERSION # Asumiendo que la versión de Bash es igual o superior a la 3.0.0
3.2.17(15)-release
$ Cargo=Senador
$ [[ $Cargo =~ ^(Governa|Sena|Verea)dora?$ ]] && echo É político
É político
$ Cargo=Senadora
$ [[ $Cargo =~ ^(Governa|Sena|Verea)dora?$ ]] && echo É político
É político
$ Cargo=Diretor
$ [[ $Cargo =~ ^(Governa|Sena|Verea)dora?$ ]] && echo É político
$
Vamos desmenuzar la Expresión Regular Coloreando la pantalla
Como usted ya había visto en la Conversación de Bar VII, el comando
Bien, ahora ya sabes como especificar la combinación de colores, pero todavía no sabes los colores. La tabla siguiente muestra los valores que la
En este punto ya puedes empezar a jugar con los colores. - Pero caramba, todavía son mucho pocos!
- Y, tienes toda la razón... El problema es que todavía no te dije que si pones el terminal en modo de énfasis (
Ejemplo Como ejemplo, veamos un script que cambiara el color de la pantalla de acuerdo a tus preferencias. $ cat mudacor.sh
#!/bin/bash
tput sgr0
clear
# Cargando los 8 colores básicos para un vector Colores=(Negro Rojo Verde Marron Azul Púrpura Cian "Gris claro")
# Listando el menu de colores
echo "
Opc Cor
CL= until [[ $CL == 0[1-8] || $CL == [1-8] ]] do read -p " Escoje el colorr de la letra: " CL done # Para los que tienen un bash a partir de la version 3.2 #+ el test do until de arriba podria hacerse #+ usando Expresiones Regulares. Veamos como: #+ until [[ $CL =~ 0?[1-8] ]] #+ do #+ read -p " #+ Escoje el color de la letra: " CL #+ done CF= until [[ $CF == 0[1-8] || $CF == [1-8] ]] do read -p " Escoje el color de Fondo: " CF done let CL-- ; let CF-- # Porque los colores varian de cero a siete tput setaf $CL tput setab $CF clear Ganando la partida con más comodinesEstaba leyendo mis correos electrónicos cuando recibo uno de Tiago enviado a la lista de Shell Script (ya hablé de la lista y de Tiago en el Rotatório Peczenyj). Aquí va el contenido del correo electrónico:
No sé si es conocido de todos pero el shell posee, ademas del globbing normal (la expansión
Creo que, en algunos casos, podria ser MUY util, eliminando uno pipe por un Estos son: ?(patron)Debe coincidir cero o una ocurrencia de un determinado patron
*(patron)Debe coincidir cero o mas ocurrencias de un determinado patron
+(patron)Debe coincidir una o mas ocurrencias de un determinado patron
@(patron)Debe coincidir con exactamente una ocurrencia de un determinado patron
!(patron)Debe coincidir con cualquier cosa, excepto con patron
Para poder utilizár las es necesario ejecutar $ shopt -s extglob
$ ls
file filename filenamename fileutils
$ ls file?(name)
file filename
$ ls file*(name)
file filename filenamename
$ ls file+(name)
filename filenamename
$ ls file@(name)
filename
$ ls file!(name) # divertido esse
file filenamename fileutils
$ ls file+(name|utils)
filename filenamename fileutils
$ ls file@(name|utils) # "lembra" um {name,utils}
filename fileutils
Usando el awk para buscar por equivalenciasAhí va una más que Tiago mandó a la lista de Shell Script de Yahoo (ya hablé de la lista y de Tiago en el Rotatório Peczenyj y en Ganando la partida con mas comodines) Quién no pasó ya por ello: Buscar una palabra, sin embargo una letra acentuada, o no, estorbó la búsqueda?
No descubrí como hacer que el Mejor explicar con un ejemplo, donde voy listar el número de la línea y la ocurrencia encontrada: $ cat dados
éco
eco
èco
êco
ëco
eço
$ awk '/^eco/{print NR,$1}' dados
2 eco
$ awk '/^e[[=c=]]o/{print NR,$1}' dados
2 eco
6 eço
$ awk '/^[[=e=]]co/{print NR,$1}' dados
1 éco
2 eco
3 èco
4 êco
5 ëco
Es decir, usar
La sintaxis es parecida con la de las clases POSIX, cambiando los dos-puntos Lo creí curioso y debe servir para algún caso semejante al descrito. find – Buscando archivo por características
Si estas como el Sr. Magoo, buscando en vano un archivo, usa el mando find [camino ...] expresión [acción] Parametros:
los principales critérios de busqueda definidos por expresión son:
Para usar mas de un critério de busqueda, haz:
Las principales acciones definidas para
Ejemplos:
Para listar en pantalla $ find . -name \*.sh Acción no especificada –print el default
./undelete.sh
./ntod.sh estos cuatro primeros archivos fueron
./dton.sh encontrados en el directório actual.
./graph.sh
./tstsh/cotafs.sh
./tstsh/data.sh Estos cuatro fueron encontrados en el
./tstsh/velha.sh directório tstsh, bajo el directório corrente
./tstsh/charascii.sh
Necesito obtener espacio en un determinado file system con mucha urgencia, entonces voy a borrar los archivos con más de un megabyte y cuyo último acceso fue hay más de 60 días. Para eso, voy a este file system y hago: $ find . –type f –size +1000000c –atime +60 –exec rm {} \;
Observa que en el ejemplo anterior use tres criterios de búsqueda, a saber:
Observa que entre estos tres criterios use el conector e, esto es, archivos regulares e mayores que 1MByte e sin acceso hace más de 60 días.
Para listar todos los archivos del disco terminados por $ find / -name \*.sh –o –name \*.txt –print
En este ejemplo debemos resaltar además de las contrabarras
Con el
También es posible formatear fechas y horas obedeciendo las tablas siguientes:
Los tres caracteres anteriores producen una fecha semejante a al del comando Veamos un ejemplo: $ find . -name ".b*" -printf '%t %p\n'
Mon Nov 29 11:18:51 2004 ./.bash_logout
Tue Nov 1 09:44:16 2005 ./.bash_profile
Tue Nov 1 09:45:28 2005 ./.bashrc
Fri Dec 23 20:32:31 2005 ./.bash_history
En ese ejemplo, el
Esas fechas también pueden ser formateadas, para eso basta pasar las letras de la tabla anterior a mayúsculas (
Para mejorar la situación, veamos unos ejemplos; sin embargo, veamos primero cuáles son los archivos del directorio corriente que empiezan por $ ls -la .b*
-rw------- 1 d276707 ssup 21419 Dec 26 17:35 .bash_history
-rw-r--r-- 1 d276707 ssup 24 Nov 29 2004 .bash_logout
-rw-r--r-- 1 d276707 ssup 194 Nov 1 09:44 .bash_profile
-rw-r--r-- 1 d276707 ssup 142 Nov 1 09:45 .bashrc
Para listar esos archivos en orden de tamaño, podemos hacer: $ find . -name ".b*" -printf '%s\t%p\n' | sort -n
24 ./.bash_logout
142 ./.bashrc
194 ./.bash_profile
21419 ./.bash_history
En el ejemplo que acabamos de ver, el $ find . -name ".b*" -printf '%TY-%Tm-%Td %TH:%TM:%TS %p\n' | sort
2004-11-29 11:18:51 ./.bash_logout
2005-11-01 09:44:16 ./.bash_profile
2005-11-01 09:45:28 ./.bashrc
2005-12-26 17:35:13 ./.bash_history
Y no te olvides, cualquer duda o falta de compañia para tomar una cerveza, lo único que tienes que hacer es mandarme un e-mail a julio.neves@gmail.com. Voy aprovechar tambiém para mandar mi aviso publicitario: puedes decirle a los amigos que quien quiera hacer un curso nota diez de programación en Shell que mande un e-mail para julio.neves@uniriotec.br para informarse. Gracias y hasta la próxima! -- HumbertoPina - 25 Jan 2007 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Licencia Creative Commons - Reconocimiento y no comercial (CC) 2009 Por Visitantes del Bar de Júlio Neves. Todo el contenido de esta página puede ser usada de acuerdo a la Creative Commons License: Atribuição-UsoNãoComercial-PermanênciaDaLicença. |