domingo, 1 de febrero de 2009

Programando en shell script

Ejecución de un script

Los scripts deben empezar por el número mágico #! seguido del programa a usar para interpretar el script:
  • #!/bin/bash script de bash
  • #!/bin/sh script de shell
  • #!/usr/bin/perl script de perl
Las forma usuales de ejecutar un script es:
  • darle permiso de ejecución al fichero y ejecutarlo como un comando:
    $ chmod +x helloworld
    ./helloworld
  • ejecutar una shell poniendo como argumento el nombre del script (sólo necesita permiso de lectura)
    $ bash helloworld

Ejecución con la shell actual

Los métodos anteriores arrancan una sub-shell que lee las ordenes del fichero, la ejecuta y después termina cediendo el control nuevamente a la shell original
Existe una forma de decirle a la shell actual que lea y ejecute una serie de ordenes por si misma sin arrancar una sub-shell:
$ . helloworld
o bien:
$ source helloworld
Ejemplo:
$ cat shellid.sh
#!/bin/bash
echo "Shell ejecutando el script, PID = $$"
$ echo "PID actual = $$"
PID actual = 6919
$ bash shellid.sh
Shell ejecutando el script, PID = 26824
$ . shellid.sh
Shell ejecutando el script, PID = 6919




Paso de parámetros

Es posible pasar parámetros a un scripts: los parámetros se recogen en las variables $1 a $9
Variable Uso
$0 el nombre del script
$1 a $9 parámetros del 1 al 9
${10}, ${11},... parámetros por encima del 10
$# número de parámetros
$ * , $@ todos los parámetros
Ejemplo:
$ cat parms1.sh
#!/bin/bash
VAL=$((${1:-0} + ${2:-0} + ${3:-0}))
echo $VAL
$ bash parms1.sh 2 3 5
10
$ bash parms1.sh 2 3
5

shift

Desplaza los parámetros hacia la izquierda el número de posiciones indicado:
$ cat parms2.sh
#!/bin/bash
echo $#
echo $ *
echo "$1 $2 $3 $4 $5 $6 $7 $8 $9 ${10} ${11}"
shift 9
echo $1 $2 $3
echo $#
echo $ *
$ bash parms2.sh a b c d e f g h i j k l
12
a b c d e f g h i j k l
a b c d e f g h i j k
j k l
3
j k l

Diferencia entre $ * y $@

Los parámetros $ * y $@ sólo se diferencian si van entrecomillados:
  • "$ * "
    se expande a una sola palabra, conteniendo todos los parámetros y con el valor de cada parámetro separado por el primer carácter de la variable especial IFS (por defecto, un espacio)
  • "$@"
    cada parámetro se expande a una palabra separada; los parámetros entrecomillados se consideran uno solo aunque lleven espacios
Ejemplo:
$ cat parms3.sh
#!/bin/bash
IFS=":"
for par in "$*"
do echo "Parámetro es: $par"
done
echo "==================================="
for par in "$@"
do echo "Parámetro es: $par"
done
$ bash parms3.sh hola "como estas hoy?" bien gracias
Parámetro es: hola:como estas hoy?:bien:gracias
===================================
Parámetro es: hola
Parámetro es: como estas hoy?
Parámetro es: bien
Parámetro es: gracias

set --

El comando set -- $var copia las palabras de la variable $var en los parámetros posicionales $1, $2, ...
$ cat setdata
hola
como estás?
$
$ cat set.sh
#!/bin/bash
file=$(cat $1) # Guarda en file el fichero pasado
set -- $file
echo $#
echo "$1 - $2 - $3"
$
$ bash set.sh setdata
3
hola - como - estás?



Entrada/salida

Es posible leer desde la entrada estándar o desde fichero usando read y redirecciones:
#!/bin/bash
echo -n "Introduce algo: "
read x
echo "Has escrito $x"
echo -n "Escribe 2 palabras: "
read x y
echo "Primera palabra $x; Segunda palabra $y"
Si queremos leer o escribir a un fichero utilizamos redirecciones:
echo $X > fichero
read X <>
Este último caso lee la primera línea de fichero y la guarda en la variable X
  • Si queremos leer un fichero línea a línea podemos usar while:
    #!/bin/bash
    # FILE: linelist
    # Usar: linelist filein fileout
    # Lee el fichero pasado en filein y
    # lo salva en fileout con las lineas numeradas
    count=0
    while read BUFFER
    do
    count=$((++count))
    echo "$count $BUFFER" » $2
    done < $1
    • el fichero de entrada se va leyendo línea a línea y almacenando en BUFFER
    • count cuenta las líneas que se van leyendo
  • El uso de lazos para leer ficheros es bastante ineficiente
    • deberían evitarse (por ejemplo, usar cat fichero)
Ejemplo de lectura de fichero
#!/bin/bash
# Usa $IFS para dividir la línea que se está leyendo
# por defecto, la separación es "espacio"
echo "Lista de todos los usuarios:"
OIFS=$IFS # Salva el valor de IFS
IFS=: # /etc/passwd usa ":" para separar los campos
cat /etc/passwd |
while read name passwd uid gid fullname ignore
do
echo "$name ($fullname)"
done
IFS=$OIFS # Recupera el $IFS original
  • El fichero /etc/passwd se lee línea a línea
    • para cada línea, sus campos se almacenan en las variables que siguen a read
    • la separación entre campos la determina la variable $IFS (por defecto, espacio en blanco)

Redirecciones

Las redirecciones y pipes pueden usarse en otras estructuras de control

Ejemplo: lee las 2 primeras líneas de un fichero

if true
then
read x
read y
fi <>
Ejemplo: lee líneas de teclado y guardalas en un fichero temporal convirtiendo minúsculas en mayúsculas
#/bin/bash
read buf
while [ "$buf" ]
do
echo $buf
read buf
done | tr ´a-z´ ´A-Z´ > tmp.$$
Ejemplo: usa exec para redireccionar la entrada estándar
# redirige la entrada estándar al descriptor 7
exec 7<&0 # redirige /etc/passwd a la entrada estándar exec 0< /etc/passwd # lee la primera línea de /etc/passwd read rootpasswd # lee de la entrada estándar echo -n "Escribe algo en el teclado " read entrada_de_teclado <&7 echo "Has escrito: $entrada_de_teclado" # eliminamos las redirecciones exec 0<&7 # cierra el descriptor de fichero 7 exec 7<&- # lee otra vez de la entrada estándar echo -n "Escribe otra cosa " read entrada_otra_vez echo "Ahora escribes: $entrada_otra_vez"


Tests

Los comandos que se ejecutan en un shell tienen un código de salida, que se almacena en la variable $?
  • si $? es 0 el comando terminó bien
  • si $? es > 0 el comando terminó mal
