Tema 3: Linux: Shell de Linux, Programación con Bash

Página creada Sancho Vasquez
 
SEGUIR LEYENDO
Tema 3: Linux: Shell de Linux, Programación con Bash

 Objetivos:
 - Entender el uso de scripts in sistemas Linux.
 - Aprender comandos y sintax de Bash shell
 - Formar programas con lenguaje de programación de Bash

Fundamentos de los shells de Linux

    ƒ   El intérprete de línea de comandos de Linux se llama el shell
    ƒ   Capa de insulación entre sistema operativo y el usuario
    ƒ   Lenguaje poderoso; programas se refiere con nombre script
    ƒ   Pegar comandos juntos
    ƒ   Conocimiento de shell scripting es fundamental para sistema operativo

El Lenguaje con los siguientes construcciones: (1) construciones de bucles, (2)
construcciones de condicionale, (3) funciones de shell, (4) funciones built-in

¿Porque Scripts?
   ƒ Script importante para administraciópn y configuración
   ƒ Por Ejemplo: Linux arranque: ejecuta el shell scripts en el directorio de
   ƒ /etc/rc.d para restorar el configuración de sistema y
   ƒ iniciar los servicios.
   ƒ sintaxis es como pegando comandos junto.
   ƒ un manera rápido para hacer prototipos.

Cuando no debe usar scripting
   • tareas intensivas de los recursos, velocidad es importante
   • aplicaciones complejos - programación estructurad es importante
   • tratamiento de ficheros; E/S intensivo
   • necesidad de generar o tratar con gráficos o GUIs
   • necesidad de tener aceso directo al hardware de sistema
   • necesidad de utilizar E/S de sockets
   • necesidad de utilizar librarias

Ejemplos de Scripts

En el caso más sencillo, el script es solo una lista de comandos guardado en un fichero.
Por ejemplo, limpiar ficheros en /var/log

  # limpiar
  # Ejecuta como root
  cd /var/log
  cat /dev/null > messages
  cat /dev/null > wtmp
  echo "Log limpia."

Un ejemplo de un script más complicado es la siguiente. (Tenemos que estudiar distintos
elementos de este script)
#!/bin/bash
  # cleanup, version 2
  # Ejecutar como root

  LOG_DIR=/var/log
  ROOT_UID=0     #   Only users with $UID 0 have root privileges.
  LINES=50       #   Default number of lines saved.
  E_XCD=66       #   Can't change directory?
  E_NOTROOT=67   #   Non-root exit error.

  if [ "$UID" -ne "$ROOT_UID" ]
  then
     echo "Must be root to run this script."
     exit $E_NOTROOT
  fi

  if [ -n "$1" ]
  # Test if command line argument present (non-empty).
  then
     lines=$1
  else
     lines=$LINES # Default, if not specified on command line.
  fi

  cd $LOG_DIR

  if [ `pwd` != "$LOG_DIR" ]     # or   if [ "$PWD" != "$LOG_DIR" ]
                                 # Not in /var/log?
  then
    echo "Can't change to $LOG_DIR."
    exit $E_XCD
  fi # Doublecheck if in right directory, before messing with log file.

  tail -$lines messages > mesg.temp # Saves last section of message log file.
  mv mesg.temp messages             # Becomes new log directory.

  cat /dev/null > wtmp #      ': > wtmp' and '> wtmp'    have the same effect.
  echo "Logs cleaned up."

  exit 0

Un valor cero desde el script cuando exit indica exito. el simbolo #! en primer linea indica que
el fichero es listo para un conjunto de comandos. En realidad es un número magico de dos
bytes que indica el tipo de fichero.

Otros ejemplos que llama interpredores son:

  #!/bin/sh
  #!/bin/bash
  #!/usr/bin/perl
  #!/usr/bin/tcl
  #!/bin/sed -f
  #!/usr/awk -f

Llamando el script

 sh < scriptname >

  chmod 555 scriptname (gives everyone read/execute permission)
  chmod +rx scriptname (gives everyone read/execute permission)
  chmod u+rx scriptname (gives only the script owner read/execute      permission)
2.2 Operaciones Basicas

