lunes, 30 de enero de 2017

Funciones de shell

Nuestro script actualmente realiza los siguientes pasos para generar el documento HTML:
  1. Abre la página
  2. Abre el encabezado de página.
  3. Establece el título de página.
  4. Cierra el encabezado de página.
  5. Abre el cuerpo de la página.
  6. Título de la página de salida.
  7. Marca de tiempo (fecha y hora) de la salida.
  8. Cierra el cuerpo de la página.
  9. Cierra la página.
Para nuestra próxima fase de desarrollo, añadiremos algunas tareas entre los pasos 7 y 8. Estos incluirán:
  • Tiempo de actividad y carga del sistema. Es la cantidad de tiempo desde el último encendido o reinicio y la cantidad promedio de tareas corriendo actualmente en el procesador en varios intervalos de tiempo.
  • Espacio en disco. El uso general del espacio en los dispositivos de almacenamiento del sistema.
  • Espacio Home. La cantidad de espacio de almacenamiento usado por cada usuario.
Si tuviéramos un comando para cada una de dichas tareas, podríamos añadirlos a nuestro script simplemente a través de sustitución de comandos:

#!/bin/bash

# Program to output a system information page

TITLE="System Information Report For $HOSTNAME"
CURRENT_TIME=$(date +"%x %r %Z")
TIMESTAMP="Generated $CURRENT_TIME, by $USER"

cat << _EOF_
<HTML>
        <HEAD>
                <TITLE>$TITLE</TITLE>
        </HEAD>
        <BODY>
                <H1>$TITLE</H1>
                <P>$TIMESTAMP</P>
                $(report_uptime)
                $(report_disk_space)
                $(report_home_space)
        </BODY>
</HTML>
_EOF_

Podríamos crear estos comandos adicionales de dos formas. Podríamos escribir tres scripts separados y colocarlos en un directorio listado en nuestro PATH, o podríamos incluir los scripts dentro de nuestro programa como funciones de shell. Como hemos mencionado antes, las funciones de shell son "mini-scripts" que se colocan dentro de otros scripts y pueden actuar como programas autónomos. Las funciones de shell tienen dos formas sintácticas:

function name {
        commands
        return
}

y

name () {
        commands
        return
}

donde name es el nombre de la función y commands es una serie de comandos contenidos dentro de la función. Ambas formas son equivalentes y podrían usarse indistintamente. A continuación vemos un script que demuestra el uso de una función de shell:

1 #!/bin/bash
2
3 # Shell function demo
4
5 function funct {
6         echo "Step 2"
7         return
8 }
9
10 # Main program starts here
11
12 echo "Step 1"
13 funct
14 echo "Step 3"

A medida que el shell lee el script, va pasando por alto de la línea 1 a la 11, ya que estas líneas son comentarios y la definición de la función. La ejecución comienza en la línea 12 con un comando echo. La línea 13 llama a la función de shell funct y el shell ejecuta la función tal como haría con cualquier otro comando. El control del programa se mueve entonces a la línea 6, y se ejecuta el segundo comando echo. La línea 7 se ejecuta a continuación. Su comando return finaliza la función y devuelve el control al programa en la línea que sigue a la llamada a la función (línea 14), y el comando final echo se ejecuta. Fíjate que, para que las llamadas a funciones sean reconocidas como funciones de shell y no interpretadas como nombres de programas externos, las definiciones de funciones de shell deben aparecer en el script antes de que sean llamadas.

Añadiremos unas definiciones de funciones de shell mínimas a nuestro script:

#!/bin/bash

# Program to output a system information page

TITLE="System Information Report For $HOSTNAME"
CURRENT_TIME=$(date +"%x %r %Z")
TIMESTAMP="Generated $CURRENT_TIME, by $USER"

report_uptime () {
        return
}
report_disk_space () {
        return
}
report_home_space () {
        return
}

cat << _EOF_
<HTML>
     <HEAD>
         <TITLE>$TITLE</TITLE>
     </HEAD>
     <BODY>
          <H1>$TITLE</H1>
          <P>$TIMESTAMP</P>
          $(report_uptime)
          $(report_disk_space)
          $(report_home_space)
     </BODY>
</HTML>
_EOF_

Los nombres de funciones de shell siguen las mismas reglas que las variables. Una función debe contener al menos un comando. El comando return (que es opcional) satisface este requerimiento.

viernes, 27 de enero de 2017