Ejemplo:
$ ls /bin/ls
/bin/ls
$ echo $?
0
$ ls /bin/ll
ls: /bin/ll: Non hai tal ficheiro ou directorio
$ echo $?
1
Podemos chequear la salida de dos comandos mediante los operadores && (AND) y || (OR)
  • estos operadores actúan en cortocircuito:
    comando1 && comando2
    comando2 sólo se ejecuta si comando1 acaba bien
    comando1 || comando2
    comando2 sólo se ejecuta si comando1 falla
  • comandos true y false: devuelven 0 y 1, respectivamente
Ejemplo con &&:
$ ls /bin/ls && ls /bin/ll
/bin/ls
ls: /bin/ll: Non hai tal ficheiro ou directorio
$ echo $?
1
$ ls /bin/ll && ls /bin/ls
ls: /bin/ll: Non hai tal ficheiro ou directorio
$ echo $?
1
Ejemplo con ||:
$ ls /bin/ls || ls /bin/ll
/bin/ls
$ echo $?
0
$ ls /bin/ll || ls /bin/ls
ls: /bin/ll: Non hai tal ficheiro ou directorio
/bin/ls
$ echo $?
0

Estructura if...then...else

Podemos usar el estado de salida de uno o varios comandos para tomar decisiones:
if comando1
then
ejecuta otros comandos
elif comando2
then
ejecuta otros comandos
else
ejecuta otros comandos
fi
  • debe respetarse la colocación de los then, else y fi
    • también puede escribirse if comando1 ; then
  • el elif y el else son opcionales, no así el fi
Ejemplo:
$ cat if.sh
#!/bin/bash
if (ls /bin/ls && ls /bin/ll) >/dev/null 2>&1
then
echo "Encontrados ls y ll"
else
echo "Falta uno de los ficheros"
fi
$ bash if.sh
Falta uno de los ficheros

Comando test

Notar que if sólo chequea el código de salida de un comando, no puede usarse para comparar valores: para eso se usa el comando test
El comando test permite:
  • chequear la longitud de un string
  • comparar dos strings o dos números
  • chequear el tipo de un fichero
  • chequear los permisos de un fichero
  • combinar condiciones juntas
test puede usarse de dos formas:
test expresión
o bien
[ expresión ]6
Si la expresión es correcta test devuelve un código de salida 0, si es falsa, devuelve 1:
  • este código puede usarse para tomar decisiones:

    if [ "$1" = "hola" ]
    then
    echo "Hola a ti también"
    else
    echo "No te digo hola"
    fi
    if [ $2 ]
    then
    echo "El segundo parámetro es $2"
    else
    echo "No hay segundo parámetro"
    fi
  • en el segundo if la expresión es correcta si $2 tiene algún valor; falsa si la variable no está definida o contiene null ("")

Expresiones

Existen expresiones para chequear strings, números o ficheros

Chequeo de strings

Expresión Verdadero sí
string el string es no nulo ("")
-z string la longitud del string es 0
-n string la longitud del string no es 0
string1 = string2 los strings son iguales
string1 != string2 los strings son distintos

Chequeo de enteros

Expresión Verdadero sí
int1 -eq int2 los enteros son iguales
int1 -ne int2 los enteros son distintos
int1 -gt int2 int1 mayor que int2
int1 -ge int2 int1 mayor o igual que int2
int1 -lt int2 int1 menor que int2
int1 -le int2 int1 menor o igual que int2

Chequeo de ficheros

Expresión Verdadero sí
-e file file existe
-r file file existe y es legible
-w file file existe y se puede escribir
-x file file existe y es ejecutable
-f file file existe y es de tipo regular
-d file file existe y es un directorio
-c file file existe y es un dispositivo de caracteres
-b file file existe y es un dispositivo de bloques
-p file file existe y es un pipe
-S file file existe y es un socket
-L file file existe y es un enlace simbólico
-u file file existe y es setuid
-g file file existe y es setgid
-k file file existe y tiene activo el sticky bit
-s file file existe y tiene tamaño mayor que 0

Operadores lógicos con test

Expresión Propósito
! invierte el resultado de una expresión
-a operador AND
-o operador OR
( expr ) agrupación de expresiones; los paréntesis tienen un significado especial para el shell, por lo que hay que escaparlos
Ejemplos:
$ test -f /bin/ls -a -f /bin/ll ; echo $?
1
$ test -c /dev/null ; echo $?
0
$ [ -s /dev/null ] ; echo $?
1
$ [ ! -w /etc/passwd ] && echo "No puedo escribir"
No puedo escribir
$ [ $$ -gt 0 -a \( $$ -lt 5000 -o -w file \) ]

Comando de test extendido

A partir de la versión 2.02 de Bash se introduce el extended test command: [[ expr ]]
  • permite realizar comparaciones de un modo similar al de lenguajes estándar:
    • permite usar los operadores && y || para unir expresiones
    • no necesita escapar los paréntesis
Ejemplos:
$ [[ -f /bin/ls && -f /bin/ll ]] ; echo $?
1
$ [[ $$ -gt 0 && ($$ -lt 5000 || -w file) ]]


Control de flujo

Además del if bash permite otras estructuras de control de flujo: case, for, while y until

Estructura case

Formato:
case valor in
patrón_1)
comandos si value = patrón_1
comandos si value = patrón_1 ;;
patrón_2)
comandos si value = patrón_2 ;;
* )
comandos por defecto ;;
esac
  • si valor no coincide con ningún patrón se ejecutan los comandos después del * )
    • esta entrada es opcional
  • patrón puede incluir comodines y usar el símbolo | como operador OR
Ejemplo:
#!/bin/bash
echo -n "Respuesta: "
read RESPUESTA
case $RESPUESTA in
S* | s*)
RESPUESTA="SI" ;;
N* | n*)
RESPUESTA="NO" ;;
* )
RESPUESTA="PUEDE" ;;
esac
echo $RESPUESTA

Lazos for

Formato:
for var in lista
do
comandos
done
  • var toma los valores de la lista
    • puede usarse globbing para recorrer los ficheros
Ejemplo: recorrer una lista
LISTA="10 9 8 7 6 5 4 3 2 1"
for var in $LISTA
do
echo $var
done
Ejemplo: recorrer los ficheros * .bak de un directorio
dir="/var/tmp"
for file in $dir/ * .bak
do
rm -f $file
done
Sintaxis alternativa, similar a la de C
LIMIT=10
for ((a=1, b=LIMIT; a <= LIMIT; a++, b--))
do
echo "$a-$b"
done

Bucle while

Formato:
while comando
do
comandos
done
  • se ejecuta mientras que el código de salida de comando sea cierto
Ejemplo:
while [ $1 ]
do
echo $1
shift
done

Bucle until

Formato:
until comando
do
comandos
done
  • se ejecuta hasta que el código de salida de comando sea hace cierto
Ejemplo:
until [ "$1" = "" ]
do
echo $1
shift
done

break y continue

Permiten salir de un lazo (break) o saltar a la siguiente iteración (continue)
  • break permite especificar el número de lazos de los que queremos salir (break n)