Dentro de operaciones básicas que estamos interesados: caracteres especiales, variables y
parametros, quoting, estatus de salir. En este apartado, investigamos estas posibilidades.

Caracteres Especiales

\# Comentarios
; Separador de Comandos. Permite dos o más comandos, echo hola; echo buenos
;; Terminador de sentencia case [Double semicolon]

Considera la siguiente ejemplo:

        case "$variable" in
                abc) echo "$variable = abc" ;;
                xyz) echo "$variable = xyz" ;;
             esac

En esta ejemplo, utilizamos full quoting. Si tenemos 'STRING' preserva todos los caracteres
especiales dentro de STRING. El operador coma: El operador coma enlace operaciones
aritmeticas; para que todos estan evaluadas, pero solo se devuelve el último

       let "t2 = ((a = 9, 15 / 3))"   # Set "a" and calculate "t2".

Expansion de variables y Sustitución de subcadenas.

La combinación :> (: con el operador de redirección >), se puede poner el tamaño de fichero a
cero, sin cambiar los permisos. Si el fichero no existe, lo crea.

.
       : > data.xxx    # Fichero "data.xxx" ahora vacio

Tiene la misma efecto que cat /dev/null >data.xxx Pero no va a fork un procesos nuevo,
debido que ":" es una función builtin. La caractera ":" tambien sirve como separador de campo
en /etc/passwd, y en la definición del variable de entorno $PATH.

    bash$ echo $PATH
    /usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/sbin:/usr/sbin:/usr/games

Mas Caracteres especiales:
   • ? es el operador de prueba; dentro de ciertos expresiones, la caractera ? indica una
      prueba para una condición.
   • $ sustitución de variable
   • [] prueba; la expresión de prueba va dentro de las claves [ ].

    var1=5
    var2=23skidoo

    echo $var1      # 5
    echo $var2      # 23skidoo
Manipulando y/o expandiendo variables

${parametro} Es lo mismo que $parametro, eg. , el valor del parametro de variable. Se puede
utilizarlo para concatenar variables con cadenas. En bash las cadenas y expresiones
numericos se trata en una manera similar.

 your_id=${USER}-on-${HOSTNAME}
 echo "$your_id"
 #
 echo "Old \$PATH = $PATH"
 PATH=${PATH}:/opt/bin #Add /opt/bin to $PATH for duration of script.
 echo "New \$PATH = $PATH"

Sentencias de control: until

 until TEST-COMMANDS; do CONSEQUENT-COMMANDS; done

Ejecutar CONSEQUENT-COMMANDS hasta el comando final
   TEST-COMMANDS tiene un estatus de salida que no es cero.

     #!/bin/bash
         COUNTER=20
         until [ $COUNTER -lt 10 ]; do
              echo COUNTER $COUNTER
              let COUNTER-=1
         done

Sentencias de control: while

 while TEST-COMMANDS; do CONSEQUENT-COMMANDS; done

Ejecutar CONSEQUENT-COMMANDS hasta el comando final
TEST-COMMANDS tiene un estatus de salida que es cero.

 #!/bin/bash
    COUNTER=0
    while [ $COUNTER -lt 10 ]; do
         echo The counter is $COUNTER
         let COUNTER=COUNTER+1
    done

Sentencia de control: for

 for NAME [in WORDS ...]; do COMMANDS; done

Ejecutar COMMANDS para cada miembro en WORDS, con NAME enlazada al miembro actual

 for i in vacas cabritos cerdos pollos
 do
      echo ``$i es un animal de granja''
 done
 echo -e pero \n GNUs no son ''
Sentencias Condicional: if

 if TEST-COMMANDS; then
        CONSEQUENT-COMMANDS;
    [elif MORE-TEST-COMMANDS; then
        MORE-CONSEQUENTS;]
     [else ALTERNATE-CONSEQUENTS;]
    fi

Un ejemplo de un script utilizando la sentencia if:

 #!/bin/sh
 X=10
 Y=5
 if test ``$X'' -gt ``$Y'' ; then
      echo ``$X is greater than $Y''
 elif test ``$X'' -lt ``$Y'' ; then
      echo ``$X is less than $Y''
 else
      echo ``$X is equal to $Y''
 fi