Diseño de arriba a abajo

A medida que los programas se hacen más grandes y complejos, se hacen más difíciles de diseñar, codificar y mantener. Al igual que con cualquier proyecto grande, a menudo es una buena idea dividir las tareas grandes y complejas en una serie de tareas simples y pequeñas. Imaginemos que estamos intentando describir una tarea común y cotidiana, ir al mercado a comprar comida, a una persona de Marte. Describiríamos el proceso completo como la siguiente serie de pasos:
  1. Subir al coche
  2. Conducir hasta el mercado
  3. Aparcar el coche
  4. Entrar en el mercado
  5. Comprar comida
  6. Volver al coche
  7. Conducir a casa
  8. Aparcar el coche
  9. Entrar en casa
Sin embargo una persona de Marte seguro que necesita más detalles. Podríamos dividir aún más la subtarea "Aparcar el coche" en una serie de pasos:
  1. Encontrar un sitio para aparcar
  2. Meter el coche dentro de ese sitio
  3. Apagar el motor
  4. Poner el freno de mano
  5. Salir del coche
  6. Cerrar el coche
La subtarea "Apagar el motor" podría dividirse aún en más pasos incluyendo "Apagar el contacto", "Sacar la llave", y así sucesivamente, hasta que cada paso del proceso completo de ir al mercado hubiera sido completamente definido.

El proceso de identificar los pasos de alto nivel y desarrollar incrementalmente vistas detalladas de dichos pasos se llama diseño de arriba a abajo. Esta técnica nos permite romper tareas grandes y complejas en muchas tareas pequeñas y simples. El diseño de arriba a abajo es un método común de diseñar programas y uno de los que se ajustan muy bien a la programación en shell en particular.

En este capítulo, usaremos el diseño de arriba a abajo para seguir desarrollando nuestro script generador de informes.

miércoles, 25 de enero de 2017

Para saber más

  • La man page de bash incluye una sección titulada "Documentos-aquí", que tiene una descripción completa de esta característica.

martes, 24 de enero de 2017

Resumiendo

En este capítulo, hemos comenzado un proyecto que nos llevará a través del proceso de construcción de un script exitoso. Hemos presentado el concepto de variables y constantes y comó pueden utilizarse. Son la primera de las muchas aplicaciones que encontraremos para expansión de parámetros. También hemos visto como producir salida de nuestro script, y varios métodos para incluir bloques de texto.

lunes, 23 de enero de 2017

Documentos-aquí

Hemos visto dos métodos distintos de mostrar nuestro texto, ambos usando el comando echo. Hay una tercera forma llamada here document or here script (documento-aquí o script-aquí). Un documento-aquí es una forma adicional de redirección I/O en la que incluimos un cuerpo de texto en nuestro script y lo introducimos en la entrada estándar de un comando. Funciona así:

comando << token

texto

token

donde comando es el nombre de un comando que acepta la entrada estándar y token es una cadena usada para indicar el final del texto incluido. Modificaremos nuestro script para usar un documento-aquí:

#!/bin/bash

# Program to output a system information page

TITLE="System Information Report For $HOSTNAME"
CURRENT_TIME=$(date +"%x %r %Z")
TIMESTAMP="Generated $CURRENT_TIME, by $USER"

cat << _EOF_
<HTML>
        <HEAD>
                <TITLE>$TITLE</TITLE>
        </HEAD>
        <BODY>
                <H1>$TITLE</H1>
                <P>$TIMESTAMP</P>
        </BODY>
</HTML>
_EOF_

En lugar de usar echo, nuestro script ahora usa cat y un documento-aquí. La cadena _EOF_ (que significa "End Of File" o "Fin del archivo", una convención común) ha sido seleccionada como token, y marca el final del texto incluido. Fíjate que el token debe aparecer sólo y no debe haber espacios al final de la línea.

Entonces ¿cual es la ventaja de usar un documento-aquí? Es principalmente lo mismo que echo, excepto que, por defecto, las comillas sencillas y dobles dentro de los documentos-aquí pierden su significado especial para el shell. Aquí hay un ejemplo en la línea de comandos:

[me@linuxbox ~]$ foo="some text"
[me@linuxbox ~]$ cat << _EOF_
> $foo
> "$foo"
> '$foo'
> \$foo
> _EOF_
some text
"some text"
'some text'
$foo