Ejemplo con break:
# Imprime el contenido de los ficheros hasta que
# encuentra una línea en blanco
for file in $*
do
while read buf
do
if [ -z "$buf" ]
then
break 2
fi
echo $buf
done < $file
done
Ejemplo con continue:
# Muestra un fichero pero no las líneas de más
# de 80 caracteres
while read buf
do
cuenta=`echo $buf | wc -c`
if [ $cuenta -gt 80 ]
then
continue
fi
echo $buf
done < $1



Funciones

Podemos definir funciones en un script de shell:
funcion() {
comandos
}
y para llamarla:
funcion p1 p2 p3
Siempre tenemos que definir la función antes de llamarla:
#!/bin/bash
# Definición de funciones
funcion1() {
comandos
}
funcion2() {
comandos
}
# Programa principal
funcion1 p1 p2 p3

Paso de parámetros

La función referencia los parámetros pasados por posición, es decir, $1, $2, ..., y $ * para la lista completa:
$ cat funcion1.sh
#!/bin/bash
funcion1()
{
echo "Parámetros pasados a la función: $ * "
echo "Parámetro 1: $1"
echo "Parámetro 2: $2"
}
# Programa principal
funcion1 "hola" "que tal estás" adios
$
$ bash funcion1.sh
Parámetros pasados a la función: hola que tal estás adios
Parámetro 1: hola
Parámetro 2: que tal estás

Variables locales

Es posible definir variables locales en las funciones:
$ cat locales.sh
#!/bin/bash
testvars() {
local localX="localX en función"
X="X en función"
echo "Dentro de la función: $localX, $X, $globalX"
}
# Programa principal
localX="localX en main"
X="X en main"
globalX="globalX en main"
echo "Dentro de main: $localX, $X, $globalX"
# Llama a la función
testvars
echo "Otra vez dentro de main: $localX, $X, $globalX"
$ bash locales.sh
Dentro de main: localX en main, X en main, globalX en main
Dentro de la función: localX en función, X en función, globalX en main
Otra vez dentro de main: localX en main, X en función, globalX en main

return

Después de llamar a una función, $? tiene el código se salida del último comando ejecutado:
  • podemos ponerlo de forma explícita usando return
#!/bin/bash
funcion2() {
if [ -f /bin/ls -a -f /bin/ln ]; then
return 0
else
return 1
fi
}
# Programa principal
if funcion2; then
echo "Los dos ficheros existen"
else
echo "Falta uno de los ficheros - adiós"
exit 1
fi
return permite devolver un entero entre 0 y 255
#!/bin/bash
# maximum.sh: Máximo de dos enteros
EQUAL=0
max() {
if [ "$1" -eq "$2" ]; then
return $EQUAL
elif [ "$1" -gt "$2" ]; then
return $1
else
return $2
fi }
max 36 34
salida=$?
if [ "$salida" -eq $EQUAL ]; then
echo "Los números son iguales"
else
echo "El mayor es $salida"
fi

Recursión

Bash permite recursión, no recomendable debido a su coste
Ejemplo: recorre recursivamente los directorios
#!/bin/bash
tree() {
local DIR=$1
local ACTUALDIR=$(pwd)
cd $DIR
local filelist=$(ls)
for file in $filelist; do
if [ -d $file ]; then
tree "$DIR/$file"
else
echo "Dir. $(basename $DIR)--> Fichero $file"
fi
done
cd $ACTUALDIR
}
tree $1


Arrays

Bash soporta arrays unidimensionales
  • No es necesario especificar su tamaño ni reservar memoria
  • Los índices empiezan a contar en cero
  • Los elementos de un array pueden ser de tipos diferentes
  • Formas de inicialización:
    • array[índice]=valor
    • array=(valor0 valor1 ...)
    • array=([índice]=valor [índice]=valor ...)
    • declare -a array
  • Para acceder a un elemento de un array tenemos que ponerlo entre llaves: ${array[índice]}

Ejemplo de uso de arrays:

$ cat array1.sh
#!/bin/bash
# Los elementos del array no necesitan ser consecutivos
array[11]=23
array[13]=37
array[5]=$((${array[11]}+ ${array[13]}))
echo "array[5]=${array[5]}"
# Otra forma de inicializar un array
array2=( cero uno dos tres cuatro )
echo "array2[3]=${array2[3]}"
echo "Elementos en array2=${#array2[@]}"
# Una tercera forma
array3=( [8]=ocho [10]=10 [13]=trece )
echo "array3[10]=${array3[10]}"
echo "array3=${array3[@]}"
echo "array3=${array3[@]:10}"
$ bash array1.sh
array[5]=60
array2[3]=tres
Elementos en array2=5
array3[10]=10
array3=ocho 10 trece
array3=10 trece

Procesamiento de Textos

Comandos simples

Existe una serie de comandos simples para realizar operaciones concretas sobre ficheros de texto
  • Ordena las líneas alfabéticamente: sort
  • Escribe partes seleccionadas de un fichero a la salida estándar: cut
  • Une texto de varios ficheros: paste
  • Formatea párrafos: fmt
  • Borra y/o reemplaza caracteres: tr
  • Elimina líneas repetidas: uniq
  • Combina varios ficheros: join
  • Divide un fichero en ficheros más pequeños: split
  • Muestra el principio/final de un fichero: head/tail
  • Muestra el fichero al revés: tac, rev
  • Muestra el número de líneas, palabras y bytes de un fichero: wc
  • Añade números de línea: nl
  • Convierte TABs en espacios: expand
  • Muestra un fichero en diferentes formatos: od
Comentaremos brevemente cada uno de ellos

sort

ordena alfabéticamente líneas de texto y las muestra en la salida estándar

Formato:

sort [opciones] fichero

Algunas opciones:

  • -b ignora blancos al principio de línea
  • -f no distingue mayúsculas/minúsculas
  • -r orden inverso
  • -m mezcla ficheros previamente ordenados
  • -n ordena numéricamente
  • -k POS1[, POS2] ordena según los campos desde POS1 o POS2, o el final si no está POS2 (el primer campo es 1)

Ejemplos:

$ cat nombres.txt
María Pérez
luis Andión
Adriana Gómez
jorge pena

$ sort nombres.txt
Adriana Gómez
María Pérez
jorge pena
luis Andión
$ sort -f nombres.txt
Adriana Gómez
jorge pena
luis Andión
María Pérez

$ sort -f +1 +0 nombres.txt
luis Andión
Adriana Gómez
jorge pena
María Pérez

$ sort -f -k 2,2 nombres.txt
luis Andión
Adriana Gómez
jorge pena
María Pérez

cut

Escribe partes seleccionadas de un fichero a la salida estándar; puede usarse para seleccionar columnas o campos de un fichero específico

Formato:

cut [opciones] fichero

Algunas opciones:

  • -b, -c, -f corta por bytes, caracteres o campos, respectivamente
  • -d fija el carácter delimitador entre campos (por defecto, TAB)

Ejemplos:

$ cat nombres-ord.txt
Luis Andión
Adriana Gómez
Jorge Pena
María Pérez
$ cut -c 1-7 nombres-ord.txt
Luis An
Adriana
Jorge P
María P