Ejemplo utilizando test

 #!/bin/sh
 if test ``$1'' = ``'' ; then
         echo ``Usage: backup-lots.sh ''
         exit
 fi
 for i in 0 1 2 3 4 5 6 7 8 9 ; do
      NEW_FILE=$1.BAK-$i
      if test -e $NEW_FILE ; then
           echo ``backup-lots.sh : warning $NEW_FILE''
           echo ``                already exists''
      else
           cp $1 $NEW_FILE
      fi
 done

Sentencias Condicional: case

 `case WORD in [PATTERN [| PATTERN]...) COMMANDS ;;]... esac'

Ejecutar selectivamente los COMMANDS basada sobre WORD que match PATTERN.
La barra $|$ esta utilizado para separar patrones multiples.

 echo -n "Entra el nombre del animal: "
      read ANIMAL
      echo -n "El \$ANIMAL tiene "
      case \$ANIMAL in
         caballo | perro | gato) echo -n "four";;
         hombre | kangaroo ) echo -n "two";;
         *) echo -n "un numero no conocido de";;
      esac
     echo "piernes."
Funciones de Shell

 [`function'] NAME () { COMMAND-LIST; }

                                #!/bin/sh
                                if test ``$1'' = ``'' ; then
   #!/bin/bash                          echo ``Usage: backup-lots.sh ''
        function quit {                 exit
           exit                 fi
                }               for i in 0 1 2 3 4 5 6 7 8 9 ; do
         function hola {             NEW_FILE=$1.BAK-$i
           echo Hola!                if test -e $NEW_FILE ; then
         }                                echo ``backup-lots.sh : warning $NEW_FILE''
         hola                             echo ``                already exists''
         quit                        else
         echo foo                         cp $1 $NEW_FILE
                                     fi
                                done