Como podemos ver, el shell no presta atención a las comillas. Las trata como caracteres ordinarios. Esto nos permite incluir comillas libremente dentro de un documento-aquí. Podría ser útil para nuestro programa de informes.

Los documentos-aquí pueden usarse con cualquier comando que acepte entrada estándar. En este ejemplo, usamos un documento-aquí para pasar una serie de comandos al programa ftp para descargar un archivo de un servidor FTP remoto:

#!/bin/bash

# Script to retrieve a file via FTP

FTP_SERVER=ftp.nl.debian.org
FTP_PATH=/debian/dists/lenny/main/installer-i386/current/images/cdrom
REMOTE_FILE=debian-cd_info.tar.gz

ftp -n << _EOF_
open $FTP_SERVER
user anonymous me@linuxbox
cd $FTP_PATH
hash
get $REMOTE_FILE
bye
_EOF_
ls -l $REMOTE_FILE

Si cambiamos el operador de redirección de "<<" a "<<-" el shell ignorará los caracteres tabulador iniciales en el documento-aquí. Esto permite sangrar un documento-aquí, lo que puede mejorar su legibilidad:

#!/bin/bash

# Script to retrieve a file via FTP

FTP_SERVER=ftp.nl.debian.org
FTP_PATH=/debian/dists/lenny/main/installer-i386/current/images/cdrom
REMOTE_FILE=debian-cd_info.tar.gz

ftp -n <<- _EOF_
    open $FTP_SERVER
    user anonymous me@linuxbox
    cd $FTP_PATH
    hash
    get $REMOTE_FILE
    bye
    _EOF_

ls -l $REMOTE_FILE

viernes, 20 de enero de 2017

Asignando valores a variables y constantes

Aquí es donde nuestro conocimiento de las expansiones empieza a dar sus frutos. Como hemos visto, a las variables se le asignan valores así:

variable=valor

donde variable es el nombre de la variable y valor es una cadena. Al contrario de otros lenguajes de programación, el shell no se preocupa por el tipo de datos asignados a una variable; los trata a todos como cadenas. Puedes forzar al shell a restringir la asignación a enteros usando el comando declare con la opción -i, pero al igual que con la configuración de las variables como sólo lectura, no se suele hacer.

Fíjate que en una asignación, no debe haber espacios entre el nombre de la variable, el signo igual y el valor. Entonces ¿en qué puede consistir el valor? Cualquier cosa que pueda expandirse en una cadena:

a=z                 # Assign the string "z" to variable a.
b="a string"        # Embedded spaces must be within quotes.
c="a string and $b" # Other expansions such as variables can be
                    # expanded into the assignment.
d=$(ls -l foo.txt)  # Results of a command.
e=$((5 * 7))        # Arithmetic expansion.
f="\t\ta string\n"  # Escape sequences such as tabs and newlines.

Pueden hacerse múltiples asignaciones de variables en una única línea:

a=5 b="a string"

Durante la expansión, los nombres de variables pueden ir rodeados de llaves opcionales "{}". Esto es útil en casos donde el nombre de una variable puede llegar a ser ambiguo debido a su contexto próximo. Aquí, tratamos de cambiar el nombre de un archivo de myfile a myfile1, usando una variable:

[me@linuxbox ~]$ filename="myfile"
[me@linuxbox ~]$ touch $filename
[me@linuxbox ~]$ mv $filename $filename1
mv: missing destination file operand after `myfile'
Try `mv --help' for more information.

Este intento falla porque el shell interpreta el segundo argumento del comando mv como una variable nueva (y vacía). El problema puede solucionarse de esta forma:

[me@linuxbox ~]$ mv $filename ${filename}1

Añadiendo las llaves en los extremos, el shell ya no interpreta el 1 del final como parte del nombre de la variable.

Aprovecharemos esta oportunidad para añadir algunos datos a nuestro informe, digamos la fecha y hora en que fue creado el informe y el nombre de usuario del creador:

#!/bin/bash

# Program to output a system information page

TITLE="System Information Report For $HOSTNAME"
CURRENT_TIME=$(date +"%x %r %Z")
TIMESTAMP="Generated $CURRENT_TIME, by $USER"

echo "<HTML>
        <HEAD>
                <TITLE>$TITLE</TITLE>
        </HEAD>
        <BODY>
                <H1>$TITLE</H1>
                <P>$TIMESTAMP</P>
        </BODY>
</HTML>"

miércoles, 18 de enero de 2017