$ cut -c 1-5,9-10 nombres-ord.txt
Luis ió
AdriaGó
Jorgena
Maríare

$ cut -d ´ ´ -f 1 nombres-ord.txt
Luis
Adriana
Jorge
María

paste

Permite unir texto de varios ficheros, uniendo las líneas de cada uno de los ficheros

Formato:

paste [opciones] fichero1 [fichero2] ...

Algunas opciones:

  • -s pega los ficheros secuencialmente, en vez de intercalarlos
  • -d especifica los carácteres delimitadores en la salida (por defecto, TAB)

Ejemplos:

$ cat nombres.txt
Luis
Adriana
Jorge
María

$ cat apellidos.txt
Andión
Gómez
Pena
Pérez

$ paste nombres.txt apellidos.txt
Luis Andión
Adriana Gómez
Jorge Pena
María Pérez

$ paste -d ´ ´ nombres.txt apellidos.txt
Luis Andión
Adriana Gómez
Jorge Pena
María Pérez

$ paste -s -d ´\t\n´ nombres.txt
Luis Adriana
Jorge María

fmt

Formatea cada párrafo, uniendo o separando líneas para que todas tengan el mismo tamaño

Algunas opciones:

  • -n o -w n pone la anchura de las líneas a n (por defecto, 75)
  • -c conserva la indentación a principio de línea y alinea a la izquierda la segunda línea
  • -s las líneas pueden dividirse, no unirse
  • -u uniformiza el espaciado entre palabras

Ejemplo:

$ cat quijote.txt
En un lugar de la Mancha, de cuyo nombre no
quiero acordarme, no ha mucho tiempo
que vivía un
hidalgo de los de lanza en astillero, adarga
antigua, rocín flaco y galgo corredor.

$ fmt -w 45 -u quijote.txt
En un lugar de la Mancha, de cuyo nombre
no quiero acordarme, no ha mucho tiempo
que vivía un hidalgo de los de lanza en
astillero, adarga antigua, rocín flaco y
galgo corredor.

tr

Borra caracteres o reemplaza unos por otros

Formato:

tr [opciones] set1 set2

Algunas opciones:

  • -d borra los caracteres especificados en set1
  • -s reemplaza caracteres repetidos por un único carácter

Ejemplos:

$ tr ´a-z´ ´A-Z´ < quijote.txt
EN UN LUGAR DE LA MANCHA, DE CUYO NOMBRE...
$ tr -d ´ ´ < quijote.txt
EnunlugardelaMancha,decuyonombre...
$ tr au pk < quijote.txt
En kn lkgpr de lp Mpnchp, de ckyo nombre...
$ tr lcu o < quijote.txt | tr -s o
En on ogar de oa Manoha, de oyo nombre

uniq

Descarta todas (menos una) las líneas idénticas sucesivas en el fichero

Formato:

uniq [opciones] fichero

Algunas opciones:

  • -d muestra las líneas duplicadas (sin borrar)
  • -u muestra sólo las líneas sin duplicación
  • -i ignora mayúsculas/minúsculas al comparar
  • -c muestra el número de ocurrencias de cada línea
  • -s n no compara los n primeros caracteres
  • -f n no compara los n primeros campos
  • -t c usa el carácter c como separador de campos (por defecto, espacio o tabulado)

Ejemplo:

$ cat nombres.txt
Julio Lorenzo
Pedro Andión
Celia Fernández
Celia Fernández
Juan Fernández
Enrique Pena

$ uniq nombres.txt
Julio Lorenzo
Pedro Andión
Celia Fernández
Juan Fernández
Enrique Pena

$ uniq -f 1 -c nombres.txt
1 Julio Lorenzo
1 Pedro Andión
3 Celia Fernández
1 Enrique Pena

join

Permite combinar dos ficheros usando campos: busca en los ficheros por entradas comunes en el campo y une las líneas; los ficheros deben estar ordenados por el campo de unión

Formato:

join [opciones] fichero1 fichero2

Algunas opciones:

  • -i ignora mayúsculas/minúsculas
  • -1 FIELD une en el campo FIELD (entero positivo) de fichero1
  • -2 FIELD une en el campo FIELD de fichero2
  • -j FIELD equivalente a -1 FIELD -2 FIELD
  • -t CHAR usa el carácter CHAR como separador de campos
  • -o FMT formatea la salida (M.N fichero M campo N, 0 campo de unión)
  • -v N en vez de la salida normal, muestra las líneas que no se unen del fichero N
  • -a N además la salida normal, muestra las líneas que no se unen del fichero N

Ejemplo:

$ cat nombres1.txt
Luis Andión
Adriana Gómez
Jorge Pena
María Pérez

$ cat nombres2.txt
Pedro Andión
Celia Fernández
Julio Lorenzo
Enrique Pena

$ join -j 2 nombres1.txt nombres2.txt
Andión Luis Pedro
Pena Jorge Enrique

$ join -j 2 -o 1.1 2.1 0 nombres1.txt nombres2.txt
Luis Pedro Andión
Jorge Enrique Pena

split

Divide un fichero en ficheros más pequeños; los ficheros más pequeños se nombran a partir del prefijo especificado (prefijoaa, prefijoab,...)

Formato:

split [opciones] fichero prefijo
Si no se pone fichero, o se pone - se lee la entrada estándar

Algunas opciones:

  • -l n pone n lineas en cada fichero de salida (por defecto 1000)
  • -b n pone n bytes en cada fichero de salida
  • -C n pone en cada fichero de salida tantas líneas completas como sea posible sin sobrepasar n bytes
  • -d usa números en vez de letras para el nombre de los ficheros de salida

Ejemplo:

$ split -l 2 quijote.txt quij
$ ls quij*
quijaa quijab quijac quijote.txt
$ cat quijaa
En un lugar de la Mancha, de cuyo nombre
no quiero acordarme, no ha mucho tiempo

$ cat quijac
galgo corredor.
$ split -l 2 -d quijote.txt quij
$ ls quij*
quij00 quij01 quij02 ...

head

Muestra el principio de un fichero

Formato:

head [opciones] fichero

Algunas opciones:

  • -n N ó -N muestra las primeras N líneas
  • -c N muestra los primeros n bytes
  • -v le añade una línea de cabecera, con el nombre del fichero

Ejemplo:

$ head -n 2 -v quijote.txt
==>quijote.txt <==
En un lugar de la Mancha, de cuyo nombre
no quiero acordarme, no ha mucho tiempo

tail

Muestra el final de un fichero

Algunas opciones:

  • -n N ó -N muestra las últimas N líneas (por defecto, 10)
  • +N muestra de la línea N al final
  • -c N muestra los últimos N bytes
  • -f hace que tail corra en un lazo, añadiendo líneas a medida que el fichero crece (útil para cuando queremos ver como se modifica un fichero)
  • --retry útil con -f; aunque el fichero no exista o sea inaccesible continua intentando hasta que puede abrirlo
  • -v le añade una línea de cabecera, con el nombre del fichero

Ejemplo:

