# 103.4 - Uso de secuencias de texto, tuberías y redireccionamientos

<table data-header-hidden data-full-width="true"><thead><tr><th width="148"></th><th></th></tr></thead><tbody><tr><td><strong>Importancia</strong></td><td>4</td></tr><tr><td><strong>Descripción</strong></td><td>El candidato debe ser capaz de redireccionar secuencias de texto y conectarlas para procesar la información de forma eficiente. Estas tareas incluyen: la redirección de la entrada estándar, la salida estándar y el error estándar; el uso de tuberías para enviar la salida de un comando a la entrada de otro; el uso de la salida de un comando como argumento para otro comando, así como el envío de la salida de un comando simultáneamente a la salida estándar y a un archivo.</td></tr></tbody></table>

#### *Áreas de conocimiento clave:*

* Redireccionar la entrada estándar (*stdin*), la salida estándar (*stdout*) y el error estándar (*stderr*).
* Utilizar tuberías para enviar la salida de un comando a la entrada de otro.
* Usar la salida de un comando como argumento de otro comando.
* Enviar la salida de un comando a *stdout*y a un archivo simultáneamente.

### *Contenidos*

Los tres canales de comunicación estándar que vimos en el [topic 103.2](#contenidos-1) (*stdin*, *stdnout* y *stdnerr*) permiten a los programadores escribir código que lee y escribe datos sin preocuparse por el tipo de medio del que proviene o al que va.&#x20;

* Por ejemplo, si un programa necesita un conjunto de datos como entrada, solo puede solicitar datos de la `stdin (0)` y lo que se esté utilizando como entrada estándar proporcionará esos datos y del mismo modo, la formas más sencilla que tiene un programa para mostrar su salida es escribirlo en la ***stndout*** (1).&#x20;

En una sesión de shell estándar, el teclado se define como `stdin` y la pantalla del monitor se define como `stdout` y `stderr`, el shell **Bash** tiene la capacidad de reasignar los canales de comunicación al cargar un programa y permite, por ejemplo, anular la pantalla como salida estándar y usar un archivo en el sistema de archivos local como *stdout*.

Tal y como hemos ido viendo, la reasignación del descriptor de archivo de un canal en el entorno de shell se denomina **redirect** y se define mediante un caracter especial dentro de la línea de comandos.&#x20;

Por ejemplo, para redirigir la salida estándar de un proceso a un archivo, el símbolo mayor que > se coloca al final del comando y sigue la ruta al archivo que recibirá la salida redirigida:&#x20;

```bash
cat /proc/cpuinfo >/tmp/cpu.txt
#ó
cat /proc/cpu_info 2>>/tmp/error.txt #Si lo que se quiere es añadir en vez de sustituir
#tambien:
uniq -c </tmp/error.txt #Para redirigir un archivo al stdnin
```

Desde la perspectiva del programador, el uso de descriptores de archivos evita tener que lidiar con el análisis de opciones y las rutas del sistema de archivos. El mismo descriptor de archivo puede incluso usarse como entrada y salida y en este caso, el descriptor de archivo se define en la línea de comandos con símbolos menores y mayores que, como en `3<>/tmp/error.txt`.

Otra forma de redirigir la entrada involucra los métodos *Here Document* (<<) y *Here String* (<<<).&#x20;

```bash
wc -c <<archivo.txt
wc -c <<<"¿cuantas letras tiene esto?"
```

### Pipes y `tee`&#x20;

&#x20;El `|` le dice al shell que inicie todos los comandos distintos al mismo tiempo y que conecte la salida del comando anterior a la entrada del siguiente comando, de izquierda a derecha siendo el objetivo otro proceso, no una ruta del sistema de archivos, un descriptor de archivo o un documento.. Por ejemplo, con `cat /proc/cpuinfo | wc` en lugar de utilizar redireccionamientos, el contenido del archivo `/proc/cpuinfo` enviado a la salida estándar por `cat` puede canalizarse al *stdin* de `wc`.

Las tuberías se pueden combinar con redireccionamientos en la misma línea de comando. El ejemplo anterior se puede reescribir en una forma más simple:

```bash
grep 'model name' </proc/cpuinfo | uniq
```

Las tuberías y redirecciones son exclusivas, es decir, una fuente puede asignarse a un solo destino, sin embargo, es posible redirigir una salida a un archivo y aún verlo en la pantalla con `tee`. Es un bifurcador, coge una estándar output y la redirige a dos sitios a la vez.

Para hacerlo, el primer programa envía su salida al *stdin* de `tee` y se le proporciona un nombre de archivo a este último para almacenar los datos:&#x20;

```bash
grep 'model name' </proc/cpuinfo | uniq | tee cpu_model.txt
```

Solo la salida estándar de un proceso es capturada por una tee, por lo que:

<pre class="language-bash" data-overflow="wrap"><code class="lang-bash">make | tee log.txt #Esto fallará por que no se ha especificado archivo en make
<strong>
</strong><strong>make 2>&#x26;1 | tee log.txt #Esto no fallara por que el error (2) se redirige a (1) por lo tanto se guaradar y podremos leerlo:
</strong>cat log.txt
</code></pre>

Una aplicación muy practica de este comando es:

{% code overflow="wrap" fullWidth="true" %}

```bash
echo "192.168.2.2 server" >> /etc/hosts
#Esto da el error Permision denied, si lo haces con "sudo" entonces este sudo solo se lo estas aplicando al echo pero no a '>>' hay problemas de seguridad.

echo "192.168.2.2 server" | sudo tee /etc/hosts #Ahora si lo ha ejecutado el root
```

{% endcode %}

### Sustitución de comando y `xargs`

Otro método para capturar la salida de un comando es *command substitution*, al colocar un comando dentro de las comillas inversas (el acento abierto para nosotros) o bien poniendo el comando dentro de `$()` , *Bash* lo reemplaza con su *stdout (1)*.&#x20;

Por ejemplo:&#x20;

<pre class="language-bash"><code class="lang-bash"><strong>#el siguiente ejemplo creará una carpeta con la fecha formateada:
</strong><strong>mkdir date +%Y-%m-%d
</strong><strong>mkdir $(date +%Y-%m-%d)
</strong><strong>
</strong><strong>#También se puede usar para almacenar la salida de un comando como una variable:
</strong>OS=`uname -o`
echo $OS #Dara como resultado "GNU/Linux"
</code></pre>

Dependiendo de la salida generada por el comando reemplazado, la sustitución del comando incorporado puede no ser apropiada por ello, un método más sofisticado para usar la salida de un programa como argumento de otro programa emplea un intermediario llamado `xargs`. Este es un adaptador, permite hacer compatibles las salidas de un comando con las entradas de otro.

Resulta especialmente útil si estás procesando un gran número de archivos y necesitas realizar ciertas tareas de forma repetitiva, su definición es la siguiente:

```bash
Primer_Comando | xargs [Opciones] [Segundo_Comando]

#Algunas opciones más comunes:
-0 o –null #cada carácter se toma literalmente y el carácter NULL separa los argumentos.
-a o –arg-file #los argumentos se leen de un archivo en vez de la entrada estándar.
-d o –delimiter #cada carácter se toma literalmente pero la separación la marca el carácter delimitador y no los espacios en blanco.
-p o –interactive #antes de cada ejecutar xargs, se pregunta si se debe proceder.

#Por ejemplo:
find -name "*.txt" | xargs rm
```

En este ejemplo, `xargs` se utliza con el comando de Linux `find` y el comando de Linux `rm`. Como resultado, todos los archivos con la extensión *.txt* se eliminan del sistema de archivos del ordenador, así que cuidadito con este comando.

Vamos a ver un par más de ejemplos sencillos:

<pre class="language-bash"><code class="lang-bash"><strong>#Busca todos los archivos ejecutables y borralos redirigiendo errores al vacio
</strong><strong>find / -type f -name "*.exe" 2>/dev/null | xargs rm
</strong><strong>
</strong><strong>#Busca todos los archivos que coincidan y listalos
</strong>find / -type f -name file.01 2>/dev/null | xargs ls -l
</code></pre>

El siguiente ejemplo muestra `xargs` ejecutando el programa `identify` con argumentos proporcionados por el programa `find`:

```bash
find /usr/share/icons -name 'debian*' | xargs identify -format "%f: %wx%h\n"
```

En el ejemplo, `xargs` tomó todas las rutas listadas por `find` y las puso como argumentos para `identify`, que luego muestra la información para cada archivo formateado como lo requiere la opción `-format` (los archivos encontrados por `find` en el ejemplo son imágenes que contienen el logotipo de distribución en un sistema de archivos Debian).

* La opción `-n 1` requiere que `xargs` ejecute el comando dado con un solo argumento a la vez; en el caso del ejemplo, en lugar de pasar todas las rutas encontradas por `find` como una lista de argumentos para `identify`, usar `xargs -n 1` ejecutaría el comando `identify` para cada ruta por separado.&#x20;
* El uso de `-n 2` ejecutaría `identify` con dos rutas como argumentos, `-n 3` con tres rutas como argumentos y así sucesivamente.&#x20;

{% hint style="info" %}
`identify` es parte de *ImageMagick*, un conjunto de herramientas de línea de comandos para inspeccionar, convertir y editar la mayoría de los tipos de archivos de imagen.&#x20;
{% endhint %}

De manera similar, cuando `xargs` procesa contenidos de varias líneas, como es el caso de la entrada proporcionada por `find`, la opción `-L` puede usarse para limitar cuántas líneas se usarán como argumentos por ejecución de comando.

{% code fullWidth="true" %}

```bash
find . -name '*avi' -print0 -o -name '*mp4' -print0 -o -name '*mkv' -print0 | xargs -0 du | sort -n 
```

{% endcode %}

Dónde:

* `-print0` -> indica a `find` que use un carácter nulo entre cada entrada para que la lista pueda ser analizada correctamente por `xargs` (se suprimió la salida) por si las rutas tienen caracteres de espacio. Esta se debe poner por cada criterio de búsqueda.
* `-0` le dice a `xargs` que el carácter nulo debe usarse como separador así, las rutas  proporcionadas por `find` se analizan correctamente incluso si tienen caracteres en blanco u otros caracteres especiales.&#x20;
* `du` se usa para averiguar el espacio ocupado en disco de cada archivo encontrado
* `sort -n` para ordenar los resultados por tamaño.