Variables y constantes

Sin embargo, hay un problema con nuestro script. ¿Ves cómo la cadena "System Information Report" está repetida? Con nuestro diminuto script no es un problema, pero imaginemos que nuestro script fuera realmente largo y que tuviéramos múltiples instancias de esta cadena. Si quisiéramos cambiar el título a otra cosa, habríamos tenido que cambiarlo en múltiples sitios, lo que podría ser mucho trabajo. ¿Y si pudiéramos arreglar el script para que la cadena sólo apareciera una vez y no múltiples veces? Podría hacer que el mantenimiento del script en el futuro sea más fácil. Aquí tenemos cómo podríamos hacerlo:

#!/bin/bash

# Program to output a system information page

title="System Information Report"

echo "<HTML>
        <HEAD>
                <TITLE>$title</TITLE>
        </HEAD>
        <BODY>
                <H1>$title</H1>
        </BODY>
</HTML>"

Creando una variable llamada title y asignándole el valor "System Information Report", podemos aprovechar la expansión de parámetros y colocar la cadena en múltiples lugares.

Pero ¿cómo creamos una variable? Simplemente, la usamos. Cuando el shell encuentra una variable, automáticamente la crea. Esto difiere de muchos lenguajes de programación en los que las variables deben ser declaradas o definidas explícitamente antes de usarla. El shell es muy laxo en esto, lo que puede causar algunos problemas. Por ejemplo, considera este escenario ejecutado en la línea de comandos:

[me@linuxbox ~]$ foo="yes"
[me@linuxbox ~]$ echo $foo
yes
[me@linuxbox ~]$ echo $fool

[me@linuxbox ~]$

Primero asignamos el valor "yes" a la variable foo, y luego mostramos su valor con echo. A continuación mostramos el valor de la variable mal escrita como "fool" y tenemos un resultado en blanco. Esto es porque el shell ha creado felizmente la variable fool cuando la encontró, y le ha dado el valor por defecto de nada, o vacía. Con esto, aprendemos que ¡debemos prestar mucha atención a nuestra escritura! También es importante entender lo que realmente ha ocurrido en este ejemplo. De nuestro estudio previo a cómo el shell realiza las expansiones, sabemos que el comando:

[me@linuxbox ~]$ echo $foo

se somete a expansión de parámetros resultando en:

[me@linuxbox ~]$ echo yes

Mientras que el comando:

[me@linuxbox ~]$ echo $fool

se expande a:

[me@linuxbox ~]$ echo

¡La variable vacía se expande en nada! Esto puede causar estragos en comandos que requieren argumentos. Aquí tenemos un ejemplo:

[me@linuxbox ~]$ foo=foo.txt
[me@linuxbox ~]$ foo1=foo1.txt
[me@linuxbox ~]$ cp $foo $fool
cp: missing destination file operand after `foo.txt'
Try `cp --help' for more information.

Asignamos valores a dos variables, foo y foo1. Luego realizamos un cp, pero escribimos mal el nombre del segundo argumento. Tras la expansión, al comando cp sólo se le envía un argumento, aunque requiere dos.

Hay algunas reglas sobre los nombres de variables:
  1. Los nombres de variables pueden consistir en caracteres alfanuméricos (letras y números) y caracteres guión bajo.
  2. El primer carácter de un nombre de variable puede ser tanto una letra o un guión bajo.
  3. Los espacios y símbolos de puntuación no están permitidos.
La palabra "variable" implica un valor que cambia, y en muchas aplicaciones, las variables se usan de esta forma. Sin embargo, la variable de nuestra aplicación, title, se usa como una constante. Una constante es como una variable ya que tiene un nombre y contiene un valor. La diferencia es que el valor de una constante no cambia. En una aplicación que realice cálculos geométricos, podríamos definir PI como una constante, y asignarle el valor de 3,1415, en lugar de usar el número literalmente a lo largo de nuestro programa. El shell no hace distinción entre constantes y variables; es más bien a conveniencia del programador. Una convención común es usar letras mayúsculas para designar contantes y minúsculas para variables. Modificaremos nuestro script para cumplir con esta convención:

#!/bin/bash

# Program to output a system information page

TITLE="System Information Report For $HOSTNAME"

echo "<HTML>
        <HEAD>
                <TITLE>$TITLE</TITLE>
        </HEAD>
        <BODY>
                <H1>$TITLE</H1>
        </BODY>