$ tail -n 2 -v quijote.txt
==>quijote.txt <==
astillero, adarga antigua, rocín flaco y
galgo corredor.

tac, rev

tac imprime el fichero de la última a la primera línea (opuesto a cat); rev invierte las lineas del fichero

Ejemplos:

$ tac quijote.txt
galgo corredor.
astillero, adarga antigua, rocín flaco y
que vivía un hidalgo de los de lanza en
no quiero acordarme, no ha mucho tiempo
En un lugar de la Mancha, de cuyo nombre

$ rev quijote.txt
erbmon oyuc ed ,ahcnaM al ed ragul nu nE
opmeit ohcum ah on ,emradroca oreiuq on
ne aznal ed sol ed ogladih nu aíviv euq
y ocalf nícor ,augitna agrada ,orellitsa
.roderroc oglag

wc

Muestra el número de líneas, palabras y bytes de un fichero

Formato:

wc [opciones] fichero

Algunas opciones:

  • -l muestra sólo el número de lineas
  • -w muestra sólo el número de palabras
  • -c muestra sólo el número de bytes
  • -L muestra la longitud de la línea más larga

Ejemplo:

$ wc quijote.txt
5 33 178 quijote.txt
$ wc -l quijote.txt
5 quijote.txt
$ wc -w quijote.txt
33 quijote.txt
$ wc -c quijote.txt
178 quijote.txt

nl

Añade números de línea; nl considera los ficheros separados en páginas lógicas, cada una de ellas con una cabecera, cuerpo y pie, cada una de estas secciones se numera de forma independiente, y la numeración se reinicia para cada página; los comienzos de cabecera, cuerpo y pie de cada página se marcan, respectivamente, con \:\:\:, \:\: y \:

Formato:

nl [opciones] fichero

Algunas opciones:

  • -b, -h o -f ESTILO indica el estilo de numeración para cuerpo, cabecera o pie, que puede ser:
    • a: numera todas las líneas
    • t: numerar sólo las líneas no vacías (por defecto para el cuerpo)
    • p REGEXP: numera sólo las líneas que concuerdan con REGEXP
    • n: no numera ninguna línea (por defecto para cabecera y pie)
  • -v n inicia la numeración en n (por defecto, 1)
  • -i n incrementa los números por n (por defecto, 1)
  • -p no reinicia la numeración al principio de cada página
  • -s STRING una STRING para separar los números de línea del texto (por defecto ´ ´)

Ejemplo:

$ nl -s ´q ´ quijote.txt
1q En un lugar de la Mancha, de cuyo nombre
2q no quiero acordarme, no ha mucho tiempo
3q que vivía un hidalgo de los de lanza en
4q astillero, adarga antigua, rocín flaco y
5q galgo corredor.

expand

Convierte TABs en espacios; útil debido a que la representación del TAB puede ser diferente en distintos sistemas

Formato:

expand [opciones] fichero ...

Algunas opciones:

  • -t n reemplaza cada TAB por n espacios (por defecto, 8)
  • -i solo reemplaza los TABs de principio de línea

Ejemplos:

$ cat hola.c
main() {
for(i=0; i<10;i++)
printf("Hola mundo!\n");
}
$ expand -t 2 hola.c
main() {
for(i=0; i<10;i++)
printf("Hola mundo!\n");
}
El comando unexpand hace la operación contraria

od

Muestra un fichero en octal, hexadecimal o otros formatos; en cada línea muestra (en la primera columna) el offset

Formato:

od [opciones] fichero

Algunas opciones:

  • -t TIPO especifica el formato de la salida (por defecto octal): o para octal, x para hexadecimal, d para decimal, c para caracteres ASCII, a para caracteres con nombre...
  • -A TIPO especifica el formato del offset (por defecto octal): o, x, d como antes, n para que no aparezca
  • -w BYTES número de bytes por linea (por defecto 16)

Ejemplo:

$ od -t x -A x quijote.txt
000000 75206e45 756c206e 20726167 6c206564
000010 614d2061 6168636e 6564202c 79756320
000020 6f6e206f 6572626d 206f6e0a 65697571
...

Expresiones regulares extendidas

Los sistemas UNIX actuales admiten extensiones a las expresiones regulares básicas:
  • debemos usar egrep, grep -E, sed -r
ER concuerda con
+ una o más ocurrencias de la RE anterior
? cero o una ocurrencia de la RE anterior
Además, \( \) y \{ \} se reemplazan por ( ) y { }
  • Ejemplos:
    • ab+c concuerda con abc, abbc, pero no con ac
    • ab?c concuerda con ac, abc, pero no con abbc
  • Para usar los caracteres (, ), { o } escaparlos con \

Alternancia

El carácter | permite alternar entre 2 o más RE
  • (a|b)c concuerda con ac o bc

Etiquetado

Las RE que se ponen entre ( ) quedan etiquetadas, y podemos hacer referencia a esos elementos mediante \n, con n el número de la etiqueta
  • Ejemplos:
    • (.)oo\1 concuerda con moom, noon, pero no con moon
    • (.)oo\1-(.)aa\1\2 concuerda con moom-paamp

Otros caracteres

Además de los ya vistos, pueden usarse otros metacaracteres:
ER concuerda con
\n, \r, \t LF, CR y tab (no siempre funcionan)
[:space:] caracteres en blanco ([ \t\n\r\f\v])
[:blank:] espacio y tabulado
[:alnum:] o \w caracteres alfánuméricos (letras y números)
[:digit:] dígitos
[:alpha:] alfabéticos
[:upper:] mayúsculas
[:lower:] minúsculas
[:xdigit:] dígitos hexadecimales
[:punct:] signos de puntuación
[:cntrl:] caracteres de control
[:graph:] caracteres imprimibles (sin espacio)
[:print:] caracteres imprimibles (con espacio)
\<, \> inicio/fin de palabra
\b posición entre palabras
\B posición en medio de una palabra
  • [[:upper:]]bc concuerda con Abc, pero no abc
  • \babc\b concuerda con ab abc df, pero no con abcdef
  • \Babc\B concuerda con ababcdf, pero no con ab abc df

Más ejemplos

  1. \w+@\w+\.\w+((\.\w+)*)? concuerda con direcciones de e-mail
  2. (0[1-9]|[12][0-9]|3[01])-(0[1-9]|1[012])-(19|20)[0-9]{2} concuerda con fechas en el formato dd-mm-yyyy (años entre el 1900 y 2099)
  3. [-+]?([0-9]*\.)?[0-9]+([eE][-+]?[0-9]+)? concuerda con números en punto flotante (con o sin exponente)

Ejemplos de uso con sed:

