Conversa de Bar Parte III
- Mozo, traiga dos choppes por favor, que hoy voy a tener que hablar mucho.
Trabajando con cadenas
Por el título encima no pienses que te voy a enseñar a ser carcelero! Me estoy referiendo a cadena de caracteres!
El Comando cut (que no es la central única de trabajadores)
Primero te quiero mostrar, de forma eminentemente práctica una instrucción simple de usar y muy útil: el comando
cut, Esta instrucción es usada para cortar un determinado pedazo de un archivo y tiene dos formas distintas de uso:
El comando cut con la opción -c
Con esta opción, el comando tiene la siguiente sintáxis:
cut -c PosIni-PosFim [archivo]
Donde:
PosIni = Posición inicial
PosFim = Posición final
%TERMINAL_INI%
$ cat números
%OUT_INI%1234567890
0987654321
1234554321
9876556789%OUT_FIM%
$ cut -c1-5 números
%OUT_INI%12345
09876
12345
98765%OUT_FIM%
$ cut -c-6 números%OUT_INI%
123456
098765
123455
987655%OUT_FIM%
$ cut -c4- números%OUT_INI%
4567890
7654321
4554321
6556789%OUT_FIM%
$ cut -c1,3,5,7,9 números%OUT_INI%
13579
08642
13542
97568%OUT_FIM%
$ cut -c -3,5,8- números%OUT_INI%
1235890
0986321
1235321
9875789%OUT_FIM%
%TERMINAL_FIM%
Como dá para ver, en realidad existen cuatro sintáxis distintas: en la primera
(-c 1-5), especifiqué una faja de posiciones, en la segunda
(-c -6), especifiqué todo hasta una determinada posición, en la tercera
(-c 4-) de una determinada posición en adelante y en la cuarta
(-c 1,3,5,7,9), determinadas posiciones. La última
(-c -3,5,8-) fue solamnte para mostrar que podemos mesclar todo.
El comando cut con la opción -f
Pero no pienses que acabó por ahí! Como debes haber notado, esta forma de
cut es útil para archivos con campos de tamaño fijo, sin embargo, actualmente lo que más existe son archivos con campos de tamaño variables, donde cada campo termina con un delimitador. Vamos a dar una ojeada en el archivo
musicas que comenzamos a preparar en nuestra conversa de la última vez que estuvimos aqui en el bar.
%TERMINAL_INI%
$ cat musicas%OUT_INI%
album 1^Artista1~Musica1:Artista2~Musica2
album 2^Artista3~Musica3:Artista4~Musica4
album 3^Artista5~Musica5:Artista6~Musica5
album 4^Artista7~Musica7:Artista8~Musica8%OUT_FIM%
%TERMINAL_FIM%
Entonces, recapitulando, su
"layout" es el siguiente:
nombre del album^intérprete1~nombre de la música1:...:intérpreten~nombre de la músican
O sea, el nombre del álbum será separado por un circunflejo (
^) del resto del registro, que está formado por diversos grupos compuestos por el intérprete de cada música del CD y la respectiva música interpretada. Estos grupos son separados entre sí por dos puntos (
:) e internamente, el nombre del intérprete será separado por un til (
~) del nombre de la música.
Entonces para sacar los datos referentes a todas las segundas músicas del archivo
musicas, debemos hacer:
%TERMINAL_INI%
$ cut -f2 -d: musicas%OUT_INI%
Artista2~Musica2
Artista4~Musica4
Artista6~Musica5
Artista8~Musica8%OUT_FIM%
%TERMINAL_FIM%
O sea, cortamos el segundo campo (
-f de
field en inglés) delimitado (
-d) por dos puntos (
:). Pero en cambio, se quisieramos solamente los intérpretes, debemos hacer:
%TERMINAL_INI%
$ cut -f2 -d: musicas | cut -f1 -d~%OUT_INI%
Artista2
Artista4
Artista6
Artista8%OUT_FIM%
%TERMINAL_FIM%
Para entender eso, vamos a sacar la primera línea de
musicas:
%TERMINAL_INI%
$ head -1 musicas%OUT_INI%
album 1^Artista1~Musica1:Artista2~Musica2%OUT_FIM%
%TERMINAL_FIM%
Entonces, observe lo que fue hecho:
Delimitador del primero
cut (
:)
%T1%album 1^Artista1~Musica1%T0%:%T2%Artista2~Musica2%T0%
De esta forma, en el primero
cut, el primer campo del delimitador (
-d) dos puntos (
:) es %T1%album 1^Artista1~Musica1%T0% y el segundo, que es lo que nos interesa, es %T2%Artista2~Musica2%T0%.
Vamos ahora a ver lo que pasó con el segundo
cut:
Nuevo delimitador (
~)
%T1%Artista2%T0%~%T2%Musica2%T0%
Ahora, primer campo del delimitador (
-d) til (
~), que es el que nos interesa, é %T1%Artista2%T0% y el segundo es %T2%Musica2%T0%.
Si el racionamiento que hicimos para la primera línea fuera aplicado en el resto del archivo, llegaremos a la respuesta anteriormente dada.
Si hay cut hay paste
Como ya era de esperar, el comando
paste sirve para pegar, solo que aqui en
Shell lo que pega son archivos. Solo para comenzar a entenderlo, vamos a hacer así::
paste arch1 arch2
De esta forma él mandará para la salida padrón (
stdout) cada uno de los registros de
arch1 al lado de los registros de
arch2 correspondientes y en caso de que ningún delimitador sea especificado, usará por
default el
<TAB>.
El paste es un comando poco usado por su sintáxis ser poco conocida. Vamos a jugar con 2 archivos creados de la siguiente forma:
%TERMINAL_INI%
$ seq 10 > enteros
$ seq 2 2 10 > pares
%TERMINAL_FIM%
Para ver el contenido de los archivos creados, vamos a usar el
paste en su forma simple que mostramos arriba:
%TERMINAL_INI%
$ paste enteros pares%OUT_INI%
1 2
2 4
3 6
4 8
5 10
6
7
8
9
10%OUT_FIM%
%TERMINAL_FIM%
Quién está en pié, se acuesta
Ahora vamos a transformar la columna del
pares en línea:
%TERMINAL_INI%
$ paste -s pares%OUT_INI%
2 4 6 8 10%OUT_FIM%
%TERMINAL_FIM%
Usando separadores
Como ya fue dicho, el separador
default del
paste es el
<TAB>, pero eso puede ser alterado con la opción
-d. Entonces para calcular la suma del contenido de
pares primeramente hariamos:
%TERMINAL_INI%
$ paste -s -d'+' pares # tambiém podria ser -sd'+'%OUT_INI%
2+4+6+8+10%OUT_FIM%
%TERMINAL_FIM%
e después pasaríamos esta línea para la calculadora (
bc) y entonces quedaria:
%TERMINAL_INI%
$ paste -sd'+' pares | bc%OUT_INI%
30%OUT_FIM%
%TERMINAL_FIM%
De esta forma, para calcular el factorial del número contenido en
$Num, basta hacer:
%TERMINAL_INI%
$ seq $Num | paste -sd'*' | bc
%TERMINAL_FIM%
Con el comando
paste tu tambiém puedes montar formatos exóticos como este a seguir:
%TERMINAL_INI%
$ ls | paste -s -d'\t\t\n'%OUT_INI%
arch1 arch2 arch3
arch4 arch5 arch6%OUT_FIM%
%TERMINAL_FIM%
Lo que pasó fue lo siguiente: fue especificado para el comando
paste que tendria que transformar líneas en columnas (por la opción
-s) y que sus separadores (si...! El acepta más de uno, pero solamente uno después de cada columna creada por el comando) serían un
<TAB>, otra
<TAB> y un
<ENTER>, generando de esta forma su salida tabulada en 3 columnas.
Ahora que ya entendiste esto, ve como hacer la misma cosa, pero de forma más fácil, menos extraña y primitiva, usando el mismo comando pero con la siguiente sintáxis:
%TERMINAL_INI%
$ ls | paste - - -%OUT_INI%
arch1 arch2 arch3
arch4 arch5 arch6%OUT_FIM%
%TERMINAL_FIM%
Y esto sucede porque si em vez de especificar los archivos colocamos el signo de menos (
-), el comando
paste los substituye por la salida o entrada padrón conforme el caso. En el ejemplo anterior los datos fueran mandados para la salida padrón (
stdout), porque el
pipe (
|) estaba desviando la salida del
ls para la entrada padrón (
stdin) del
paste, pero vea el ejemplo a seguir:
%TERMINAL_INI%
$ cat arch1%OUT_INI%
predisposición
privilegiado
profesional%OUT_FIM%
$ cat arch2%OUT_INI%
ver
mario
motor%OUT_FIM%
$ cut -c-3 arq1 | paste -d "" - arq2%OUT_INI%
prever
primario
promotor%OUT_FIM%
%TERMINAL_FIM%
En este caso, el
cut devolvió las tres primeras letras de cada registro de
arch1, el
paste fue montado para no tener separador (
-d"") y recibir la entrada padrón (desviada por el
pipe) en el trazo (
-), generando la salida junto con
arch2.
El Comando tr
Otro comando muy interesante es el tr que sirve para substituir, comprimir o retirar caracteres. Su sintáxis sigue el siguiente padrón:
tr [opciones] cadena1 [cadena2]
El comando
tr copia el texto de la entrada padrón (
stdin) y cambia las veces que halle los caracteres de
cadena1 por el correspondiente contenido de la
cadena2 o cambia las múltiples ocurrencias de los caracteres de
cadena1 por solamente un caracter, o todavia puede retirar los caracteres de la
cadena1.
Las principales
opciones del comando son:
| Principales Opciones del comando tr |
| Opción |
Significado |
| -s |
Comprime n ocurrencias de la cadena1 en solo una |
| -d |
Retira del archivo los caracteres de la cadena1 |
Cambiando caracteres con tr
Primero te voy a dar un ejemplo bien bobo:
%TERMINAL_INI%
$ echo bobo | tr o a%OUT_INI%
baba%OUT_FIM%
%TERMINAL_FIM%
O sea, cambié todas sas ocurrencias de la letra
o por la letra
a.
Suponga que en un determinado punto de mi
script pido al operador para teclear
s o
n (si o no), y guardo su respuesta em la variable
$Resp. El contenido de
$Resp puede estar con letra mayúscula o minúscula, y de esta forma tendria que hacer diversos tests para saber si la respuesta dada fue
S,
s,
N o
n. Entonces lo mejor es hacer:
%TERMINAL_INI%
$ Resp=$(echo $Resp | tr SN sn)
%TERMINAL_FIM%
y luego de este comando tendria seguridade de que el contenido de
$Resp seria un
s o un
n.
Si mi archivo
ArchEnt está todo escrito con letras mayúsculas y deseo pasarlas para minúsculas hago:
%TERMINAL_INI%
$ tr A-Z a-z < ArchEnt > /tmp/$$
$ mv -f /tmp/$$ ArchEnt
%TERMINAL_FIM%
Note que em este caso usé la notacióno
A-Z para no tener que escribir
ABCD...YZ. Otro tipo de notación que puede ser usada son las
escape sequences (preferiria escribir en español, pero en este caso como lo traduciría? Secuencias de escape? Medio sin sentido, no te parece? Pero continuemos...) que tambiém son reconocidas por otros comandos y también en lenguaje C, y cuyo significado verás a seguir:
| Escape Sequences |
| Secuencia |
Significado |
Octal |
| \t |
Tabulación |
\011 |
| \n |
Nueva línea |
\012 |
| \v |
Tabulación Vertical |
\013 |
| \f |
Nueva Página |
\014 |
| \r |
Início de línea <^M> |
\015 |
| \\ |
Una barra invertida |
\0134 |
Sacando caracteres con tr
Entonces dejame contarte un "causo": un alumno que estaba enojado conmigo, resolvió complicar mi vida y en un ejercicio práctico valiendo nota, que pasé para ser hecho en el computador, me entregó el
script con todos los comandos separados por punto y coma (recuerdas que te dije que el punto y coma servía para separar diversos comandos en una misma línea?).
Voy a dar un ejemplo simplificado e idiota de un "chorizo" así:
%TERMINAL_INI%
$ cat confuso%OUT_INI%
echo lea Programación Shell Linux de Julio Cezar Neves > libro;cat libro;pwd;ls;rm -f lixo 2>/dev/null;cd ~%OUT_FIM%
%TERMINAL_FIM%
Yo ejecutaba el programa y él funcionaba:
%TERMINAL_INI%
$ confuso%OUT_INI%
lea Programación Shell Linux de Julio Cezar Neves
/home/jneves/LM
confuso livro musexc musicas musinc muslist numeros%OUT_FIM%
%TERMINAL_FIM%
Pero prueba es cosa seria (y billete de dólar es todavia más :)) entonces, para entender lo que el aluno habia hecho, lo llamé y en su frente ejecuté el siguiente comando:
%TERMINAL_INI%
$ tr ";" "\n" < confuso%OUT_INI%
echo lea Programación Shell Linux de Julio Cezar Neves
pwd
ls
rm -f lixo 2>/dev/null%OUT_FIM%
cd ~
%TERMINAL_FIM%
El alumno quedó muy triste, porque en 2 o 3 segundos le deshice la broma en la cual perdió horas.
Ahora vea bien! Si yo tuviera una máquina con Unix, habria hecho en cambio:
%TERMINAL_INI%
$ tr ";" "\012" < confuso
%TERMINAL_FIM%
Xprimiendo con tr
Ahora vea la diferencia entre los dos comandos
date: el que hice hoy y otro que fue ejecutado hace dos semanas:
%TERMINAL_INI%
$ date # Hoy%OUT_INI%
Sun Sep 19 14:59:54 2006%OUT_FIM%
$ date # Hace dos semanas%OUT_INI%
Sun Sep 5 10:12:33 2006%OUT_FIM%
%TERMINAL_FIM%
Para ver la hora debería hacer:
%TERMINAL_INI%
$ date | cut -f 4 -d ' '%OUT_INI%
14:59:54%OUT_FIM%
%TERMINAL_FIM%
Sin embargo, dos semanas antes ocurriria lo siguiente:
%TERMINAL_INI%
$ date | cut -f 4 -d ' '%OUT_INI%
5%OUT_FIM%
%TERMINAL_FIM%
Ahora observe porqué:
%TERMINAL_INI%
$ date # Hace dos semanas%OUT_INI%
Sun Sep 5 10:12:33 2004%OUT_FIM%
%TERMINAL_FIM%
Como puedes notar, existen 2 caracteres en blanco antes del
5 (día), lo que embroma todo porque el tercer pedazo está vacio y el cuarto es el día (
5). Entonces lo ideal sería comprimir los espacios en blanco sucesivos en solamente un espacio para poder tratar las dos cadenas resultantes del comando
date de la misma forma, y eso se hace así:
%TERMINAL_INI%
$ date | tr -s " "a%OUT_INI%
Sun Sep 5 10:12:33 2004%OUT_FIM%
%TERMINAL_FIM%
Como puedes ver, no existen más los dos espacios, Entonces ahora podria cortar:
%TERMINAL_INI%
$ date | tr -s " " | cut -f 4 -d " "%OUT_INI%
10:12:33%OUT_FIM%
%TERMINAL_FIM%
Mira ahí, vé como el
Shell ya está solucionando problemas!. Vea este archivo que fue bajado de una máquina con aquél sistema operacional que sufre de todos los vírus:
%TERMINAL_INI%
$ cat -ve ArqDoDOS.txt%OUT_INI%
Este archivo^M$
foi generado por^M$
DOS/Rwin y fue^M$
bajado por un^M$
ftp mal hecho.^M$%OUT_FIM%
%TERMINAL_FIM%
y ahora te quiero dar dos consejos:
%DICA_INI%
Consejo #1 - La opción
-v del
cat muestra los caracteres de control invisibles, con la notación
^L, donde
^ es la tecla control y
L es la respectiva letra. La opción
-e muestra el final de la línea como un signo de pesos (
$).
%DICA_FIM%
%DICA_INI%
Consejo #2 - Esto ocurre porque en formato DOS (o rwin), el fin de los registros está formado por un
carriage-return (
\r) y un
line-feed (
\n). En Linux sin embargo, el final del registro tiene solamente el
line-feed.
%DICA_FIM%
Vamos entonces a limpiar este archivo.
%TERMINAL_INI%
$ tr -d '\r' < ArchDeDOS.txt > /tmp/$$
$ mv -f /tmp/$$ ArchDeDOS.txt
%TERMINAL_FIM%
Ahora vamos a ver lo que pasó:
%TERMINAL_INI%
$ cat -ve ArchDeDOS.txt%OUT_INI%
Este archivo$
foi generado por el$
DOS/Rwin y fue$
bajado por un$
ftp mal hecho.$%OUT_FIM%
%TERMINAL_FIM%
Bien, la opción
-d del
tr retira los caracteres especificados de todo el archivo. De esta forma retiré los caracteres no deseados, grabando en un archivo temporario de trabajo y posteriormente lo renombré con su nombre original.
Obs: En Unix debería hacer:
%TERMINAL_INI%
$ tr -d '\015' < ArchDeDOS.txt > /tmp/$$
%TERMINAL_FIM%
%ATENCIÓN_INI%
Esto pasó porque el
ftp fue hecho de modo binario (o
image), o sea, sin la interpretación del texto. Si antes de la transmisión del archivo hubiera sido estipulada la opción
ascii del
ftp, esto no habría ocurrido.
%ATENCIÓN_FIM%
- Mira, después de este consejo, estoy comenzando a gustar de este tal de
Shell, pero todavia hay mucha cosa que no consigo hacer.
- Claro!, hasta aqui no te hablé casi nada sobre programación en
Shell, tenemos mucha cosa para aprender, sin embargo, con lo que aprendiste, ya dá para resolver muchos problemas, desde que tú adquieras el “modo
Shell de pensar”. Serías capaz de hacer un
script para decirme quienes son las personas que están “logadas” por más de un dia en tu servidor?
- Claro que no! Para eso seria necesario que conociera los comandos condicionales que todavia no me explicaste como funcionan.
- Dejame intentar cambiar un pouco tu lógica y traerla para el “modo
Shell de pensar”, pero antes es mejor tomar un chope... Chico!, traeme otros dos...
- Ahora que ya mojéi a palavra, vamos a resolver el problema que te propuse. Nota como funciona el comando who:
%TERMINAL_INI%
$ who%OUT_INI%
jneves pts/1 Sep 18 13:40
rtorres pts/0 Sep 20 07:01
rlegaria pts/1 Sep 20 08:19
lcarlos pts/3 Sep 20 10:01%OUT_FIM%
%TERMINAL_FIM%
Y mira tambiém el
date:
%TERMINAL_INI%
$ date%OUT_INI%
Mon Sep 20 10:47:19 BRT 2004%OUT_FIM%
%TERMINAL_FIM%
Ves que el mes y el dia están em el mismo formato en ambos comandos?
%DICA_INI%
Algunas vezes un comando tiene la salida en portugués y el otro en inglés. Cuando eso pase, puedes usar el siguiente artificio:
%TERMINAL_INI%
$ date%OUT_INI%
Mon Sep 20 10:47:19 BRT 2004%OUT_FIM%
$ LANG=pt_BR date%OUT_INI%
Seg Set 20 10:47:19 BRT 2004%OUT_FIM%
%TERMINAL_FIM%
Y así pasas la salida del comando
date para portugués, o para otro idioma que quieras.
%DICA_FIM%
Entonces, si en algún registro del
who no encuentro la fecha de hoy, significa que el individuo está "logado" por más de un día, ya que él no puede haberse "logado" mañana... Vamos a guardar el pedazo que importa de la fecha de hoy para buscarla em la salida del
who:
%TERMINAL_INI%
$ Fecha=$(date | cut -c 5-10)
%TERMINAL_FIM%
Aquí usé la construcción
$(...), para dar prioridad a la ejecución de los comandos antes de atribuir a su salida a la variable
$Fecha. Vamos a ver si funcionó:
%TERMINAL_INI%
$ echo $Fecha%OUT_INI%
Sep 20%OUT_FIM%
%TERMINAL_FIM%
Muy bien! Ahora, lo que tenemos que hacer es buscar en el comando
who los registros que no poseen esta fecha.
- Ah! Me parece que estoy entendiendo! Tu mencionaste en buscar y se me ocurrió el comando
grep, acerté?
- Correctísimo! Solo que tengo que usar el
grep con aquella opción que solamente lista sos registros em los quales él
no encontró la cadena. Te acuerdas que opción es esa?
- Claro, es la opción
-v...
- Eso mismo! Estás quedando un lujo! Entonces vamos a ver:
%TERMINAL_INI%
$ who | grep -v "$Fecha"%OUT_INI%
jneves pts/1 Sep 18 13:40%OUT_FIM%
%TERMINAL_FIM%
- Y si quisiera un poco mas de adornos,haría así:
%TERMINAL_INI%
$ who | grep -v "$Fecha" | cut -f1 -d ' '%OUT_INI%
jneves%OUT_FIM%
%TERMINAL_FIM%
- Viste? No fue necesario usar ningún comando condicional, porque además nuestro comando condicional más usado, el famoso
if, no verifica condición sino instrucciones, como veremos ahora.
Comandos Condicionales
Vea las líneas de comando que siguen:
%TERMINAL_INI%
$ ls musicas%OUT_INI%
musicas%OUT_FIM%
$ echo $?%OUT_INI%
0%OUT_FIM%
$ ls ArchInexistente%OUT_INI%
ls: ArchInexistente: No such file or directory%OUT_FIM%
$ echo $?%OUT_INI%
1%OUT_FIM%
$ who | grep jneves%OUT_INI%
jneves pts/1 Sep 18 13:40 (10.2.4.144)%OUT_FIM%
$ echo $?%OUT_INI%
0%OUT_FIM%
$ who | grep juliana%OUT_INI%
$ echo $?%OUT_FIM%
1
%TERMINAL_FIM%
- y que hace ese
$? por ahí? Comenzando por pesos (
$) parece ser una variable, correcto?
- Si, es una variable que contiene el código de salida de la última instrucción ejecutada. Te puedo garantizar que si esta instrucción fué bien ejecutada, $? tendrá el valor cero, em caso contrario su valor será diferente de cero.
El Comando if
Lo que nuestro comando condicional
if hace es testar la variável
$?. Entonces vamos a ver su sintáxis:
if cmd
then
cmd1
cmd2
cmdn
else
cmd3
cmd4
cmdm
fi
o sea: en caso que el comando
cmd halla sido ejecutado con éxito, los comandos del bloque del
then (
cmd1,
cmd2 y
cmdn) serán ejecutados, em el caso contrario, los comandos ejecutados serán los del bloque opcional del
else (
cmd3,
cmd4 y
cmdm), terminando con un
fi.
Vamos a ver em la prática como eso funciona usando un
scriptisiño que sirve para incluir usuários en el
/etc/passwd:
%TERMINAL_INI%
$ cat incusu%OUT_INI%
#!/bin/bash
# Versión 1
if grep ^$1 /etc/passwd
then
echo Usuario \'$1\' ya existe
else
if useradd $1
then
echo Usuario \'$1\' incluído en /etc/passwd
else
echo "Problemas em el catastro. Usted es root?"
fi
fi%OUT_FIM%
%TERMINAL_FIM%
Nota que el
if está verificando diretamente el comando
grep y ésta es su finalidad. En caso de que el
if sea exitoso, o sea, el usuário (cuyo nombre está en
$1) fuera encontrado en
/etc/passwd, los comandos del bloque del
then serán ejecutados (en este ejemplo es solamente el
echo) y en el caso contrario, las instrucciones del bloque del
else son las que serán ejecutadas, cuando entonces un nuevo
if verifica si el comando
useradd fué bien ejecutado , creando el registro del usuario en
/etc/passwd, o no cuando entonces dará el mensaje de error.
Veamos su ejecución, primero pasando un usuario ya existente:
%TERMINAL_INI%
$ incusu jneves%OUT_INI%
jneves:x:54002:1001:Julio Neves:/home/jneves:/bin/bash
Usuario 'jneves' ya existe%OUT_FIM%
%TERMINAL_FIM%
Como ya vimos diversas veces, pero siempre es bueno insistir en el tema para que quedes alertado, en el ejemplo dado surgió una línea no deseada, ella es la salida del comando
grep. Para evitar que eso pase, debemos desviar la salida de esta instrucción para
/dev/null, quedando así:
%TERMINAL_INI%
$ cat incusu%OUT_INI%
#!/bin/bash
# Versión 2
if grep ^$1 /etc/passwd > /dev/null # ou: if grep -q ^$1 /etc/passwd
then
echo Usuario \'$1\' ya existe
else
if useradd $1
then
echo Usuario \'$1\' incluído en /etc/passwd
else
echo "Problemas en el catastro. Usted es root?"
fi
fi%OUT_FIM%
%TERMINAL_FIM%
Ahora vamos a verificarlo, pero como usuario normal (no
root):
%TERMINAL_INI%
$ incusu JuanNadie%OUT_INI%
./incusu[6]: useradd: not found
Problemas en el catastro. Usted es root?%OUT_FIM%
%TERMINAL_FIM%
Epa!, aquél error no era para pasar! Para evitar que eso suceda debemos mandar também la salida de error (
strerr, te acuerdas?) del
useradd para
/dev/null, quedando en la versión final así:
%TERMINAL_INI%
$ cat incusu%OUT_INI%
#!/bin/bash
# Versión 3
if grep ^$1 /etc/passwd > /dev/null
then
echo Usuario \'$1\' ya existe
else
if useradd $1 2> /dev/null
then
echo Usuario \'$1\' incluído en /etc/passwd
else
echo "Problemas en el catastro. Usted es root??"
fi
fi%OUT_FIM%
%TERMINAL_FIM%
Después de estas alteraciones y de hacer un
su – (volverme
root) veamos su comportamiento:
%TERMINAL_INI%
$ incusu perez%OUT_INI%
Usuario 'perez' incluído en /etc/passwd%OUT_FIM%
%TERMINAL_FIM%
Y nuevamente:
%TERMINAL_INI%
$ incusu perez%OUT_INI%
Usuario 'perez' ya existe%OUT_FIM%
%TERMINAL_FIM%
Recuerdas que te dije que a lo largo de nuestras conversas y choppes nuestros programas irían a ir mejorando? Entonces veamos ahora como podríamos mejorar nuestro programa para incluir músicas:
%TERMINAL_INI%
$ cat musinc%OUT_INI%
#!/bin/bash
# Incluye CDs (versión 3)
#
if grep "^$1$" musicas > /dev/null
then
echo Este álbum ya está incluído
else
echo $1 >> musicas
sort musicas -o musicas
fi%OUT_FIM%
%TERMINAL_FIM%
Como viste, es una pequeña evolución de la versión anterior, de forma que, antes de incluir un registro (que por la versión anterior podría ser duplicado), verificamos si el registro comenzaba (
^) y terminaba (
$) igual al parámetro pasado (
$1). El uso del circunflejo (
^) en el inicio de la cadena y el pesos (
$) en el fin, son para verificar si el parâmetro pasado (el álbum y sus datos) son exactamente iguales a algún registro anteriormente incluído y no unicamente igual a un pedazo de alguno de los registros.
Vamos a ejecutarlo pasando un álbum ya anteriormente incluído:
%TERMINAL_INI%
$ musinc "album 4^Artista7~Musica7:Artista8~Musica8"%OUT_INI%
Este álbum ya está incluído%OUT_FIM%
%TERMINAL_FIM%
Y ahora uno no incluído:
%TERMINAL_INI%
$ musinc "album 5^Artista9~Musica9:Artista10~Musica10"
$ cat musicas%OUT_INI%
album 1^Artista1~Musica1:Artista2~Musica2
album 2^Artista3~Musica3:Artista4~Musica4
album 3^Artista5~Musica5:Artista6~Musica5
album 4^Artista7~Musica7:Artista8~Musica8
album 5^Artista9~Musica9:Artista10~Musica10%OUT_FIM%
%TERMINAL_FIM%
- Como viste, el programa mejoró un poquito, pero todavia no está pronto. A medida que te vaya enseñando a programar en
shell, nuestra CDteca va a ir quedando cada vez mejor.
- Entendí todo lo que me explicaste, pero todavia no sé como hacer un
if para verificar condiciones, o sea el uso normal del comando.
- Mira, para eso existe el comando
test, él es el que verifica condiciones. El comando
if verifica el comando
test. Pero eso está medio confuso y como ya hablé mucho, esty precisando de unos choppes para mojar la palabra. Vamos a parar por aqui y en la próxima vez te explico claramente el uso del
test y de diversas otras sintáxis del
if.
- Estamos de acuerdo entonces! Me parece bien porque yo tambiém estoy quedando tonto y así aprovecho para practicar esa cantidad de cosas de las cuales me hablaste hoy .
- Para fijar lo que aprendiste, trata de hacer un
scriptiziño para informar si un determinado usuario, que será pasado como parámetro está logado (ajjjhh!) o no.
- Chico,dos choppes más por favor...
Y no te olvides, cualquer duda o falta de compañia para tomar un chopp o hasta para hablar mal de los políticos lo único que tienes que hacer es mandarme un e-mail para
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 - 03 Oct 2006
--
HumbertoPina - 03 Oct 2006
--
HumbertoPina - 03 Oct 2006