</HTML>"

Tenemos la oportunidad de mejorar nuestro título añadiendo el valor de la variable de shell HOSTNAME. Es el nombre de red de la máquina.

Nota: El shell en realidad proporciona una forma de forzar la inmutabilidad de las constantes, a través del uso del comando integrado declare con la opción -r (sólo lectura). Si le asignamos TITTLE de esta forma:

declare -r TITLE="Page Title"

el shell prevendrá cualquier asignación posterior a TITTLE. Esta característica es poco usada, pero existe en scripts muy formales.

lunes, 16 de enero de 2017

Segunda etapa: Añadiendo algunos datos

Ahora que nuestro programa puede generar un documento minimalista, pongamos algunos datos en el informe. Para hacerlo, haremos los siguientes cambios:

#!/bin/bash

# Program to output a system information page

echo "<HTML>
        <HEAD>
                <TITLE>System Information Report</TITLE>
        </HEAD>
        <BODY>
                <H1>System Information Report</H1>
        </BODY>
</HTML>"

Hemos añadido un título de página y un encabezado al cuerpo del informe.

viernes, 13 de enero de 2017

Primera etapa: Documento minimalista

Lo primero que necesitamos saber es el formato de un documento HTML bien construido. Tiene esta pinta:

<HTML>
    <HEAD>
        <TITLE>Page Title</TITLE>
    </HEAD>
    <BODY>
        Page body.
    </BODY>
</HTML>

Si introducimos esto en nuestro editor de texto y guardamos el archivo como foo.html, podemos usar la siguiente URL en Firefox para ver el archivo:

file:///home/username/foo.html

La primera etapa de nuestro programa será capaz de generar este archivo HTML en la salida estándar. Podemos escribir un programa que haga esto muy fácilmente. Arranquemos nuestro editor de texto y creemos un nuevo archivo llamado ~/bin/sys_info_page:

[me@linuxbox ~]$ vim ~/bin/sys_info_page

e introduzcamos el siguiente programa:

#!/bin/bash



# Program to output a system information page



echo "<HTML>"
echo "    <HEAD>"
echo "          <TITLE>Page Title</TITLE>"
echo "    </HEAD>"
echo "    <BODY>"
echo "          Page body."
echo "    </BODY>"
echo "</HTML>"

Nuestro primer acercamiento a este problema contiene un shebang, un comentario (siempre es una buena idea) y una secuencia de comandos echo, uno por cada línea de salida. Tras guardar el archivo, lo haremos ejecutable y trataremos de ejecutarlo:

[me@linuxbox ~]$ chmod 755 ~/bin/sys_info_page
[me@linuxbox ~]$ sys_info_page

Cuando el programa se ejecute, deberíamos ver el texto del documento HTML mostrado en la pantalla, ya que los comandos echo del script envían su salida a la salida estándar. Ejecutaremos el programa de nuevo y redirigiremos la salida del programa al archivo sys_info_page.html, con lo que podemos ver el resultado con un navegador web:

[me@linuxbox ~]$ sys_info_page > sys_info_page.html
[me@linuxbox ~]$ firefox sys_info_page.html

Hasta aquí todo bien.

Cuando escribimos programas, siempre es una buena idea esforzarse en que sean simples y claros. El mantenimiento es más fácil cuando el programa es fácil de leer y comprender, no hace falta mencionar que podemos hacer que el programa sea más fácil de realizar reduciendo la cantidad de escritura. Nuestra versión actual del programa funciona bien, pero podría ser más simple. Podríamos en realidad combinar todos los comandos echo en uno, lo que ciertamente haría más fácil añadir más líneas a la salida del programa. Así que, hagamos ese cambio en nuestro programa:

#!/bin/bash

# Program to output a system information page

echo "<HTML>
     <HEAD>
           <TITLE>Page Title</TITLE>
     </HEAD>
     <BODY>
           Page body.
     </BODY>
</HTML>"

Una cadena entre comillas puede incluir nuevas líneas, y por lo tanto contener múltiples líneas de texto. El shell seguirá leyendo el texto hasta que se encuentre las comillas de cierre. Funciona de esta forma también en la línea de comandos:

[me@linuxbox ~]$ echo "<HTML>
>         <HEAD>
>                 <TITLE>Page Title</TITLE>
>         </HEAD>
>         <BODY>
>                 Page body.
>         </BODY>
> </HTML>"