$ echo "abc1234def" | sed -r "s/[0-9]+/NUMERO/"
abcNUMEROdef
$ echo "abc1234def" | sed -r ´s/[0-9]+/<&>/´
abc<1234>def
# En el siguiente ejemplo, notar que las ER intentan siempre reconocer la secuencia más larga posible
$ echo "000x111x222x333" | sed ´s/x.*x/<&>/´
000333
# Eliminar blancos a principio y al final de línea y sustituir más de un blanco seguido por uno solo
$ sed -r "s/^_+// ; s/_+$// ; s/__+/_/g" fich
# Pon los 4 primeros caracteres de cada línea al final de la misma
$ sed -r ´s/^(.{4,4})(.*)/\2\1/´ fich
# Cambia de minúsculas a mayúsculas la primera letra de cada palabra
$ sed -r ´s/\<./\u&/g´
# Convierte DOS newlines (CR/LF) a formato Unix (LF)
$ sed ´s/^M$//´4
# también funcionaría
$ sed ´s/\r//´

Expresiones regulares básicas

UNIX admite dos tipos de expresiones regulares: básicas y extendidas
  • las básicas son las clásicas de UNIX, aunque se consideran obsoletas en POSIX
  • aplicaciones como grep o sed las usan por defecto
  • para usar las extendidas:
    • grep --> egrep o grep -E
    • sed --> sed -r
  • las expresiones extendidas proporcionan más potencia
La mayoría de los caracteres son tratados como literales:
  • concuerdan (match) consigo mismos:
    • a concuerda con a, ab con ab, etc.
  • la excepción son los metacaracteres:
    . [ ] ^ $ * ( ) \

ER de un sólo carácter

ER concuerda con
. cualquier carácter
[ ] cualquiera de los caracteres entre corchetes, p.e. [abc] concuerda con a, b o c; [a-z] concuerda con cualquier letra minúscula
[^ ] cualquier carácter que no esté entre corchetes
^ principio de línea
$ final de línea
* 0 o más ocurrencias de la expresión regular anterior
\( \) permite agrupar ER
\ escapa un metacarácter
  • Dentro de [ ] los metacaracteres pierden su significado especial: p.e. [a.]c concuerda con ac y .c
  • Para incluir un carácter ] en una lista colocarlo al principio; para incluir un ^ en cualquier lugar menos al principio; para incluir un - al final: p.e. [a^]c concuerda con ac y ^c

Ejemplos:

ER concuerda con
a..c cadena que empiece por a, seguida por dos caracteres y c: a00c, xaxxcxx, aacc,...
0[abc]0 cadenas que tengan un 0 seguido de un carácter a, b, o c y seguido de otro 0: 0a0, 00ab0b0, bc0c0,...
0[^abc]0 cadenas que tengan un 0 seguido de un carácter distinto a a, b, o c y seguido de otro 0
0[a-z]0 cadenas que tengan un 0 seguido de una letra minúscula, y 0
^abc líneas que empiecen por abc
abc$ líneas que terminen por abc
ab*c cadenas que empiecen por a, que continúen con 0 o más b, y una c: abc, ac, abbc, aaccab,...pero no cba o aaab
b[cq]*e cadenas que empiecen por b, que continúen con 0 o más c o q, y una e: be, bcce, bccqqee o bqqqce
.* cualquier cadena
abc.* cualquier cadena que empiece por abc
0\(abc\)*0 cadenas que tengan un 0 seguido de 0 o más ocurrencias de abc, y seguido de otro 0: 0abc0, 00, 0abcabc0,..., pero no 0ac0 o 0cba0
^#.*\.$ línea que empiece por # y termine por . (notar que el segundo . está escapado por la \; la ER .* implica 0 o más caracteres cualquiera)

Repetición

Podemos repetir una regexp usando \{ \}
Constructor Propósito
\{n\} concuerda con exactamente n ocurrencias de la RE previa
\{n,\} concuerda con al menos n ocurrencias de la RE previa
\{n, m\} concuerda con entre n y m ocurrencias de la RE previa
Ejemplos:
  • a\{5\}: 5 ocurrencias del carácter a
  • .\{5,\}: al menos 5 ocurrencias de cualquier carácter

Comandos grep y sed

grep y sed son dos comandos que usan REGEXP

grep

Busca en ficheros por un patrón determinado
grep [opciones] patrón [fichero...]
Opciones:
  • -E o egrep: usa expresiones regulares extendidas
  • -F o fgrep: interpreta los patrones no como expresiones regulares sino como cadenas de caracteres fijas
  • -R o rgrep: lee todos los ficheros bajo cada directorio, recursivamente
  • -i o --ignore-case: busca ignorando diferencias entre mayúsculas y minúsculas
  • -w o --word-regexp: para forzar que la cadena reconocida sea una palabra completa
  • -l o --files-with-matches: no muestra el contenido de la linea encontrada pero si que muestra el fichero que contiene la cadena buscada
  • -n o --line-number: muestra el número de línea dentro del fichero
  • -v o --invert-match: en lugar de sacar la lineas que cumplen la búsqueda sacará las que no cumplen
Si no especificamos fichero, grep usa la entrada estándar:
  • podemos usarlo para probar las expresiones regulares:
    $ egrep ´[Uu]nix´
    unix
    unix
    Unix
    Unix
    Linux

sed (stream editor)

Editor de flujo; permite realizar transformaciones básicas de un flujo de entrada (un fichero o una entrada desde una tubería)

Formato (para substituciones):

sed [opciones] s/REGEXP/reemplazo/flag [fichero]
Algunos comandos:
  • s substitución
  • d borrado
  • i\, a\, añade antes/después de la línea afectada
  • c\ reemplaza la línea afectada
Algunas ociones:
  • -e comando: añade comando
  • -i edita el fichero in-place
  • -n suprime la salida
Algunos flags:
  • g: aplica los cambios globalmente (por defecto, sólo se cambia la primera aparición en cada línea)
  • p imprime las líneas afectadas, incluso con la opción -n.
  • NUMERO: reemplaza la aparición número NUMERO
  • w fichero: escribe las líneas con sustituciones al fichero indicado
Ejemplo: cambia, en el fichero amigos, todas las apariciones de pepe y paco por Pepe y Paco, respectivamente:
$ sed -e ´s/pepe/Pepe/g´ -e ´s/paco/Paco/g´ amigos (también sed ´s/pepe/Pepe/g ; s/paco/Paco/g´ amigos)
Ejemplo: cambia pepe por Pepe, pero sólo en las líneas que tengan Potamo
$ sed ´/Potamo/s/pepe/Pepe/g´ amigos
Ejemplo: muestra sólo las lineas que contengan jaime
$ sed -n ´/jaime/p´ amigos
Ejemplo: borra las lineas que contengan jaime
$ sed ´/jaime/d´ amigos
Ejemplo: cambia las lineas que contengan jaime por otra cosa
$ sed ´/jaime/c\BORRADO´ amigos
Ejemplo: inserta una línea antes de las líneas que contengan jaime por otra cosa
$ sed ´/jaime/i\APARICION´ amigos
Ejemplo: reemplaza, en cada línea de fichero, la quinta ocurrencia de stop por STOP
$ sed ´s/stop/STOP/5´ fichero
Ejemplo: igual que antes pero guarda cada línea reemplazada en el fichero f2
$ sed ´s/stop/STOP/5w f2´ fichero