Sentencias Condicional: case

 `case WORD in [PATTERN [| PATTERN]...) COMMANDS ;;]... esac'

Ejecutar selectivamente los COMMANDS basada sobre WORD que match PATTERN.
La barra $|$ esta utilizado para separar patrones multiples.

 echo -n "Entra el nombre del animal: "
      read ANIMAL
      echo -n "El \$ANIMAL tiene "
      case \$ANIMAL in
         caballo | perro | gato) echo -n "four";;
         hombre | kangaroo ) echo -n "two";;
         *) echo -n "un numero no conocido de";;
      esac
     echo "piernes."

Factorial con bucle while

Funciones en en bash son como programas en C. Este ejemplo es lo mismo del factorial de
antes, pero este vez utilizando un bucle while.

 function factorial ()
 {
    N=$1
    A=1
    while test $N -gt 0 ; do
         A = 'expr $A '+' $N'
         N = 'expr $N - 1'
    done
    echo $A
 }
Algunas Bourne Shell Builtins
      :         expander argumentos y hacer redirecciones
      .         Leer y ejecutar los comandos del FILENAME en contexto de shell actual
      break Salir del bucle
      cd        cambiar directorio actual de trabajo
      continue resumir la proximo iteración dentro un bucle.
      Echo       imprime argumentos a salida estandar
      eval      argumentos estan concatenado
      exec
      exit      Salir del shell
      export pasar a procesos hijos
      getopts Parse options to shell scripts or functions.
      hash
      kill      señal para para proceso
      pwd       imprime directorio actual
      read       lear del entrada del shell
      return devuelve un funci\'on de shell con valor
      test, '['} Evaluar un expresión conditional.
      'imes imprime los usuarios y tiempos de sistema usado por los hijos.

Variables
       IFS              una lista de caracteres que separar campos utlizado cuando el
                       shell compartir palabras como parte de la expansión
       PATH            lista de directorios que shell puede buscar comandos
       HOME            directorio home de usuario
       CDPATH           lista de directorios usado por comando cd
       MAILPATH         lista de ficheros que el shell mira para nuevas mensajes de correo
       PS1             cadena primaria de prompt
       PS2             cadena secundaria de prompt
       OPTIND          el indice de la última opción procesado por getopts
       OPTARG           el valor de la última argumenta procesado por getopts

Ejemplos

 # cleanup: Un script para limpiar los ficheros log en /var/log
 # Ejecutar como root

 cd /var/log
 cat /dev/null > messages
 cat /dev/null > wtmp
 echo "Los logs esta limpia."
Example 2-2. cleanup An enhanced and generalized version of above      script.
 #!/bin/bash
 # cleanup, version 2
 # Run as root, of course.

 if [ -n $1 ]
 # Test if command line argument present.
 then
    lines=$1
 else
    lines=50
    # default, if not specified on command line.
 Fi
 cd /var/log
 tail -$lines messages > mesg.temp
 # Saves last section of message log file.
 mv mesg.temp messages

 # cat /dev/null > messages
 # No longer needed, as the above method is safer.

 cat /dev/null > wtmp
 echo "Logs cleaned up."

 exit 0
 # A zero return value from the script upon exit
 # indicates success to the shell.

Conditionals with variables

 #!/bin/bash
   T1="foo"
   T2="bar"
  if [ "$T1" = "$T2" ]; then
    echo expression evaluated as true
  else
    echo expression evaluated as false
    fi

Caracteres especiales de Bash

Algunas caracteres tiene distintos signficacia dependiendo en su contexto. Dos de ellos puede
ser las caracteres siguientes:

   •   ; Seperador de comandos. Puede poner dos comandos en misma linea.
   •   : null command. Exit status 0, alias for true, see below

 echo hola; echo amigo     En este ejemplo, vemos la manera de separar dos comandos.

Endless loop:

 while :
 do
    operation-1
    operation-2
    ...
    operation-n
 done

 if condition
 then :   # Do nothing and branch ahead
 else
    take-some-action
 fi
${} Parameter substitution.

   •      ${parameter-default}         Si parametro no esta fija, usar default
   •      ${parameter=default}         Si parametro no esta fija, fijar al default
   •      ${parameter+otherwise}       Si parametro fija, usar 'otherwise", else usar null string
   •      ${parameter?err_msg}         Si parametro fjio, usarlo, else imprimir err_msg

  #!/bin/bash
 : ${HOSTNAME?} {USER?} {MAIL?}
   echo $HOSTNAME
   echo $USER
   echo $MAIL
   echo Critical env. variables set.
 exit 0

Sustitución y expansión

   •      ${var#pattern}, ${var##pattern} quitar mas corta/mas larga parte de patron
          si es similar a principio de variable
   •      ${var%pattern}, ${var%%pattern} quitar mas corta/mas larga si es similar a
          final de variable
   •      ${var:pos} variable var expandido, empezando con posición pos
   •      ${var:pos:len} expansión a max de longitud len de caracteres de variable var
          desde la posición pos
   •      ${var/patt/replacement} primer similaridad de patron, dentro var replaced with
          replacement.
   •      ${var//patt/replacement} all matches of pattern, within var replaced with
          replacement.
   •
Ejemplo

 #!/bin/bash

 var1=abcd-1234-defg
 echo "var1 = $var1"

 t=${var1#*-*}
 echo "var1 (with everything, up to and including first - stripped out) = $t"
 t=${var1%*-*}
 echo "var1 (with everything from the last - on stripped out) = $t"

 path_name=/home/bozo/ideas/thoughts.for.today
 echo "path_name = $path_name"
 t=${path_name##/*/}
 # Same effect as    t=`basename $path_name`
 echo "path_name, stripped of prefixes = $t"
 t=${path_name%/*.*}
 # Same effect as    t=`dirname $path_name`
 echo "path_name, stripped of suffixes = $t"

 t=${path_name:11}
 echo "$path_name, with first 11 chars stripped off = $t"
 t=${path_name:11:5}
 echo "$path_name, with first 11 chars stripped off, length 5 = $t"

 t=${path_name/bozo/clown}
 echo "$path_name with bozo replaced = $t"
 t=${path_name//o/O}
 echo "$path_name with all o's capitalized = $t"

 exit 0
Grupo de Comandos

(….)     Grupo de comandos. \texttt{(a=hello; echo \$a)}
{ ….} Bloque de código. Crea un funci\'on anonimo. Bloque de codigo puede tener E/S
rediricionado desde y hacia el.

 #!/bin/bash
 # Bloques de código y E/S redirección
 {
 read fstab
 } < /etc/fstab

 echo "Primer linea de /etc/fstab es:"
 echo "$fstab"

 exit 0

Redirección

The concept of a standard input and a standard output is one of the most useful features of
UNIX-like operating systems. The idea is that every program, no matter how diverse, should be
reading input and generating output in a uniform, clearly understood way. Once you have that, it
makes it much easier for programs to talk to one another directly. It is also possible to write generic
programs that bridge the input and output of any two programs, which is an amazingly handy ability
to have.

>           Send standard output to a file.
<           Read standard input from a file.
2>          Redirect standard error as specified.
>>          Append standard output to the specified file.
|           Couple the standard output of one program to the standard input of another
            program.
tee         Copy standard input to a file and also to standard output.
script Save the activity on your screen in a file.
xargs Read multiple arguments from standard input and use them as arguments to
            some specified command.

     •    scriptname >filename
     •    command >&2               (redireccionar salida de un comando a stderr)
     •    scriptname >>filename     (anadir la salida de scriptname a un fichero)

     •    |    forzar redirecci\'on. Sobre-escribir un fichero existente.
     •    -     redirecci\'on desde/hacia stdin or stdout.

Ejemplos:

 echo ls -l | sh
cat *.lst | sort | uniq                 # ordenar la salida de .lst
                                         # ficheros y borrar lineas duplicados.

 (cd /source/directory && tar cf - . ) | (cd /dest/directory && tar xvfp -)
 # Mover arbol de fichero entero desde un directorio a otro

 bunzip2 linux-2.2.15.tar.bz2 | tar xvf -

Redirección y ficheros

Hay tres descriptores de ficheros: stdin, stdout y stderr

    •   redireccionar stdout a un fichero
    •   redireccionar stderr a un fichero
    •   redireccionar stdout a un fichero
    •   redireccionar stderr a stdout
    •   redireccionar stderr y stdout a un fichero
    •   redireccionar stderr y stdout a stdout
    •   redireccionar stderr y stdout a stderr

1 representa stdout
2 representa stderr

 grep da * 2> grep-errors.txt        Escribir sderr a un fichero

        grep da * 1>&2
                           Escribir stdout a misma descriptor de fichero que stderr

Escribir sterr a misma descriptor de fichero que stdout

  grep * 2>&1
                   rm -f $(find / -name core) &> /dev/null

Tuberias

 ls -l | sed -e "s/[aeio]/u/g"

 ls -l | grep "\.txt$"
Acceder Parametros

$0, $1,etc. parametros positional (desde linea de comandos o desde función, o
      un variable)
$#    numero de argumentos o parametros posicional en linea de comandos
$$    id de proceso de script, utilizado a menudo en scripts para construir nombre de
      ficheros temporal
$?    estatus de salida (exit) de función o de script
$*    todo los parametros positionales
$@    lo mismo de $*, pero para cada parametro es un string en comillas
$-    flags passed to script
$!     PID de última job ejecutando en fondo
=     asignación de variables (no espacio antes y despues de &)

Obtener valor de return de un programa

Valor de salida return value de una programa esta guardad en un variable especial, denomida
$?.
Ejemplo de como capturar valor de salida:

 #!/bin/bash
 cd /dada &> /dev/null
 echo rv: $?
 cd $(pwd) &> /dev/null
 echo rv: $?

                                           #!/bin/bash
Comparición de Cadenas                     S1='string'
                                           S2='String'
s1 = s2 s1 igual a s2                      if [ $S1=$S2 ];
                                               then
s1 != s2 s1 no es igual a s2                          echo "S1('$S1') no es igula a S2('$S2')"
s1 < s2                                          fi
s1 > s2                                          if [ $S1=$S1 ];
-n s1 s1 no es null                              then
                                                       echo "S1('$S1') es igual a S1('$S1')"
z s1 s1 es null                                  fi

Sentencia de if/then
                                                     #!/bin/bash

 if [ condition-true ]                               if [ 0 ]
 then                                                then
    command 1                                           echo "0 es verdad."
    command 2                                        else
    ...                                                 echo "0 es falso."
 else                                                fi
    # Opcional para anadir mas chequeos
    command 3                                        if [ ]     #NULL (empty condition)
    command 4                                        then
    ...                                                 echo "NULL es verdad."
 fi                                                  else
                                                        echo "NULL es falso."
                                                     fi

                                                     if [ xyz ]        #string
   if [ -x filename ]; then                          then
                                                        echo "string aleatorio es verdad."
   elif                                              else
                                                        echo "string aleatorio es falso."
                                                     fi
Equivalence of [ ] and test

 #!/bin/bash

 if test -z $1
 then
    echo "No command-line arguments."
 else
    echo "First command-line argument is $1."
 fi

 # Both code blocks are functionally identical.

 if [ -z $1 ]
 # if [ -z $1
 # also works, but outputs an error message.
 then
    echo "No command-line arguments."
 else
    echo "First command-line argument is $1."
 fi
 exit 0

Tests, command chaining, redirección

 #!/bin/bash
 filename=sys.log

 if [ ! -f $filename ]
 then
    touch $filename; echo "Creating file."
 else
    cat /dev/null > $filename; echo "Cleaning out file."
 fi

 # Nota /var/log/messages tiene que tener permisos (644) para que este funciona
 tail /var/log/messages > $filename
 echo "$filename contains tail end of system log."

 exit 0

Aritmetica y comparaciones de cadenas
#!/bin/bash

 a=4
 b=5
 # a y b puede estar tratado como enteros o strings.

 if [ $a -ne $b ]
 then
    echo "$a is not equal to $b"
    echo "(arithmetic comparison)"
 fi

 echo

 if [ $a != $b ]
 then
    echo "$a is not equal to $b."
    echo "(string comparison)"
 fi

 echo

Operadores Aritmeticas

Arithmetic operators: +, -, *, /
Arithmetic relational operators: -lt (), -le (=), -eq (==),   -ne (!=)

Using expr: Arithmetic Operators
  #!/bin/bash

 echo Arithmetic Operators
 echo
 a=`expr 5 + 3`
 echo 5 + 3 = $a

 a=`expr $a + 1`
 echo
 echo a + 1 = $a
 echo \(incrementing a variable\)

 a=`expr 5 % 3`
 # modulo
 echo
 echo 5 mod 3 = $a

Logical Operators

 #!/bin/bash
 echo Logical Operators

 a=3
 echo a = $a
 b=`expr $a \> 10`
 echo 'b=`expr $a \> 10`, therefore...'
 echo "If a > 10, b = 0 (false)"
 echo b = $b

 b=`expr $a \< 10`
 echo "If a < 10, b = 1 (true)"
 echo b = $b
Comparison Operators

 #!/bin/bash
 echo Comparison Operators
 echo
 a=zipper
 echo a is $a
 if [ `expr $a = snap` ]
 # Force re-evaluation of variable 'a'
 then
    echo "a is not zipper"
 fi

String Operators

 #!/bin/bash
 echo String Operators
 echo

 a=1234zipper43231
 echo The string being operated upon is $a.
 # index: position of substring
 b=`expr index $a 23`
 echo Numerical position of first 23 in $a is $b.
 # substr: print substring, starting position & length specified
 b=`expr substr $a 2 6`
 echo Substring of $a, starting at position 2 and 6 chars long is $b.
 # length: length of string
 b=`expr length $a`
 echo Length of $a is $b.
 # 'match' operations similarly to 'grep'
 b=`expr match $a [0-9]*`
 echo Number of digits at the beginning of $a is $b.
 b=`expr match $a '\([0-9]*\)'`
 echo The digits at the beginning of $a are $b.

 exit 0

Letting let do some arithmetic
#!/bin/bash

 let a=11
 # Same as 'a=11'
 let a=a+5
 # Equivalent to let "a      = a + 5"
 # (double quotes makes      it more readable)
 echo "a = $a"
 let "a
--no-run-if-empty, -r        If the generated command line is empty, don't run
                             it.
--msax-chars=max-chars, -s   Specify a maximum length for a command line.
max-chars
--verbose, -t                Before executing the command, display it to
                             standard output.
--version                    Output the version number and exit.
--exit, -x                   Exit if the size specified by -s is exceeded.
--max-procs=max-procs, -P    Specify an upper limit of processes to be
max-procs                    simultaneously
También puede leer