El carácter ">" que va delante es el prompt de shell contenido en la variable de shell PS2. Aparece siempre que escribamos una sentencia multilínea en el shell. Esta función es un poco oscura ahora mismo, pero más adelante, cuando veamos las sentencias de programación multilínea, se transformará en algo más útil.

miércoles, 11 de enero de 2017

Comenzando un proyecto

A partir de este capítulo, comenzaremos a construir un programa. El propósito de este proyecto es ver cómo varias funciones del shell se usan para crear programas y, lo que es más importante, para crear buenos programas.

El programa que escribiremos es un generador de informes. Presentará varias estadísticas de nuestro sistema y su estado, y producirá este informe en formato HTML, para que podamos verlo en un navegador web como Firefox o Chrome.

Los programas a menudo se construyen en una serie de etapas, donde cada etapa añade funciones y capacidades. La primera etapa de nuestro programa producirá una página HTML muy minimalista que no contiene información del sistema. Eso vendrá luego.

lunes, 9 de enero de 2017

Para saber más

viernes, 6 de enero de 2017

Resumiendo

En este primer capítulo sobre scripting, hemos visto cómo se escriben los scripts y cómo se hace para que se ejecuten fácilmente en nuestro sistema. También vimos cómo podemos usar varias técnicas de formateo para mejorar la legibilidad (y por lo tanto, el mantenimiento) de nuestros scripts. En capítulos posteriores, la facilidad de mantenimiento aparecerá una y otra vez como un principio central en una buena escritura de scripts.

miércoles, 4 de enero de 2017

Configurando vim para escribir scripts

El editor de texto vim tiene muchas, muchas posibilidades de configuración. Hay varias opciones comunes que pueden facilitar la escritura de scripts:

:syntax on

activa el destacado sintáctico. Con esta configuración, diferentes elementos de la sintaxis del shell se mostrarán en colores diferentes cuando veamos un script. Esto es útil para identificar ciertos tipos de errores de programación. Además se ve genial. Fíjate que para que esta característica funcione, debes tener una versión completa de vim instalada, y el archivo que estés editando debe tener un shebang indicando que el archivo es un script de shell. Si tienes dificultades con el comando anterior, prueba :set syntax=sh en su lugar.

:set hlsearch

activa la opción de destacar los resultados de búsqueda. Digamos que buscamos la palabra "echo." Con esta opción activada, cada instancia de la palabra será destacada.

:set tabstop=4

establece el número de columnas ocupadas por un carácter tabulador. Por defecto son 8 columnas. Estableciendo el valor a 4 (que es una práctica común) permite a las líneas largas ajustarse más fácilmente a la pantalla.

:set autoindent

activa la función "sangría automática". Esto hace que vim sangre una nueva línea la misma cantidad que la línea recién escrita. Esto acelera la escritura en muchos tipos de construcción de programas. Para detener la sangría, pulsa Ctrl-d.

Estos cambios pueden hacerse permanentes añadiendo estos comandos (sin los dos puntos delante) a tu archivo ~/.vimrc.

lunes, 2 de enero de 2017

Sangría y continuación de líneas

Cuando empleamos comandos largos, la legibilidad puede mejorarse separando el comando en varias líneas. En el capítulo 17, vimos un ejemplo particularmente largo del comando find:

[me@linuxbox ~]$ find playground \( -type f -not -perm 0600 -exec chmod 0600 ‘{}’ ‘;’ \) -or \( -type d -not -perm 0700 -exec chmod 0700 ‘{}’ ‘;’ \)

Obviamente, este comando es un poco complicado de descifrar a primera vista. En un script, este comando podría ser más fácil de comprender si está escrito de esta forma:

find playground \
    \( \
        -type f \
        -not -perm 0600 \
        -exec chmod 0600 ‘{}’ ‘;’ \
    \) \
    -or \
    \( \
        -type d \
        -not -perm 0700 \
        -exec chmod 0700 ‘{}’ ‘;’ \
    \)

Usando continuaciones de línea (secuencias de barra invertida-salto de línea) y sangría, la lógica de este comando complejo se describe más claramente para el lector. Esta técnica también funciona en la línea de comandos, aunque raramente se utiliza, ya que es muy incómodo de escribir y editar. Una diferencia entre un script y la línea de comandos es que el script puede emplear caracteres de tabulación para lograr la sangría, mientras que la línea de comandos no puede, ya que el tabulador se usa para activar el completado.