Indicación de líneas:

podemos especificar las líneas del fichero en las que queremos que se realicen las operaciones:
sed ´3s/stop/STOP/g´ (reemplaza sólo en la línea 3)
sed ´3,10s/stop/STOP/g´ (reemplaza de la línea 3 a la 10)
sed ´3,$s/stop/STOP/g´ (reemplaza de la línea 3 al final)
sed ´!3s/stop/STOP/g´ (reemplaza en todas las líneas menos la 3)

Operador &:

se sustituye por el patrón reconocido

Ejemplo: reemplaza stop por

$ sed ´3s/stop/<&>/g´ fichero

Comandos desde fichero:

la opción -f permite leer comandos de sed agrupados en un fichero

Ejemplo: reemplazo desde la línea 1 hasta una línea que comience por END (o el final, si no hay ninguna)

$ cat file.sed
1,/^END/{
s/[Ll]inux/GNU\/Linux/g
s/samba/Samba/g
}

$ sed -f file.sed fichero

lunes, 26 de enero de 2009

Servicio de directorio: LDAP

Servicio de directorio: LDAP

LDAP: Protocolo Ligero de Acceso a Directorios (Lightweight Directory Access Protocol)

OpenLDAP



Modelo de datos de LDAP

Un directorio es una base de dato optimizada para lectura, navegación y búsqueda
  • la información se almacena de manera jerárquica
  • generalmente no se soportan transacciones complejas ni sistemas de recuperación
  • las actualizaciones son cambios simples
  • proporcionan respuestas rápidas a grandes volúmenes de búsquedas
  • el directorio puede estar replicado y/o distribuido entre varios sistemas (p.e. DNS)
LDAP organiza el directorio como una estructura jerárquica de entradas (nodos) en forma de árbol
  • Cada entrada posee un conjunto de atributos, que pueden ser de diferentes tipos
    • cada atributo se identifica por su tipo y uno o más valores
    • los tipos son normalmente palabras nemotécnicas, como uid (identificador de usuario), cn (common name), c (country), dc (domain component), etc.
    • los diferentes atributos de un nodo están determinados por la clase a la que pertenece
    • las clases permiten definir entradas con diferente información: clases para personas, para equipos, administrativas, etc.
    • las clases se definen mediante ficheros de esquema (schema)
  • Cada nodo debe poseer un nombre único: nombre distinguido o dn (distinguished name)
    • el dn identifica de forma unívoca cada objeto en la base de datos
Ejemplo: árbol de usuarios y grupos en LDAP, basado en nombres de dominios de Internet:
Image LDAParbol
  • cada nodo puede tener varios atributos, p.e. el nodo pepe podría tener los siguientes atributos:
    dn: cn=Jose Pena,ou=People,dc=midominio,dc=com
    objectClass: account
    uid: pepe
    sn: Pena
    description: alumno
    mail: pepe@midominio.com
  • el formato en el que se muestran los atributos del objeto se denomina LDIF (LDAP Data Interchange Format)
    • formato de intercambio de datos para importar y exportar datos a un servidor LDAP



Instalación de un servidor LDAP

Describiremos como montar un servidor LDAP simple que nos permita la gestión de usuarios y grupos
  • el servidor mantendrá la lista de usuarios y grupos del dominio
  • en los clientes la autenticación de usuarios y los permisos se basará en el servidor LDAP

Pasos para la instalación del servidor en Debian

  1. Instalar los siguientes paquetes:
    1. slapd: servidor OpenLDAP
    2. ldap-utils: utilidades del paquete OpenLDAP
      • incluye comandos como ldapsearch, ldapadd
    • El comando slapcat permite ver la estructura creada
    • Podemos hacer búsquedas sencillas con ldapsearch
    # ldapsearch -x -b dc=midominio,dc=com ´(cn=admin)´
  2. Ficheros de configuración:
    1. Fichero /etc/ldap/ldap.conf
      • Fichero de configuración global para los clientes LDAP
      • Permite especificar la base por defecto, el servidor LDAP, etc. (cambiarlo para poner nuestra configuración, comprobar que tiene permisos 644)
      • Para más info, ver man ldap.conf
    2. Fichero /etc/ldap/sldap.conf
      • Fichero de configuración del demonio slapd
      • En principio, no es necesario cambiarlo (comprobar que tiene permisos 600)
  3. Crear la estructura de la base de datos: crearemos los nodos de People y Group del árbol LDAP
    1. Crear el siguiente fichero estructura.ldif en formato LDIF:
          dn: ou=People,dc=midominio,dc=com
      objectClass: top
      objectClass: organizationalUnit
      ou: People

      dn: ou=Group,dc=midominio,dc=com
      objectClass: top
      objectClass: organizationalUnit
      ou: Group
    2. Añadir los nodos a la base de datos:
      # ldapadd -x -D 'cn=admin,dc=midominio,dc=com' -W -f estructura.ldif
      • -x autenticación simple sin SASL (Simple Authentication and Security Layer)
      • -D nombre distinguido con el que nos conectamos a LDAP (ponemos el del administrador)
      • -W pide la contraseña de forma interaciva
      • -f fichero a cargar
  4. Añadir un usuario y un grupo a la base de datos
    1. Crear un fichero como este, que tiene la información para un usuario y un grupo (¡no useis acentos!):
          dn: cn=Jose Pena,ou=People,dc=midominio,dc=com
      objectClass: top
      objectClass: account
      objectClass: posixAccount
      objectClass: shadowAccount
      uid: pepe
      cn: Jose Pena
      uidNumber: 2000
      gidNumber: 2000
      homeDirectory: /home/pepe
      loginShell: /bin/bash
      gecos: Jose Pena, Despacho 22,,

      dn: cn=pepe,ou=Group,dc=midominio,dc=com
      objectClass: top
      objectClass: posixGroup
      cn: pepe
      gidNumber: 2000
      • define un usuario pepe y un grupo pepe con información similar a la aparece en el fichero /etc/passwd
      • puede añadirse más información, como la que aparece en el fichero /etc/shadow: campos shadowMax, shadowExpire, shadowWarning, etc.
    2. Añadir el fichero con:
      # ldapadd -x -D ´cn=admin,dc=midominio,dc=com´ -W -f user-group.ldif
    3. Probarlo haciendo búsquedas, tipo:
      # ldapsearch -x uid=pepe
    4. Añadir una contraseña al usuario: puede hacerse directamente metiendo un campo userPassword en el fichero anterior, pero es preferible hacerlo mediante el comando ldappasswd
    # ldappasswd -x ´cn=Jose Pena,ou=People,dc=midominio,dc=com´ -D ´cn=admin,dc=midominio,dc=com´ -W -S
  5. Si queremos migrar la información de los ficheros /etc o NIS a LDAP podemos usar el paquete MigrationTools (ver www.padl.com/OSS/MigrationTools.html)
  6. Por último, también podemos querer instalar el servidor como cliente, por lo que debemos seguir los pasos indicados en la sección de instalación de un cliente

Fichero de configuración slapd.conf

Fichero de configuración del demonio slapd
Tiene dos partes:
  1. Directivas globales: distintos tipos de opciones del servidor, entre ellas los esquemas que el directorio debe conocer:
    • nis.schema define los atributos de objetos para las cuentas de usuario, grupos, etc.
    • se basa en los esquemas básicos core.schema y cosine.schema
    • inetorgperson.schema añade objetos con más información personal para los usuarios
  2. Directivas específicas para las distintas bases de datos
    • Incluye el sufijo, directorio donde guardar la base, y el tipo de database a usar (por defecto bdb, Berkeley Database)
    • También un conjunto de reglas de acceso de la forma:
      access to [by ]+
    • es una expresión que especifica a qué datos del directorio se aplica la regla. Tres opciones:
      1. puede indicarse todo el directorio mediante un asterisco (*)
      2. un subconjunto de entradas cuyo nombre distinguido contiene un cierto sufijo (por ejemplo, dn=".*,dc=midominio,dc=com")
      3. un atributo concreto de dichos objetos (por ejemplo, dn=".*,ou=People,dc=midominio,dc=com" attr=userPassword)
    • indica a quién (a qué usuario(s)) se especifica la regla; puede tomar diferentes valores, siendo los más comunes los siguientes:
      • self el propietario de la entrada
      • dn="..." el usuario representado por el nombre distinguido
      • users cualquier usuario acreditado
      • anonymous cualquier usuarios no acreditado
      • * cualquier usuario
    • indica qué operación concede la regla:
      • none sin acceso
      • auth utilizar la entrada para validarse
      • compare comparar
      • search búsqueda
      • read lectura
      • write modificación
    • Por ejemplo, la regla por defecto para userPassword permite que los usuarios puedan autenticarse y cambiar sus propias contraseñas y el administrador las de todos

Instalación de un cliente LDAP

Describiremos como como configurar un cliente para que acceda a la información almacenada en el directorio de LDAP del servidor
  • Dos pasos:
    1. Configurar el Name Service Switch (fichero /etc/nsswitch.conf)
    2. Configurar el módulo de autenticación (PAM, Pluggable Authentication Modules)
  • Los módulos necesarios para esta configuración pueden descargarse de la página de PADL (www.padl.com) o directamente como paquetes Debian
  • En algunas distros (RedHat) existe la herramienta authconfig que facilita esta configuración

Configurar el Name Service Switch

El NSS se encarga, entre otras, de realizar la correspondencia entre los números y nombres de usuario
  • permite gestionar los permisos de acceso de usuarios a ficheros
  • se configura a través del fichero /etc/nsswitch.conf
  • la configuración de ese fichero la vimos en el tema 5
Pasos (hacerlos en un sistema diferente del servidor):
  1. Instalar el paquete libnss-ldap
    • En la configuración indicar la IP del servidor LDAP y el DN de la base del directorio LDAP
      • esa configuración se guarda en el fichero /etc/libnss-ldap.conf
  2. Modificar las líneas de passwd, group y shadow del fichero nsswitch.conf
    passwd: files ldap
    group: files ldap
    shadow: files ldap
    • esto indica al NSS que busque la información primero en los ficheros y, si no la encuentra, en el servidor LDAP
  3. Probar que funciona:
    1. Hacer un finger al usuario definido en LDAP
        # finger pepe
      Login: pepe Name: Jose Pena
      Directory: /home/pepe Shell: /bin/bash
      ...
    2. Crear el directorio /home/pepe y cambiarle el propietario a pepe
    3. Hacer un su - pepe para ver que podemos cambiar al usuario pepe en el cliente

Configurar el módulo de autenticación

Aunque el usuario es válido en el cliente, todavía no podemos autenticarnos (entrar en la cuenta)
  • Debemos configurar el servicio PAM
PAM (Pluggable Authentication Module)
biblioteca de autentificación genérica que cualquier aplicación puede utilizar para validar usuarios, utilizando por debajo múltiples esquemas de autentificación alternativos (ficheros locales, Kerberos, LDAP, etc.)
  • permite añadir nuevos mecanismos de autentificación (Kerberos, LDAP,...) sin tener que modificar los servicios de entrada al sistema como login, ftp, ssh, etc.
  • PAM utiliza módulos que proporcionan autentificación en los servicios de entrada al sistema:
    • Módulos de autentificación (auth): comprobación de contraseñas (utilizado por el proceso de login para averiguar si las credenciales tecleadas por el usuario (nombre y contraseña) son correctas)
    • Módulos de cuentas (account): controlan que la autenticación sea permitida (que la cuenta no haya caducado, que el usuario tenga permiso de iniciar sesiones a esa hora del día, etc.)
    • Módulos de contraseña (password): permiten cambiar contraseñas
    • Módulos de sesión (session): configuran y administran sesiones de usuarios (tareas adicionales que son necesitadas para permitir acceso, como el montaje de directorios, actualización del fichero lastlog, etc.)
  • Las librerías de PAM están en /lib/security y los ficheros de configuración en /etc/pam.d/ (en versiones viejas, se configuraba a través del fichero /etc/pam.conf)
  • Existe un fichero de configuración para cada servicio que usa PAM
  • También existen ficheros comunes que son incluidos por los ficheros de configuración: common-auth, common-account, common-password y common-session

Configuración de PAM para usar LDAP

en el cliente seguimos los siguientes pasos:
  1. Instalar el paquete libpam-ldap
    • Cambiar el Root login account al DN del administrador LDAP
    • El resto de opciones dejarlas por defecto
    • La configuración es en el fichero /etc/pam_ldap.conf
    • Para más información, ver la página de manual de pam_ldap.conf
    NOTA sobre la configuración: en el último paso de la instalación nos pide la clave del administrador de LDAP
    • esta clave se guarda en plano en el fichero /etc/pam_ldap.secret, y se usa para que el root del sistema pueda modificar directamente las contraseñas de los usuarios
    • si no se quiere tener ese fichero con la clave, dejar vacío el campo clave y en el fichero /etc/pam_ldap.conf comentar la línea que empieza por rootbinddn
  2. Cambiar los ficheros common-auth, common-account y common-password, añadiendo la siguiente línea:
    sufficient pam_ldap.so
    cambiando por auth, account o password según corresponda
  3. En el fichero common-auth añadir use_first_pass después de pam_unix.so
    • esto evita que a los usuarios no-LADP se les pida la contraseña dos veces
  4. Podemos crear directorios home ``al vuelo'': añadir lo siguiente al principio de common-session
    session    required    pam_mkhomedir.so skel=/etc/skel/ umask=0022
    • al entrar el usuario en la cuenta, si no existe su HOME, se crea
  5. Probar que funciona:
    1. Entrar en la cuenta como un usuario LDAP
    2. Cambiar la contraseña del usuario

Instalación del paquete nscd

una vez que todo funciona, en el cliente podemos instalar el paquete nscd
  • Name Service Cache Daemon
  • Hace caché para los nombres leidos del servidor LDAP