<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE book PUBLIC "-//NLM//DTD BITS Book Interchange DTD v2.0 20151225//EN" "https://jats.nlm.nih.gov/extensions/bits/2.1/BITS-book2-1.dtd">
<book book-type="chapter" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:mml="http://www.w3.org/1998/Math/MathML" xmlns:ali="http://www.niso.org/schemas/ali/1.0/" xmlns:xlink="http://www.w3.org/1999/xlink" dtd-version="2.0" xml:lang="es">
<book-meta>
<book-id/>
<book-id book-id-type="publisher">Universidad Rey Juan Carlos</book-id>
<subj-group>
<subject>Material docente</subject>
</subj-group>
<book-title-group>
<book-title><target target-type="page" id="pges_1"/><target target-type="page" id="pges_2"/><target target-type="page" id="pges_3"/><target target-type="page" id="pges_4"/>LABORATORIO DE ADMINISTRACI&#x00D3;N Y GESTI&#x00D3;N DE REDES Y SISTEMAS ESCUELA T&#x00C9;CNICA SUPERIOR DE INGENIER&#x00CD;A DE TELECOMUNICACI&#x00D3;N</book-title>
</book-title-group>
<pub-date>
<month>09</month>
<year>2022</year>
</pub-date>
<publisher>
<publisher-name>Universidad Rey Juan Carlos</publisher-name>
</publisher>
<permissions>
<copyright-statement>Derechos de autor 2024 los autores</copyright-statement>
<copyright-year>2024</copyright-year>
<copyright-holder>los autores</copyright-holder>
<license>
<license-p>&#x00A9; 2022 Miguel Angel Ortu&#x00F1;o P&#x00E9;rez. Algunos derechos reservados. Este documento se distribuye bajo la licencia <italic>Atribuci&#x00F3;n-CompartirIgual 4.0 Internacional</italic> de Creative Commons, disponible en <ext-link ext-link-type="uri" xlink:href="https://creativecommons.org/licenses/by-sa/4.0/deed.es">https://creativecommons.org/licenses/by-sa/4.0/deed.es</ext-link></license-p>
<license-p>http://hdl.handle.net/10115/20118</license-p>
</license>
</permissions>
</book-meta>
<front-matter>
<toc id="fmatter1" content-type="toc">
<toc-title-group>
<title><target target-type="page" id="pges_5"/><bold>&#x00CD;NDICE</bold></title>
</toc-title-group>
<toc-entry content-type="chapter"><title><bold>1. INTRODUCCI&#x00D3;N A LINUX</bold></title> <nav-pointer rid="c1"><bold>11</bold></nav-pointer></toc-entry>
<toc-entry content-type="section"><title>1.1. Un poco de historia de Unix y Linux</title> <nav-pointer rid="c1-s1">11</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>1.2. &#x00BF;GNU/Linux &#x00F3; Linux?</title> <nav-pointer rid="c1-s2">11</nav-pointer></toc-entry>
<toc-entry content-type="chapter"><title><bold>2. LINUX ES SOFTWARE LIBRE</bold></title> <nav-pointer rid="c2"><bold>13</bold></nav-pointer></toc-entry>
<toc-entry content-type="section"><title>2.1. Inconvenientes del software libre para el usuario</title> <nav-pointer rid="c2-s1">14</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>2.2. Ventajas del software libre para el usuario</title> <nav-pointer rid="c2-s2">15</nav-pointer></toc-entry>
<toc-entry content-type="chapter"><title><bold>3. EL SISTEMA OPERATIVO</bold></title> <nav-pointer rid="c3"><bold>17</bold></nav-pointer></toc-entry>
<toc-entry content-type="section"><title>3.1. Procesos</title> <nav-pointer rid="c3-s1">17</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>3.2. Servicios principales de un sistema UNIX</title> <nav-pointer rid="c3-s2">17</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>3.3. Interfaces de usuario</title> <nav-pointer rid="c3-s3">18</nav-pointer></toc-entry>
<toc-entry content-type="chapter"><title><bold>4. EL ADMINISTRADOR</bold></title> <nav-pointer rid="c4"><bold>21</bold></nav-pointer></toc-entry>
<toc-entry content-type="section"><title>4.1. Procesos de root, m&#x00E9;todo tradicional</title> <nav-pointer rid="c4-s1">21</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>4.2. Procesos de root con sudo</title> <nav-pointer rid="c4-s2">22</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>4.3. Algunas ambig&#x00FC;edades</title> <nav-pointer rid="c4-s3">23</nav-pointer></toc-entry>
<toc-entry content-type="chapter"><title><bold>5. USUARIOS Y GRUPOS</bold></title> <nav-pointer rid="c5"><bold>25</bold></nav-pointer></toc-entry>
<toc-entry content-type="section"><title>5.1. Identificadores de usuario y grupo</title> <nav-pointer rid="c5-s1">25</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>5.2. Importancia de las contrase&#x00F1;as</title> <nav-pointer rid="c5-s2">25</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>5.3. Secret sharing</title> <nav-pointer rid="c5-s3">27</nav-pointer></toc-entry>
<toc-entry content-type="chapter"><title><bold>6. M&#x00C1;QUINAS VIRTUALES</bold></title> <nav-pointer rid="c6"><bold>33</bold></nav-pointer></toc-entry>
<toc-entry content-type="section"><title>6.1. Utilidad de las m&#x00E1;quinas virtuales</title> <nav-pointer rid="c6-s1">34</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>6.2. Inconvenientes de las m&#x00E1;quinas virtuales</title> <nav-pointer rid="c6-s2">34</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>6.3. Tipos de M&#x00E1;quina Virtual</title> <nav-pointer rid="c6-s3">35</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>6.3.1. MV de proceso y de sistema</title> <nav-pointer rid="c6-s3-s1">35</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>6.3.2. Emulaci&#x00F3;n Completa o Virtualizaci&#x00F3;n Completa</title> <nav-pointer rid="c6-s3-s2">35</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>6.3.3. Virtualizaci&#x00F3;n</title> <nav-pointer rid="c6-s3-s3">36</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>6.3.4. Paravirtualizaci&#x00F3;n</title> <nav-pointer rid="c6-s3-s4">37</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>6.3.5. Virtualizaci&#x00F3;n nativa</title> <nav-pointer rid="c6-s3-s5">37</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>6.4. Contenedores</title> <nav-pointer rid="c6-s4">38</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>6.4.1. Caracter&#x00ED;sticas de los contenedores</title> <nav-pointer rid="c6-s4-s1">38</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>6.4.2. Inconvenientes de los contenedores</title> <nav-pointer rid="c6-s4-s2">42</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>6.5. Otros tipos de virtualizaci&#x00F3;n</title> <nav-pointer rid="c6-s5">43</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>6.6. T&#x00E9;cnicas sin virtualizaci&#x00F3;n</title> <nav-pointer rid="c6-s6">46</nav-pointer></toc-entry>
<toc-entry content-type="chapter"><title><target target-type="page" id="pges_6"/><bold>7. CONFIGURACI&#x00D3;N DE VIRTUALBOX</bold></title> <nav-pointer rid="c7"><bold>51</bold></nav-pointer></toc-entry>
<toc-entry content-type="section"><title>7.1. Estructura de los laboratorios del GSyC</title> <nav-pointer rid="c7-s1">51</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>7.2. Im&#x00E1;genes de m&#x00E1;quinas virtuales</title> <nav-pointer rid="c7-s2">52</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>7.3. Interfaces de red en VirtualBox</title> <nav-pointer rid="c7-s3">56</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>7.4. Configuraci&#x00F3;n que seguiremos en pr&#x00E1;cticas</title> <nav-pointer rid="c7-s4">58</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>7.5. Cambio de host en el laboratorio</title> <nav-pointer rid="c7-s5">60</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>7.6. Configuraci&#x00F3;n del teclado</title> <nav-pointer rid="c7-s6">62</nav-pointer></toc-entry>
<toc-entry content-type="chapter"><title><bold>8. DOCKER</bold></title> <nav-pointer rid="c8"><bold>65</bold></nav-pointer></toc-entry>
<toc-entry content-type="section"><title>8.1. Prerrequisitos</title> <nav-pointer rid="c8-s1">65</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>8.2. Instalaci&#x00F3;n de Docker</title> <nav-pointer rid="c8-s2">66</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>8.3. Ejecuci&#x00F3;n de im&#x00E1;genes</title> <nav-pointer rid="c8-s3">67</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>8.4. Creaci&#x00F3;n de im&#x00E1;genes</title> <nav-pointer rid="c8-s4">71</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>8.5. Networking</title> <nav-pointer rid="c8-s5">78</nav-pointer></toc-entry>
<toc-entry content-type="chapter"><title><bold>9. SHELL: INT&#x00C9;RPRETE DE &#x00D3;RDENES</bold></title> <nav-pointer rid="c9"><bold>87</bold></nav-pointer></toc-entry>
<toc-entry content-type="section"><title>9.1. &#x00BF;Qui&#x00E9;n soy? &#x00BF;D&#x00F3;nde estoy? &#x00BF;Qu&#x00E9; tengo?</title> <nav-pointer rid="c9-s1">87</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>9.2. Metacaracteres de la Shell</title> <nav-pointer rid="c9-s2">88</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>9.3. Funcionamiento de la shell</title> <nav-pointer rid="c9-s3">88</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>9.4. Variables</title> <nav-pointer rid="c9-s4">90</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>9.4.1. Variables de entorno</title> <nav-pointer rid="c9-s4-s1">90</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>9.5. Ficheros</title> <nav-pointer rid="c9-s5">94</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>9.5.1. &#x00C1;rbol de directorios</title> <nav-pointer rid="c9-s5-s1">94</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>9.5.2. Permisos</title> <nav-pointer rid="c9-s5-s2">96</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>9.5.3. path</title> <nav-pointer rid="c9-s5-s3">99</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>9.6. Operaciones b&#x00E1;sicas con ficheros y directorios</title> <nav-pointer rid="c9-s6">100</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>9.7. Enlaces</title> <nav-pointer rid="c9-s7">106</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>9.8. Comandos de uso b&#x00E1;sico de la red</title> <nav-pointer rid="c9-s8">110</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>9.9. Entrada y salida</title> <nav-pointer rid="c9-s9">113</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>9.10. Programaci&#x00F3;n de Scripts</title> <nav-pointer rid="c9-s10">115</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>9.11. Filtros</title> <nav-pointer rid="c9-s11">116</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>9.12. Usos no est&#x00E1;ndar de la barra</title> <nav-pointer rid="c9-s12">119</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>9.13. Ordenes internas</title> <nav-pointer rid="c9-s13">119</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>9.14. Permisos especiales</title> <nav-pointer rid="c9-s14">121</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>9.14.1. SUID</title> <nav-pointer rid="c9-s14-s1">121</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>9.14.2. Sticky bit</title> <nav-pointer rid="c9-s14-s2">122</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>9.14.3. Umask</title> <nav-pointer rid="c9-s14-s3">123</nav-pointer></toc-entry>
<toc-entry content-type="section"><title><target target-type="page" id="pges_7"/>9.15. source</title> <nav-pointer rid="c9-s15">123</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>9.16. Invocaci&#x00F3;n de la shell</title> <nav-pointer rid="c9-s16">124</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>9.17. Manejo b&#x00E1;sico de procesos</title> <nav-pointer rid="c9-s17">126</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>9.18. Tareas</title> <nav-pointer rid="c9-s18">127</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>9.19. Tmux</title> <nav-pointer rid="c9-s19">128</nav-pointer></toc-entry>
<toc-entry content-type="chapter"><title><bold>10. COPIAS DE SEGURIDAD</bold></title> <nav-pointer rid="c10"><bold>133</bold></nav-pointer></toc-entry>
<toc-entry content-type="section"><title>10.1. rsync</title> <nav-pointer rid="c10-s1">134</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>10.2. FreeFileSync</title> <nav-pointer rid="c10-s2">135</nav-pointer></toc-entry>
<toc-entry content-type="chapter"><title><bold>11. EL LENGUAJE PYTHON</bold></title> <nav-pointer rid="c11"><bold>141</bold></nav-pointer></toc-entry>
<toc-entry content-type="section"><title>11.1. Introducci&#x00F3;n</title> <nav-pointer rid="c11-s1">141</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>11.2. El int&#x00E9;rprete de python</title> <nav-pointer rid="c11-s2">144</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>11.3. Operadores</title> <nav-pointer rid="c11-s3">145</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>11.4. Identificadores</title> <nav-pointer rid="c11-s4">146</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>11.5. Tipado</title> <nav-pointer rid="c11-s5">146</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>11.6. Declaraci&#x00F3;n de objetos</title> <nav-pointer rid="c11-s6">147</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>11.7. Funciones predefinidas</title> <nav-pointer rid="c11-s7">147</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>11.8. Tabulaci&#x00F3;n</title> <nav-pointer rid="c11-s8">148</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>11.9. Tipos de objeto</title> <nav-pointer rid="c11-s9">148</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>11.9.1. Cadenas</title> <nav-pointer rid="c11-s9-s1">150</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>11.9.2. Listas</title> <nav-pointer rid="c11-s9-s2">151</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>11.9.3. Diccionarios</title> <nav-pointer rid="c11-s9-s3">157</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>11.9.4. Tuplas</title> <nav-pointer rid="c11-s9-s4">159</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>11.10. Sentencias de control</title> <nav-pointer rid="c11-s10">160</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>11.11. format</title> <nav-pointer rid="c11-s11">163</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>11.12. Funciones</title> <nav-pointer rid="c11-s12">165</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>11.13. Cadenas de documentaci&#x00F3;n</title> <nav-pointer rid="c11-s13">170</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>11.14. Ficheros</title> <nav-pointer rid="c11-s14">172</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>11.15. Excepciones</title> <nav-pointer rid="c11-s15">173</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>11.16. Fechas</title> <nav-pointer rid="c11-s16">174</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>11.17. Cadenas binarias</title> <nav-pointer rid="c11-s17">175</nav-pointer></toc-entry>
<toc-entry content-type="chapter"><title><bold>12. LIBRER&#x00CD;AS DE PYTHON</bold></title> <nav-pointer rid="c12"><bold>177</bold></nav-pointer></toc-entry>
<toc-entry content-type="section"><title>12.1. Librer&#x00ED;a sys</title> <nav-pointer rid="c12-s1">177</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>12.2. Librer&#x00ED;a subprocess</title> <nav-pointer rid="c12-s2">177</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>12.3. Librer&#x00ED;as os, shutil</title> <nav-pointer rid="c12-s3">179</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>12.4. Librer&#x00ED;as pickle: Persistencia</title> <nav-pointer rid="c12-s4">181</nav-pointer></toc-entry>
<toc-entry content-type="section"><title><target target-type="page" id="pges_8"/>12.5. Acceso a las variables de entorno</title> <nav-pointer rid="c12-s5">183</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>12.6. M&#x00F3;dulos</title> <nav-pointer rid="c12-s6">183</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>12.7. optparse</title> <nav-pointer rid="c12-s7">186</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>12.8. Bots de telegram</title> <nav-pointer rid="c12-s8">189</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>12.8.1. Creaci&#x00F3;n de un bot de telegram</title> <nav-pointer rid="c12-s8-s1">189</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>12.8.2. Uso de la librer&#x00ED;a telepot</title> <nav-pointer rid="c12-s8-s2">190</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>12.9. Expresiones Regulares en python</title> <nav-pointer rid="c12-s9">192</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>12.9.1. Introducci&#x00F3;n</title> <nav-pointer rid="c12-s9-s1">192</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>12.9.2. Metacaracteres</title> <nav-pointer rid="c12-s9-s2">194</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>12.9.3. Regexp en python</title> <nav-pointer rid="c12-s9-s3">197</nav-pointer></toc-entry>
<toc-entry content-type="chapter"><title><bold>13. SISTEMAS DE FICHEROS</bold></title> <nav-pointer rid="c13"><bold>205</bold></nav-pointer></toc-entry>
<toc-entry content-type="section"><title>13.1. Estructura del sistemas de fichero</title> <nav-pointer rid="c13-s1">205</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>13.2. FHS Filesystem Hierarchy Standard</title> <nav-pointer rid="c13-s2">207</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>13.2.1. Directorios de usuarios</title> <nav-pointer rid="c13-s2-s1">207</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>13.2.2. Programas y mandatos</title> <nav-pointer rid="c13-s2-s2">208</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>13.2.3. Configuraci&#x00F3;n del sistema</title> <nav-pointer rid="c13-s2-s3">209</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>13.2.4. El Hardware</title> <nav-pointer rid="c13-s2-s4">209</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>13.2.5. Documentaci&#x00F3;n</title> <nav-pointer rid="c13-s2-s5">210</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>13.2.6. Ficheros Temporales</title> <nav-pointer rid="c13-s2-s6">210</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>13.2.7. Otros directorios relacionados con el S.O.</title> <nav-pointer rid="c13-s2-s7">211</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>13.2.8. Puntos de Montaje</title> <nav-pointer rid="c13-s2-s8">211</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>13.3. Montaje de sistemas de ficheros</title> <nav-pointer rid="c13-s3">212</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>13.3.1. mount, df, lsblk</title> <nav-pointer rid="c13-s3-s1">212</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>13.3.2. El fichero /etc/fstab</title> <nav-pointer rid="c13-s3-s2">213</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>13.3.3. Tipos de sistemas de ficheros</title> <nav-pointer rid="c13-s3-s3">214</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>13.3.4. Sistemas de Ficheros en Espacio de usuario</title> <nav-pointer rid="c13-s3-s4">215</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>13.3.5. frametitle</title> <nav-pointer rid="c13-s3-s5">216</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>13.3.6. sshfs</title> <nav-pointer rid="c13-s3-s6">216</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>13.4. Codificaci&#x00F3;n de caracteres</title> <nav-pointer rid="c13-s4">217</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>13.4.1. Codificaciones cl&#x00E1;sicas</title> <nav-pointer rid="c13-s4-s1">218</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>13.4.2. ASCII extendido</title> <nav-pointer rid="c13-s4-s2">218</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>13.4.3. Unicode</title> <nav-pointer rid="c13-s4-s3">218</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>13.4.4. recode</title> <nav-pointer rid="c13-s4-s4">219</nav-pointer></toc-entry>
<toc-entry content-type="chapter"><title><bold>14. DEVOPS</bold></title> <nav-pointer rid="c14"><bold>221</bold></nav-pointer></toc-entry>
<toc-entry content-type="section"><title>14.1. DevOps</title> <nav-pointer rid="c14-s1">221</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>14.2. Desarrollo &#x00C1;gil</title> <nav-pointer rid="c14-s2">224</nav-pointer></toc-entry>
<toc-entry content-type="section"><title><target target-type="page" id="pges_9"/>14.3. Problemas habituales</title> <nav-pointer rid="c14-s3">229</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>14.4. Aspectos humanos</title> <nav-pointer rid="c14-s4">231</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>14.5. Aspectos t&#x00E9;cnicos</title> <nav-pointer rid="c14-s5">233</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>14.6. Herramientas</title> <nav-pointer rid="c14-s6">236</nav-pointer></toc-entry>
<toc-entry content-type="chapter"><title><bold>15. ADMINISTRACI&#x00D3;N DE SERVICIOS</bold></title> <nav-pointer rid="c15"><bold>243</bold></nav-pointer></toc-entry>
<toc-entry content-type="section"><title>15.1. Empaquetado de ficheros</title> <nav-pointer rid="c15-s1">243</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>15.2. Instalaci&#x00F3;n de paquetes</title> <nav-pointer rid="c15-s2">245</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>15.2.1. El sistema de paquetes de Debian</title> <nav-pointer rid="c15-s2-s1">246</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>15.2.2. dpkg</title> <nav-pointer rid="c15-s2-s2">246</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>15.2.3. apt</title> <nav-pointer rid="c15-s2-s3">247</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>15.2.4. Sistemas de paquetes en macOS</title> <nav-pointer rid="c15-s2-s4">249</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>15.2.5. Paquetes Snap</title> <nav-pointer rid="c15-s2-s5">250</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>15.3. B&#x00FA;squeda de ficheros</title> <nav-pointer rid="c15-s3">252</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>15.4. Hora. Parada del sistema</title> <nav-pointer rid="c15-s4">252</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>15.5. Copias de seguridad</title> <nav-pointer rid="c15-s5">253</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>15.6. Administraci&#x00F3;n de los demonios</title> <nav-pointer rid="c15-s6">254</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>15.6.1. Systemd</title> <nav-pointer rid="c15-s6-s1">255</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>15.6.2. Niveles de ejecuci&#x00F3;n</title> <nav-pointer rid="c15-s6-s2">257</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>15.7. Consulta de logs</title> <nav-pointer rid="c15-s7">260</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>15.8. Tareas peri&#x00F3;dicas: cron</title> <nav-pointer rid="c15-s8">261</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>15.8.1. Tareas peri&#x00F3;dicas</title> <nav-pointer rid="c15-s8-s1">261</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>15.8.2. Tabla de cron</title> <nav-pointer rid="c15-s8-s2">262</nav-pointer></toc-entry>
<toc-entry content-type="chapter"><title><bold>16. OPENSSH</bold></title> <nav-pointer rid="c16"><bold>267</bold></nav-pointer></toc-entry>
<toc-entry content-type="section"><title>16.1. Criptograf&#x00ED;a de clave p&#x00FA;blica</title> <nav-pointer rid="c16-s1">267</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>16.2. Uso de OpenSSH</title> <nav-pointer rid="c16-s2">268</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>16.3. configuraci&#x00F3;n de ssh</title> <nav-pointer rid="c16-s3">274</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>16.4. sshfs</title> <nav-pointer rid="c16-s4">275</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>16.5. T&#x00FA;neles con SSH</title> <nav-pointer rid="c16-s5">277</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>16.5.1. T&#x00FA;nel local</title> <nav-pointer rid="c16-s5-s1">278</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>16.5.2. T&#x00FA;nel remoto</title> <nav-pointer rid="c16-s5-s2">282</nav-pointer></toc-entry>
<toc-entry content-type="chapter"><title><bold>17. CONTROL DE INTEGRIDAD</bold></title> <nav-pointer rid="c17"><bold>289</bold></nav-pointer></toc-entry>
<toc-entry content-type="section"><title>17.1. Funci&#x00F3;n hash</title> <nav-pointer rid="c17-s1">289</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>17.2. cmp</title> <nav-pointer rid="c17-s2">290</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>17.3. diff</title> <nav-pointer rid="c17-s3">290</nav-pointer></toc-entry>
<toc-entry content-type="chapter"><title><target target-type="page" id="pges_10"/><bold>18. BENCHMARK</bold></title> <nav-pointer rid="c18"><bold>293</bold></nav-pointer></toc-entry>
<toc-entry content-type="section"><title>18.1. Benchmark de la cpu</title> <nav-pointer rid="c18-s1">293</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>18.2. Benchmark de red</title> <nav-pointer rid="c18-s2">293</nav-pointer></toc-entry>
<toc-entry content-type="chapter"><title><bold>19. SESIONES GR&#x00C1;FICAS REMOTAS</bold></title> <nav-pointer rid="c19"><bold>297</bold></nav-pointer></toc-entry>
<toc-entry content-type="section"><title>19.1. Introducci&#x00F3;n</title> <nav-pointer rid="c19-s1">297</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>19.2. Protocolos para sesiones gr&#x00E1;ficas remotas (1)</title> <nav-pointer rid="c19-s2">297</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>19.3. Protocolos para sesiones gr&#x00E1;ficas remotas (2)</title> <nav-pointer rid="c19-s3">298</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>19.4. X11 Forwarding</title> <nav-pointer rid="c19-s4">298</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>19.5. VNC</title> <nav-pointer rid="c19-s5">299</nav-pointer></toc-entry>
<toc-entry content-type="chapter"><title><bold>20. NETSTAT</bold></title> <nav-pointer rid="c20"><bold>307</bold></nav-pointer></toc-entry>
<toc-entry content-type="chapter"><title><bold>21. EDITORES DE TEXTO</bold></title> <nav-pointer rid="c21"><bold>311</bold></nav-pointer></toc-entry>
<toc-entry content-type="section"><title>21.1. Introducci&#x00F3;n</title> <nav-pointer rid="c21-s1">311</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>21.2. vi</title> <nav-pointer rid="c21-s2">312</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>21.2.1. Modos de vi</title> <nav-pointer rid="c21-s2-s1">314</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>21.2.2. &#x00D3;rdenes imprescindibles</title> <nav-pointer rid="c21-s2-s2">314</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>21.2.3. &#x00D3;rdenes b&#x00E1;sicas</title> <nav-pointer rid="c21-s2-s3">315</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>21.2.4. Otras &#x00F3;rdenes</title> <nav-pointer rid="c21-s2-s4">316</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>21.3. Editores ligeros</title> <nav-pointer rid="c21-s3">317</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>21.4. Emacs / XEmacs</title> <nav-pointer rid="c21-s4">317</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>21.5. Otros editores</title> <nav-pointer rid="c21-s5">320</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>21.5.1. Atom</title> <nav-pointer rid="c21-s5-s1">320</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>21.5.2. gedit</title> <nav-pointer rid="c21-s5-s2">321</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>21.5.3. SciTE</title> <nav-pointer rid="c21-s5-s3">321</nav-pointer></toc-entry>
<toc-entry content-type="subsection"><title>21.5.4. Kate</title> <nav-pointer rid="c21-s5-s4">322</nav-pointer></toc-entry>
<toc-entry content-type="chapter"><title><bold>22. MARKDOWN</bold></title> <nav-pointer rid="c22"><bold>325</bold></nav-pointer></toc-entry>
<toc-entry content-type="section"><title>22.1. Introducci&#x00F3;n</title> <nav-pointer rid="c22-s1">325</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>22.2. Herramientas para Markdown</title> <nav-pointer rid="c22-s2">325</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>22.3. P&#x00E1;rrafos</title> <nav-pointer rid="c22-s3">326</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>22.4. Secciones en el texto</title> <nav-pointer rid="c22-s4">326</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>22.5. Cursiva, negrita, enlaces</title> <nav-pointer rid="c22-s5">327</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>22.6. Im&#x00E1;genes</title> <nav-pointer rid="c22-s6">327</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>22.7. Listas</title> <nav-pointer rid="c22-s7">328</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>22.8. C&#x00F3;digo</title> <nav-pointer rid="c22-s8">329</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>22.9. Tablas</title> <nav-pointer rid="c22-s9">329</nav-pointer></toc-entry>
<toc-entry content-type="section"><title>22.10. Generaci&#x00F3;n de HTML</title> <nav-pointer rid="c22-s10">329</nav-pointer></toc-entry>
</toc>
</front-matter>
<book-body>
<book-part id="c1" book-part-type="chapter">
<book-part-meta>
<book-part-id book-part-id-type="publisher-id">URJC</book-part-id>
<title-group>
<label>1.</label>
<title><target target-type="page" id="pges_11"/>INTRODUCCI&#x00D3;N A LINUX</title>
</title-group>
</book-part-meta>
<body>
<sec id="c1-s1">
<label><bold>1.1.</bold></label>
<title><bold>Un poco de historia de Unix y Linux</bold></title>
<sec id="c1-s1-1">
<title><bold>Un poco de historia de Unix y Linux</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>UNIX surgi&#x00F3; en 1969 en los Laboratorios Bell (Ken Thomson, Dennis Ritchie)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Dos grandes vertientes</p>
<list list-type="bullet">
<list-item><p>BSD: SunOS, NetBSD, OpenBSD, Mac OS</p></list-item>
<list-item><p>System V: Solaris, Iris, Aix, Linux (a&#x00F1;o 1991) Distribuciones Linux</p>
<list list-type="simple">
<list-item><label>&#x25CB;</label> <p>Slackware</p></list-item>
<list-item><label>&#x25CB;</label> <p>Gentoo</p></list-item>
<list-item><label>&#x25CB;</label> <p>Suse</p></list-item>
<list-item><label>&#x25CB;</label> <p>RedHat y derivados: Fedora, Mandriva (Mandrake)</p></list-item>
<list-item><label>&#x25CB;</label> <p>Debian y derivados: Ubuntu, knoppix, GnuLiNex, guadalinex</p></list-item>
</list></list-item>
</list></list-item>
</list>
</sec>
<sec id="c1-s1-2">
<title><bold>Partes de un sistema operativo</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Kernel (N&#x00FA;cleo): elemento m&#x00E1;s importante. Permite que las aplicaciones accedan al hardware. Es responsable de la gesti&#x00F3;n de recursos, seguridad, etc</p></list-item>
<list-item><label>&#x25FE;</label> <p>Procesos de usuario: distintos programas ejecut&#x00E1;ndose concurrentemente en un sistema</p></list-item>
<list-item><label>&#x25FE;</label> <p>La interacci&#x00F3;n entre el n&#x00FA;cleo y los procesos se hace mediante llamadas al sistema (<italic>system calls</italic>)</p></list-item>
</list>
<p>La shell es un interfaz de usuario en modo texto. Es una aplicaci&#x00F3;n como otra cualquiera</p>
</sec></sec>
<sec id="c1-s2">
<label><bold>1.2.</bold></label>
<title><bold>&#x00BF;GNU/Linux &#x00F3; Linux?</bold></title>
<sec id="c1-s2-1">
<title><bold>&#x00BF;GNU/Linux &#x00F3; Linux?</bold></title>
<list list-type="order">
<list-item><p>La Free Software Foundation (Richard Stallman) considera que:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Linux es estrictamente el kernel</p>
<p><fig id="fig1">
<label><bold>Figura 1:</bold></label>
<caption><title>El Sistema Operativo</title></caption>
<graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-1.jpg"/>
</fig></p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_12"/>Los procesos de usuario (<italic>programas y otras utilidades b&#x00E1;sicas para el sistema</italic>) provienen del proyecto GNU (y algunos otros).</p></list-item>
<list-item><label>&#x25FE;</label> <p>Al conjunto se le debe llamar GNU/Linux.</p></list-item>
</list></list-item>
<list-item><p>Un n&#x00FA;mero importante de personas y organismos se oponen a esta definici&#x00F3;n. La mayor&#x00ED;a de la gente lo llama simplemente Linux</p></list-item>
</list>
</sec></sec>
</body>
</book-part>
<book-part id="c2" book-part-type="chapter">
<book-part-meta>
<book-part-id book-part-id-type="publisher-id">URJC</book-part-id>
<title-group>
<label>2.</label>
<title><target target-type="page" id="pges_13"/>LINUX ES SOFTWARE LIBRE</title>
</title-group>
</book-part-meta>
<body>
<sec id="c2-1">
<title><bold>Linux es Software Libre</bold></title>
<p>Linux es el <italic>producto estrella</italic> del Soft. Libre</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Hay software libre para cualquier S.O.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Hay software propietario para Linux</p></list-item>
</list>
<p>Cuatro libertades. Quien lo recibe tiene:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>libertad de uso. Usarlo como quiera, donde quiera</p></list-item>
<list-item><label>&#x25FE;</label> <p>libertad de redistribuci&#x00F3;n. Redistribuirlo a quien quiera, como quiera</p></list-item>
<list-item><label>&#x25FE;</label> <p>libertad de modificaci&#x00F3;n. Modificar, adaptar, corregir, mejorar</p></list-item>
<list-item><label>&#x25FE;</label> <p>libertad de distribuir las modificaciones</p></list-item>
</list>
<p>Imprescindible: disponibilidad de c&#x00F3;digo fuente.</p>
<p>Como cualquier modelo, puede ser criticado. Desarrollar software libre no es ni garant&#x00ED;a de calidad ni garant&#x00ED;a de &#x00E9;xito. Pero algunos argumentos en contra habituales no tienen ning&#x00FA;n sentido:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><italic>Los m&#x00E9;dicos, los abogados y los fontaneros no trabajan gratis. &#x00BF;Por qu&#x00E9; habr&#x00ED;an de hacerlo los programadores?</italic></p></list-item>
</list>
<disp-quote>
<p><bold>software libre &#x2260; software gratis</bold></p>
<p><bold>desarrollar software libre &#x2260; no cobrar</bold></p>
</disp-quote>
<p>&#x00BF;Qu&#x00E9; no es software libre?</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Software gratuito</p></list-item>
<list-item><label>&#x25FE;</label> <p>Shareware</p></list-item>
<list-item><label>&#x25FE;</label> <p>Adware</p></list-item>
<list-item><label>&#x25FE;</label> <p>Versiones de evaluaci&#x00F3;n</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_14"/>Dominio P&#x00FA;blico</p></list-item>
</list>
<p>Tipos de licencias libres:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Minimalistas. Permiten <italic>cerrar</italic> el c&#x00F3;digo. Pj BSD</p></list-item>
<list-item><label>&#x25FE;</label> <p><italic>Protectoras de la libertad</italic>. GPL.</p>
<p>Redistribuciones con mismos derechos que la primera distribuci&#x00F3;n</p></list-item>
</list>
</sec>
<sec id="c2-2">
<title><bold>Motivos para desarrollar software libre</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>&#x00C9;tica, satisfacci&#x00F3;n personal, pertenencia a una comunidad</p></list-item>
<list-item><label>&#x25FE;</label> <p>Aprendizaje</p></list-item>
<list-item><label>&#x25FE;</label> <p>Tesis doctorales, TFG</p></list-item>
<list-item><label>&#x25FE;</label> <p>Empresas que se dedican a otra cosa</p></list-item>
<list-item><label>&#x25FE;</label> <p>Organismos p&#x00FA;blicos</p></list-item>
<list-item><label>&#x25FE;</label> <p>Empresas que obtienen dinero por servicios</p></list-item>
<list-item><label>&#x25FE;</label> <p>Empresas de Hardware</p></list-item>
<list-item><label>&#x25FE;</label> <p>etc etc</p></list-item>
</list>
</sec>
<sec id="c2-s1">
<label><bold>2.1.</bold></label>
<title><bold>Inconvenientes del software libre para el usuario</bold></title>
<sec id="c2-s1-1">
<title><bold>Inconvenientes del software libre para el usuario</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Ninguno. El modelo de software libre no tiene, por s&#x00ED; mismo, ning&#x00FA;n inconveniente para el usuario</p></list-item>
</list>
<p>Si bien habr&#x00E1; ocasiones, cada vez menos, en que una soluci&#x00F3;n de software propietario resulta m&#x00E1;s apropiada:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Software libre inexistente o de calida insuficiente</p></list-item>
<list-item><label>&#x25FE;</label> <p>Hardware no soportado</p></list-item>
<list-item><label>&#x25FE;</label> <p>Soporte insuficiente</p></list-item>
<list-item><label>&#x25FE;</label> <p>Otros (discutible) <italic>Quien me rodea usa determinado software</italic></p></list-item>
</list>
<p><target target-type="page" id="pges_15"/>Es muy habitual usar soluciones propietarias <italic>por inercia</italic>, un peque&#x00F1;o esfuerzo en explorar las alternativas libres puede ser extraordinariamente rentable</p>
</sec>
</sec>
<sec id="c2-s2">
<label><bold>2.2.</bold></label>
<title><bold>Ventajas del software libre para el usuario</bold></title>
<sec id="c2-s2-1">
<title><bold>Ventajas del software libre para el usuario</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>4 libertades</p></list-item>
<list-item><label>&#x25FE;</label> <p>Facilita la reutilizaci&#x00F3;n</p></list-item>
<list-item><label>&#x25FE;</label> <p>Mucho menor coste</p></list-item>
<list-item><label>&#x25FE;</label> <p>Nadie impone la renovaci&#x00F3;n de Hw, Sw ni formaci&#x00F3;n de usuarios</p></list-item>
<list-item><label>&#x25FE;</label> <p>Mejor interoperabilidad y escalabilidad</p></list-item>
<list-item><label>&#x25FE;</label> <p>Garant&#x00ED;a de privacidad</p></list-item>
<list-item><label>&#x25FE;</label> <p>Permite conocer mejor el software y comprobar su calidad</p></list-item>
<list-item><label>&#x25FE;</label> <p>Igualdad de oportunidades: Mismas herramientas para todos. Promoci&#x00F3;n de econom&#x00ED;a local</p></list-item>
</list>
<p>M&#x00E1;s informaci&#x00F3;n: Estudio FLOSSImpact<target target-type="page" id="pges_16"/></p>
</sec>
</sec>
</body>
</book-part>
<book-part id="c3" book-part-type="chapter">
<book-part-meta>
<book-part-id book-part-id-type="publisher-id">URJC</book-part-id>
<title-group>
<label>3.</label>
<title><target target-type="page" id="pges_17"/>EL SISTEMA OPERATIVO</title>
</title-group>
</book-part-meta>
<body>
<sec id="c3-s1">
<label><bold>3.1.</bold></label>
<title><bold>Procesos</bold></title>
<sec id="c3-s1-1">
<title><bold>El Sistema Operativo se ocupa de:</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Gesti&#x00F3;n de procesos</p></list-item>
<list-item><label>&#x25FE;</label> <p>Gesti&#x00F3;n de memoria</p></list-item>
<list-item><label>&#x25FE;</label> <p>Gesti&#x00F3;n de dispositivos</p></list-item>
<list-item><label>&#x25FE;</label> <p>Gesti&#x00F3;n de sistemas de ficheros</p></list-item>
<list-item><label>&#x25FE;</label> <p>Gesti&#x00F3;n de red</p></list-item>
<list-item><label>&#x25FE;</label> <p>Procesos = ejecutables + librer&#x00ED;as din&#x00E1;micas</p></list-item>
<list-item><label>&#x25FE;</label> <p>Identificadores asociados a cada proceso:</p>
<list list-type="bullet">
<list-item><p>PID: Identificaci&#x00F3;n &#x00FA;nica de cada proceso</p></list-item>
<list-item><p>UID: Identificaci&#x00F3;n de usuario</p></list-item>
<list-item><p>GID: Identificaci&#x00F3;n de grupo (posibilidad de varios grupos por proceso)</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>uid=0 &#x21D2; <italic>super-usuario</italic>, &#x201C;root&#x201D;:</p>
<list list-type="bullet">
<list-item><p>Control sobre el resto de procesos</p></list-item>
<list-item><p>Permiso para acceder a todos los ficheros</p></list-item>
<list-item><p>Posibilidad de realizar ciertas tareas privilegiadas</p></list-item>
</list></list-item>
</list>
</sec>
</sec>
<sec id="c3-s2">
<label><bold>3.2.</bold></label>
<title><bold>Servicios principales de un sistema UNIX</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>init. Primer proceso, padre de todos los dem&#x00E1;s. Se encarga de arrancar y parar el sistema.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Terminales remotas: login y logout</p></list-item>
<list-item><label>&#x25FE;</label> <p>syslog</p></list-item>
<list-item><label>&#x25FE;</label> <p>Ejecuci&#x00F3;n peri&#x00F3;dica de &#x00F3;rdenes: cron y at</p></list-item>
<list-item><label>&#x25FE;</label> <p>Entorno gr&#x00E1;fico (X Window)</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_18"/>Entorno de red (demonios)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Correo electr&#x00F3;nico, sistema de impresi&#x00F3;n, . . .</p></list-item>
</list>
</sec>
<sec id="c3-s3">
<label><bold>3.3.</bold></label>
<title><bold>Interfaces de usuario</bold></title>
<sec id="c3-s3-1">
<title><bold>Interfaz gr&#x00E1;fico</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>En los a&#x00F1;os 1980 y 1990, la aparici&#x00F3;n de los interfaces gr&#x00E1;ficos supuso un gran avance, al facilitar el uso de los ordenadores, especialmente para usuarios no especializados. Tambi&#x00E9;n para tareas que se hacen eventualmente</p></list-item>
<list-item><label>&#x25FE;</label> <p>Para tareas exigentes, son mucho menos eficiente que los interfaces de texto: obligan a hacer las cosas <italic>a mano</italic> y de una en una</p></list-item>
<list-item><label>&#x25FE;</label> <p>Solo se puede hacer lo que el interfaz haya previsto que se haga</p></list-item>
<list-item><label>&#x25FE;</label> <p>No es la filosof&#x00ED;a Unix, no son est&#x00E1;ndar</p></list-item>
<list-item><label>&#x25FE;</label> <p>Exigen sesi&#x00F3;n gr&#x00E1;fica (mucho m&#x00E1;s caro que pj ssh)</p></list-item>
<list-item><label>&#x25FE;</label> <p>No siempre disponibles (sistemas empotrados, routers, etc)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Hay gestores gr&#x00E1;ficos, pero no ser&#x00E1;n v&#x00E1;lidos en esta asignatura</p></list-item>
</list>
<p>Unix dispone de interfaz gr&#x00E1;fico desde los 80: <italic>X Window</italic>, a&#x00F1;o 1984 (No confundir con Microsoft Windows).</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>X Window System es un sistema gr&#x00E1;fico utilizado fundamentalmente en sistemas Unix, aunque es multiplataforma. Tambi&#x00E9;n llamado X11, est&#x00E1; desarrollado por la fundaci&#x00F3;n X.org</p></list-item>
<list-item><label>&#x25FE;</label> <p>Proporciona un mecanismo para mostrar ventanas gr&#x00E1;ficas basado en dos partes: cliente y servidor</p>
<list list-type="bullet">
<list-item><p>Servidor X: Se ejectuta t&#x00ED;picamente en la m&#x00E1;quina en la que est&#x00E1; sentado el usuario.</p></list-item>
<list-item><p>Clientes X: Aplicaciones que producen una salida gr&#x00E1;fica que env&#x00ED;an al Servidor X para que la presente en pantalla. Pueden ejecutarse en ordenadores remotos.</p></list-item>
</list></list-item>
</list>
<p><target target-type="page" id="pges_19"/>Es un sistema complejo, que requiere componentes adicionales:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Sobre las X Window va el <italic>gestor de ventanas</italic> ( Kwin, Enlightenment, Metacity, Xfwm, MWM...)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Sobre el gestor de ventanas, va el <italic>escritorio</italic> (KDE, Gnome, Xfce...)</p></list-item>
</list>
<p>Desde finales de la d&#x00E9;cada de 2010, se empieza a popularizar <italic>wayland</italic> como alternativa a X Window</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Es m&#x00E1;s moderno, ligero y seguro</p></list-item>
<list-item><label>&#x25FE;</label> <p>Tambi&#x00E9;n sigue la arquitectura cliente-servidor</p></list-item>
<list-item><label>&#x25FE;</label> <p>No usa gestores de ventanas sino unos elementos similares llamados <italic>compositors</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>Los escritorios (y las aplicaciones) son los mismos que con X Window, el usuario final no percibe la diferencia</p></list-item>
</list>
</sec>
<sec id="c3-s3-2">
<title><bold>Interfaz de texto: consola</bold></title>
<p>El interfaz de texto resulta m&#x00E1;s dificil de aprender a manejar, pero resulta mucho m&#x00E1;s flexible y potente</p>
<p><italic>Write programs that do one thing and do it well. Write programs to work together. Write programs that handle text streams, because that is a universal.</italic></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>interfaz texto: teclado</p>
<list list-type="bullet">
<list-item><p>terminales x</p></list-item>
<list-item><p>consola: terminales virtuales (Ctrl+Alt+F1) (Ctrl+Alt+F6)</p></list-item>
<list-item><p>Vuelta a sesi&#x00F3;n X (Ctrl+Alt+F7)</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>exit (EOF, Ctrl + D)</p></list-item>
</list>
<p>En MS Windows el interfaz de consola para la administraci&#x00F3;n es una opci&#x00F3;n viable desde la aparici&#x00F3;n en 2006 de PowerShell<target target-type="page" id="pges_20"/></p>
</sec>
</sec>
</body>
</book-part>
<book-part id="c4" book-part-type="chapter">
<book-part-meta>
<book-part-id book-part-id-type="publisher-id">URJC</book-part-id>
<title-group>
<label>4.</label>
<title><target target-type="page" id="pges_21"/>EL ADMINISTRADOR</title>
</title-group>
</book-part-meta>
<body>
<sec id="c4-1">
<title><bold>El administrador</bold></title>
<p>Persona responsable de:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Instalaci&#x00F3;n del sistema</p></list-item>
<list-item><label>&#x25FE;</label> <p>Incorporar nuevo hardware y software</p></list-item>
<list-item><label>&#x25FE;</label> <p>A&#x00F1;adir y eliminar usuarios</p></list-item>
<list-item><label>&#x25FE;</label> <p>Salvaguarda de informaci&#x00F3;n</p></list-item>
<list-item><label>&#x25FE;</label> <p>Seguimiento y monitorizaci&#x00F3;n del sistema</p></list-item>
<list-item><label>&#x25FE;</label> <p>Pol&#x00ED;tica de seguridad</p></list-item>
<list-item><label>&#x25FE;</label> <p>Resoluci&#x00F3;n de problemas</p></list-item>
<list-item><label>&#x25FE;</label> <p>Soporte t&#x00E9;cnico</p></list-item>
</list>
<p>Hay tareas que solo el usuario root puede hacer. Entre otras:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Editar ficheros de configuraci&#x00F3;n de la m&#x00E1;quina, como los interfaces de red, sistemas de ficheros, arranque del sistema...</p></list-item>
<list-item><label>&#x25FE;</label> <p>Iniciar y detener demonios (en los casos habituales, aunque excepcionalmente un usuario ordinario podr&#x00ED;a gestionar alg&#x00FA;n demonio)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Instalar paquetes de software (para toda la m&#x00E1;quina)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Crear, modificar, eliminar cuentas de usuario</p></list-item>
</list>
</sec>
<sec id="c4-s1">
<label><bold>4.1.</bold></label>
<title><bold>Procesos de root, m&#x00E9;todo tradicional</bold></title>
<sec id="c4-s1-1">
<title><bold>Procesos de root, m&#x00E9;todo tradicional</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Por motivos de seguridad, el administrador deber&#x00ED;a lanzar procesos como root solo para lo que sea imprescindible, el tiempo imprescindible. El resto del tiempo, es preferible trabajar con una cuenta de usuario ordinario</p></list-item>
</list>
<p><target target-type="page" id="pges_22"/>Formas tradicionales de lanzar procesos como root</p>
<list list-type="order">
<list-item><p>Iniciar sesi&#x00F3;n con usuario root y contrase&#x00F1;a de root (no recomendable, muchas veces no permitido)</p></list-item>
<list-item><p>su</p>
<p>Solicita la contrase&#x00F1;a de root y ejecuta una shell como root. Todos los procesos lanzados ser&#x00E1;n de root, hasta que se cierre la shell</p></list-item>
</list>
</sec>
</sec>
<sec id="c4-s2">
<label><bold>4.2.</bold></label>
<title><bold>Procesos de root con sudo</bold></title>
<sec id="c4-s2-1">
<title><bold>Procesos de root con sudo</bold></title>
<p>Para mejorar la seguridad, aparece sudo, que evita que haya una sesi&#x00F3;n de root siempre abierta</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>sudo &#x003C;orden&#x003E;</code></p>
<p>Permite ejecutar una &#x00FA;nica &#x00F3;rden como root, desde una sesi&#x00F3;n de usuario ordinario</p></list-item>
<list-item><label>&#x25FE;</label> <p>sudo no pregunta la contrase&#x00F1;a de root, no es necesario conocerla (de hecho, en los sistemas con sudo, el usuario root no suele tener contrase&#x00F1;a)</p></list-item>
<list-item><label>&#x25FE;</label> <p>sudo solicita la contrase&#x00F1;a del propio usuario que est&#x00E1; lanzando sudo</p>
<list list-type="bullet">
<list-item><p>Para llamarle la atenci&#x00F3;n y hacerle tener m&#x00E1;s cuidado</p></list-item>
<list-item><p>Para verificar que la persona que est&#x00E1; en el terminal sigue siendo quien ha abierto la sesi&#x00F3;n</p></list-item>
</list></list-item>
</list>
<p>Solamente algunos usuarios pueden ejecutar sudo:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Aquellos indicados en el fichero <code>/etc/sudoers</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>Por omisi&#x00F3;n, el usuario que instala la m&#x00E1;quina est&#x00E1; en <code>/etc/sudoers</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>En ubuntu, se incluye en <code>/etc/sudoers</code> a todos los usuarios del grupo <italic>admin</italic></p></list-item>
</list>
<p>Por todo esto, <code>sudo</code> es m&#x00E1;s seguro que su, aunque</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>en algunos sistemas, <code>sudo</code> no est&#x00E1; instalado y no se puede usar</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_23"/>en algunos sistemas, el uso de <code>sudo</code> es opcional</p></list-item>
<list-item><label>&#x25FE;</label> <p>en algunos sistemas, como Ubuntu, el uso de <code>sudo</code> es obligatorio, los usuarios no pueden ejecutar <code>su</code></p>
<list list-type="bullet">
<list-item><p>Pero si vamos a ejecutar muchas &#x00F3;rdenes como root y sudo nos resulta inc&#x00F3;modo, podemos hacer una peque&#x00F1;a <italic>trampa</italic>:</p>
<p><code>sudo su</code></p></list-item>
</list></list-item>
</list>
<p>Para aplicaciones gr&#x00E1;ficas, no debe usarse <code>sudo</code> sino <code>gksudo</code></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>gksudo</code> <italic>mi-aplicacion</italic></p></list-item>
</list>
</sec>
<sec id="c4-s2-2">
<title><bold>sudo y redirecciones</bold></title>
<p>La orden <italic>sudo</italic> por omisi&#x00F3;n no incluye las posibles redirecciones</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>sudo echo hola &#x003E; /tmp/aa</code></p>
<p>El proceso <italic>echo</italic> se lanza con la identidad del root (id 0), pero la redirecci&#x00F3;n la ejecuta el usuario ordinario</p></list-item>
<list-item><label>&#x25FE;</label> <p>Para poder usar redirecciones, ejecutamos una subshell con el par&#x00E1;metro -c</p>
<p><code>sudo bash -c &#x0022;echo hola&#x003E;aa&#x0022;</code></p>
<p><code>sudo bash -c &#x0022;find /root | grep prueba &#x0022;</code></p>
<p>(o, alternativamente, sudo su)</p></list-item>
</list>
</sec>
</sec>
<sec id="c4-s3">
<label><bold>4.3.</bold></label>
<title><bold>Algunas ambig&#x00FC;edades</bold></title>
<sec id="c4-s3-1">
<title><bold>Algunas ambig&#x00FC;edades</bold></title>
<list list-type="order">
<list-item><p>En el lenguaje oral, la palabra <italic>root</italic> a veces provoca ambig&#x00FC;edades, podemos referirnos a</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>El usuario <italic>root</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>El directorio <italic>home</italic> del usuario <italic>root</italic> :</p>
<p><code>/root</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>El punto del que cuelga el sistema de ficheros:</p>
<p><code>/.</code></p></list-item>
</list>
<p>Por el contexto se distingue f&#x00E1;cilmente</p></list-item>
<list-item><p><target target-type="page" id="pges_24"/>La palabra <italic>fichero</italic> tambi&#x00E9;n puede tener distintos significados</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>El sentido habitual en inform&#x00E1;tica (fichero ordinario)</p></list-item>
<list-item><label>&#x25FE;</label> <p>En sentido Unix, incluye ficheros ordinarios, directorios, pipes y ficheros especiales (dispositivos)</p></list-item>
</list></list-item>
</list>
</sec>
</sec>
</body>
</book-part>
<book-part id="c5" book-part-type="chapter">
<book-part-meta>
<book-part-id book-part-id-type="publisher-id">URJC</book-part-id>
<title-group>
<label>5.</label>
<title><target target-type="page" id="pges_25"/>USUARIOS Y GRUPOS</title>
</title-group>
</book-part-meta>
<body>
<sec id="c5-s1">
<label><bold>5.1.</bold></label>
<title><bold>Identificadores de usuario y grupo</bold></title>
<sec id="c5-s1-1">
<title><bold>Usuarios y grupos</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Cada usuario tiene un identificador (<italic>UID</italic>), un grupo principal (o primario) al que pertenece (<italic>GID</italic>), una serie de grupos adicionales, un nombre de usuario (<italic>login</italic>), un directorio de trabajo (<italic>home</italic>)</p></list-item>
<list-item><label>&#x25FE;</label> <p>La orden <code>id</code> nos da el UID, el GID y los grupos adicionales de un usuario</p></list-item>
<list-item><label>&#x25FE;</label> <p>Cada usuario puede tener dos tipos de recursos en un sistema UNIX: Procesos y Ficheros</p></list-item>
<list-item><label>&#x25FE;</label> <p>Cada UID y cada GID puede tener asociado un nombre, especificado en los ficheros /etc/passwd y /etc/group, respectivamente</p></list-item>
<list-item><label>&#x25FE;</label> <p>La informaci&#x00F3;n de <code>/etc/passwd</code> y <code>/etc/group</code> la utilizan diversas &#x00F3;rdenes de administraci&#x00F3;n. Ambos ficheros deben existir y ser coherentes para que el sistema funcione correctamente</p></list-item>
<list-item><label>&#x25FE;</label> <p>No es recomendable editar estos ficheros directamente, sino mediante mandatos como <code>usermod</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>Si se edita <code>/etc/passwd</code> y <code>/etc/group/</code> directamente, debe usarse vipw y vigr</p></list-item>
</list>
</sec>
</sec>
<sec id="c5-s2">
<label><bold>5.2.</bold></label>
<title><bold>Importancia de las contrase&#x00F1;as</bold></title>
<sec id="c5-s2-1">
<title><bold>Importancia de las contrase&#x00F1;as</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>El campo <italic>passwd</italic> en el <code>/etc/passwd</code> y el <code>/etc/shadow</code> se encuentra cifrado con una funci&#x00F3;n <italic>hash</italic> para evitar que los usuarios (y administradores) puedan conocer las contrase&#x00F1;as de otros usuarios.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Se usa un cifrado de un solo sentido: no existe algoritmo para averiguar la contrase&#x00F1;a a partir de estos ficheros.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Pero se pueden probar varias contrase&#x00F1;as, hasta millones por segundo (John the Ripper).</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_26"/>Es imprescindible elegir palabras clave seguras, que no aparezcan en diccionarios, evitando nombres o fechas significativas, combinando s&#x00ED;mbolos, y de la mayor longitud posible.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Ejemplos de malas contrase&#x00F1;as:</p></list-item>
</list>
<preformat>
123456
4312
toby
r2d2
tornillo
fromage
mostoles
</preformat>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Contrase&#x00F1;as que parecen buenas, pero son malas:</p></list-item>
</list>
<preformat>
XCV330
NCC-1701-A
ARP2600V
</preformat>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Buenas contrase&#x00F1;as</p>
<p>Para usos ordinarios, una contrase&#x00F1;a razonablemente buena ser&#x00E1; parecida (&#x00A1;pero no igual!) a una de estas</p><p><table-wrap>
<table frame="hsides" rules="groups">
<thead>
<tr>
<th valign="top" align="left"><p><code>Contrase&#x00F1;a</code></p></th>
<th valign="top" align="left"><p><code>Nemot&#x00E9;cnico</code></p></th>
</tr>
</thead>
<tbody>
<tr>
<td valign="top" align="left"><p><code>00QuReMa:</code></p></td>
<td valign="top" align="left"><p><code>Queridos Reyes Magos:</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>3x4igDoze</code></p></td>
<td valign="top" align="left"><p><code>3x4=doce</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>1pt,yTp1</code></p></td>
<td valign="top" align="left"><p><code>uno para todos,todos para uno</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>19Dy500n</code></p></td>
<td valign="top" align="left"><p><code>19 dias y 500 noches</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>R,cmqht?0</code></p></td>
<td valign="top" align="left"><p><code>Rascay&#x00FA;, cuando mueras que har&#x00E1;s t&#x00FA;?</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>wali1YS!</code></p></td>
<td valign="top" align="left"><p><code>we all live in a Yellow Submarine</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>Lh10knpr.</code></p></td>
<td valign="top" align="left"><p><code>le hare una oferta que no podr&#x00E1; rechazar</code></p></td>
</tr>
</tbody>
</table>
</table-wrap></p>
<p>Para aplicaciones especialmente sensibles, es necesario ampliar el <italic>keyspace</italic> a 14 caracteres o m&#x00E1;s</p></list-item>
<list-item><label>&#x25FE;</label> <p>Es conveniente que busquemos e inutilicemos las contrase&#x00F1;as d&#x00E9;biles de nuestros usuarios, ya que suponen un primer punto de entrada en nuestro sistema</p></list-item>
<list-item><label>&#x25FE;</label> <p>En Unix, el root no puede leer las contrase&#x00F1;as. Pero en otros entornos s&#x00ED;. Y muchos usuarios emplean siempre la misma contrase&#x00F1;a</p>
<p>Ejemplo:</p>
<list list-type="order">
<list-item><p>Juan Torpe usa como contrase&#x00F1;a dgj441iU en <ext-link ext-link-type="uri" xlink:href="mailto:juan.torpe@hotmail.com">juan.torpe@hotmail.com</ext-link></p></list-item>
<list-item><p><target target-type="page" id="pges_27"/>Juan Torpe se apunta en <code><ext-link ext-link-type="uri" xlink:href="www.ladygaga-videos-prohibidos.com">www.ladygaga-videos-prohibidos.com</ext-link></code>, con su cuenta de correo y su contrase&#x00F1;a de siempre</p></list-item>
<list-item><p>El administrador malicioso de este web ya conoce el nombre de Juan, su cuenta de correo y su contrase&#x00F1;a.</p>
<list list-type="bullet">
<list-item><p>Puede usar la funci&#x00F3;n <italic>&#x00BF;contrase&#x00F1;a olvidada?</italic> y colarse en cualquier otra cuentas de Juan</p></list-item>
</list></list-item>
</list></list-item>
</list>
<p>Debemos instruir a nuestros usuarios sobre esto</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Los usuarios sin duda olvidar&#x00E1;n en ocasiones su contrase&#x00F1;a y tendremos que generles una nueva, de forma segura</p></list-item>
<list-item><label>&#x25FE;</label> <p>Pero es muy poco profesional que nosotros como administradores olvidemos una contrase&#x00F1;a. Debemos usar varias y guardarlas de forma medianamente segura (gpg, keepassx, lastpass, etc)</p></list-item>
</list>
</sec>
</sec>
<sec id="c5-s3">
<label><bold>5.3.</bold></label>
<title><bold>Secret sharing</bold></title>
<sec id="c5-s3-1">
<title><bold>Secret sharing</bold></title>
<p>Aunque pongamos cuidado extremo en guardar nuestra contrase&#x00F1;a, esto puede no ser suficiente.</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>&#x00BF;Y si a pesar de todo, la perdemos o nos la roban?</p></list-item>
<list-item><label>&#x25FE;</label> <p>&#x00BF;Y si no estamos disponibles? (Viaje, enfermedad, muerte...)</p></list-item>
<list-item><label>&#x25FE;</label> <p>&#x00BF;Y si nos secuestran?</p></list-item>
<list-item><label>&#x25FE;</label> <p>&#x00BF;Y si hacemos alg&#x00FA;n disparate?</p></list-item>
</list>
<p>Podr&#x00ED;amos dividir la contrase&#x00F1;a en n trozos y repartirlos entre n personas de nuestra confianza, de forma que con el acuerdo de todos, el secreto es recuperable</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Problema: Bastar&#x00ED;a con que se pierde un trozo para perder el secreto</p></list-item>
<list-item><label>&#x25FE;</label> <p>Soluci&#x00F3;n: Algoritmos de <italic>secret sharing</italic></p></list-item>
</list>
<p>Un esquema <italic>secret sharing</italic> es aquel que permite dividir un secreto en n fragmentos, de forma que basten t (<italic>t &#x003C; n</italic>) para reconstruirlo</p>
<p><target target-type="page" id="pges_28"/>Armory incluye esto de manera nativa, con electrum podemos usar una herramienta independiente, ssss</p>
<p>Con la orden <code>ssss-split</code>, dividimos el secreto en n fragmentos</p>
<preformat>
koji@mazinger:~$ ssss-split -t 3 -n 5
WARNING: couldn&#x0027;t get memory lock (ENOMEM, try to adjust RLIMIT_MEMLOCK!).
Generating shares using a (3,5) scheme with dynamic security level.
Enter the secret, at most 128 ASCII characters
ABRETE SESAMO
1-378a29cbbe9b38f0d473bdab0654
2-d7cc58bbce72070afc675f0991dd
3-d42a4e6f0dca1d32a6d1f96e4e92
4-629dd862cb052f8774bdb26d91df
5-617bceb608bd35bf2e0b140a4e82
</preformat>
<p>Con la orden ssss-combine, recuperamos el secreto</p>
<preformat>
koji@mazinger:~$ ssss-combine -t 3
WARNING: couldn&#x0027;t get memory lock (ENOMEM, try to adjust RLIMIT_MEMLOCK!).
Enter 3 shares separated by newlines:
Share [1/3]: 2-d7cc58bbce72070afc675f0991dd
Share [2/3]: 3-d42a4e6f0dca1d32a6d1f96e4e92
Share [3/3]: 5-617bceb608bd35bf2e0b140a4e82
Resulting secret: ABRETE SESAMO
</preformat>
<p>Si el secreto es mayor de 128 bytes, se cifra con una clave tradicional y se aplica ssss a esta nueva clave</p>
<preformat>
/etc/passwd
</preformat>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Contiene la informaci&#x00F3;n de todos los usuarios del sistema.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Contenido: l&#x00ED;neas con campos separados por dos puntos:</p>
<p><code><italic>login :passwd :UID :GID :info :home-dir :shell</italic></code></p></list-item>
<list-item><label>&#x25FE;</label> <p>El campo &#x201C;<italic>login</italic>&#x201D; puede tener hasta 32 caracteres en Linux, pero se recomienda limitarlo a 8, como en los UNIX cl&#x00E1;sicos</p></list-item>
<list-item><label>&#x25FE;</label> <p>El campo &#x201C;<italic>passwd</italic>&#x201D; contiene la contrase&#x00F1;a cifrada (con DES o con MD5) y puede estar en otro fichero, en el <code>/etc/shadow</code>.</p></list-item>
<list-item><label>&#x25FE;</label> <p>El campo &#x201C;<italic>info</italic>&#x201D; contiene el nombre real del usuario e informaci&#x00F3;n adicional como el tel&#x00E9;fono, etc. Por (desafortunados) motivos hist&#x00F3;ricos, tambi&#x00E9;n se le denomina GECOS</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_29"/>En algunos sistemas, puede haber informaci&#x00F3;n externa (NIS, LDAP. . .)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Programas que lo utilizan directamente: <code>login, su, passwd.</code></p></list-item>
</list>
<preformat>
/etc/group
</preformat>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Nombres de grupos del sistema, y miembros de cada grupo.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Contenido: l&#x00ED;neas con campos separados por dos puntos:</p>
<p><code><italic>nombre :passwd :GID :lista-logins</italic></code></p></list-item>
<list-item><label>&#x25FE;</label> <p>&#x201C;<italic>lista-logins</italic>&#x201D; son usuarios separador por comas que pertenecen a ese grupo.</p></list-item>
<list-item><label>&#x25FE;</label> <p>El campo &#x201C;<italic>passwd</italic>&#x201D; no se suele utilizar. Permite ingresar en un grupo en el que no se es miembro.</p></list-item>
<list-item><label>&#x25FE;</label> <p>En algunos sistemas, puede haber informaci&#x00F3;n externa (NIS, LDAP. . .)</p></list-item>
</list>
</sec>
<sec id="c5-s3-2">
<title><bold>/etc/shadow</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Si existe, contiene las contrase&#x00F1;as cifradas de los usuarios del sistema.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Contenido: l&#x00ED;neas con campos separados por dos puntos:</p>
<p><code><italic>login :passwd :a :b :c :d :e :f :g</italic></code></p>
<p>a: momento en que la <italic>passwd</italic> fue cambiada por &#x00FA;ltima vez.</p>
<p>b: d&#x00ED;as que deben pasar antes de que pueda cambiarse.</p>
<p>c: d&#x00ED;as despu&#x00E9;s de los cuales la <italic>passwd</italic> debe cambiarse.</p>
<p>d: d&#x00ED;as antes de la expiraci&#x00F3;n para avisar al usuario.</p>
<p>e: d&#x00ED;as despu&#x00E9;s de la expiraci&#x00F3;n para desactivar la cuenta.</p>
<p>f: momento en que la cuenta se ha desactivado.</p>
<p>g: campo reservado.</p></list-item>
</list>
</sec>
<sec id="c5-s3-3">
<title><bold>Para mejorar la seguridad se a&#x00F1;ade un &#x201D;salt&#x201D;</bold></title>
<p><italic>salt</italic> es un tipo de <italic>nounce</italic>: Number used once. 2 bytes aleatorios que se a&#x00F1;aden a la contrase&#x00F1;a</p>
<p>Sin salt</p>
<preformat>
password --&#x003E; hash (password)
&#x0022;sesamo&#x0022; --&#x003E; zv/coRb$PjGToGEqNZF434TmQ7bAH.rVi3i.o7IWQAI9qqzeGKe/JkJq bDfQE2gBFYzBTDNCHyoxpZvSLhenkPT3L6aZN0
</preformat>
<p><target target-type="page" id="pges_30"/>El atacante puede usar una <italic>rainbow table</italic>: El resultado de aplicar hash a un diccionario completo. Si encuentra la hash en la tabla, conoce la contrase&#x00F1;a que fue usada</p>
<preformat>
rainbow table
hash(palabra1)
hash(palabra2)
hash(palabra3)
</preformat>
<p>Con salt</p>
<preformat>
password+salt --&#x003E; hash (password+salt)
rainbow table:
hash(palabra1+salt1)
hash(palabra1+salt2)
hash(palabra1+salt3)
</preformat>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><italic>salt</italic> se guarda en abierto: se a&#x00F1;ade al hash, son los primeros dos bytes</p></list-item>
<list-item><label>&#x25FE;</label> <p>Esto obliga a que la rainbow table sea mucho mayor, puede hacerla inviable</p></list-item>
</list>
</sec>
<sec id="c5-s3-4">
<title><bold>Desactivar un usuario del sistema</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Bloquear su contrase&#x00F1;a en el <code>/etc/passwd</code> o <code>/etc/shadow</code> (a&#x00F1;adiendo un car&#x00E1;cter &#x201C;<code>-</code>&#x201D; o &#x201C;<code>*</code>&#x201D;, por ejemplo).</p></list-item>
<list-item><label>&#x25FE;</label> <p>Eliminar sus tareas peri&#x00F3;dicas (<code>/var/spool/cron</code>).</p></list-item>
<list-item><label>&#x25FE;</label> <p>Revisar <code>/etc/aliases</code> y <code>.forward</code> por si el usuario tuviera acciones a realizar con el correo recibido.</p></list-item>
</list>
<p>Eliminar un usuario</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>userdel</code></p></list-item>
<list-item><label>&#x25FE;</label> <p><code>userdel -r</code> tambi&#x00E9;n borra su correo y su <italic>home</italic></p></list-item>
</list>
</sec>
<sec id="c5-s3-5">
<title><bold>Usuarios especiales</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>No todas las l&#x00ED;neas del <code>/etc/passwd</code> corresponden con usuarios f&#x00ED;sicos.</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_31"/>Super-usuario: uid=0 (su <italic>login</italic> es normalemente <code>root</code>).</p></list-item>
<list-item><label>&#x25FE;</label> <p>Otros usuarios del sistema: se utilizan para:</p>
<list list-type="bullet">
<list-item><p>tareas espec&#x00ED;ficas de administraci&#x00F3;n</p></list-item>
<list-item><p>propietarios de determinados ficheros del sistema</p></list-item>
<list-item><p>ejecuci&#x00F3;n de determinadas aplicaciones (bases de datos, servidores de web, ftp, e-mail, noticias, etc)</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Normalmente, los usuarios normales tienen UIDs entre el 1000 y el 30000.</p></list-item>
</list>
</sec>
<sec id="c5-s3-6">
<title><bold>Cambio de contrase&#x00F1;a</bold></title>
<p>Para cambiar la contrase&#x00F1;a y otros datos se utilizan las &#x00F3;rdenes <code>passwd</code> (contrase&#x00F1;a), <code>chfn</code> (info/gecos), <code>chsh</code> (<italic>shell</italic>):</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Estas &#x00F3;rdenes tienen <italic>set-uid</italic> para que un usuario normal pueda modificar informaci&#x00F3;n privilegiada.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Antes de nada, piden la <italic>passwd</italic> del usuario para verificar que es quien dice ser.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Bloquean cada fichero a modificar para asegurar exclusi&#x00F3;n de accesos.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Realizan las modificaciones.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Desbloquean ficheros.</p></list-item>
</list>
<p>Para crear o cambiar la contrase&#x00F1;a de un usuario desde un script</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>echo &#x0022;jperez:sesamo&#x0022; | chpasswd</code></p></list-item>
</list>
</sec>
<sec id="c5-s3-7">
<title><bold>Cambios de usuario y grupo</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>su ejecuta otra <italic>shell</italic> bajo un usuario distinto.</p>
<list list-type="bullet">
<list-item><p><code>su jperez</code></p>
<p>ejecuta otra shell, perteneciente al usuario <italic>jperez</italic></p>
<p>No hace falta ser <code>root</code> para ejecutar esto, si la invoca un usuario ordinario, le pedir&#x00E1; la contrase&#x00F1;a de <italic>jperez</italic></p></list-item>
<list-item><p><code>su</code></p>
<p>ejecuta shell con uid=0 (<code>root</code>).</p></list-item>
<list-item><p><target target-type="page" id="pges_32"/>Pide la contrase&#x00F1;a del usuario destino, excepto salvo si el origen es <code>root</code>.</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p><code>newgrp</code> ejecuta una <italic>shell</italic> con distinto GID.</p>
<list list-type="bullet">
<list-item><p>Tiene <italic>set-uid</italic></p></list-item>
<list-item><p><code>newgrp</code> permite cambiar el GID a otro grupo al que pertenezcamos (cambia el grupo primario)</p></list-item>
</list></list-item>
</list>
<p>Mandatos que s&#x00F3;lo pueden ejecutarse como <code>root</code></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>groupadd</code> <italic>grupo</italic></p>
<p>crea un grupo</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>adduser</code> <italic>usuario</italic><xref ref-type="fn" rid="FN1"><sup>1</sup></xref></p>
<p>a&#x00F1;ade un usuario. Copia en su home el directorio <code>/etc/skel</code></p>
<p><code>adduser</code> <italic>usuario grupo</italic></p>
<p>a&#x00F1;ade un usuario a un grupo</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>usermod -g</code> grupo_primario usuario</p>
<p>Cambia el grupo primario por omisi&#x00F3;n del usuario</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>passwd</code> <italic>usuario</italic></p>
<p>Cambia la contrase&#x00F1;a de un usuario</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>chown</code> <italic>due&#x00F1;o fichero(s)</italic></p>
<p>cambia el due&#x00F1;o de un fichero</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>chgrp</code> <italic>due&#x00F1;o fichero(s)</italic></p>
<p>cambia el grupo de un fichero</p></list-item>
</list>
<p>Mandatos para cualquier usuario</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>passwd</code></p>
<p>Cambia la contrase&#x00F1;a del usuario</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>newgrp</code> <italic>grupo</italic></p>
<p>Entre los grupos de un usuario, elige el grupo primario</p></list-item>
</list>
</sec>
</sec>
</body>
<back>
<fn-group>
<fn id="FN1"><p><sup>1</sup> En RedHat, useradd usuario y chfn usuario</p></fn>
</fn-group>
</back>
</book-part>
<book-part id="c6" book-part-type="chapter">
<book-part-meta>
<book-part-id book-part-id-type="publisher-id">URJC</book-part-id>
<title-group>
<label>6.</label>
<title><target target-type="page" id="pges_33"/>M&#x00C1;QUINAS VIRTUALES</title>
</title-group>
</book-part-meta>
<body>
<sec id="c5-s3-8">
<title><bold>M&#x00E1;quinas Virtuales</bold></title>
<p>M&#x00E1;quina Virtual: Software que crea una capa de abstracci&#x00F3;n, ofreciendo una m&#x00E1;quina diferente a la m&#x00E1;quina original</p>
<p>Las m&#x00E1;quinas virtuales que nos interesan en administraci&#x00F3;n de sistemas suelen ofrecer a un sistema operativo la percepci&#x00F3;n de una m&#x00E1;quina f&#x00ED;sica</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Las aplicaciones y los usuarios dentro de la m&#x00E1;quina virtual se relacionan con la capa de abstracci&#x00F3;n y no con la plataforma real</p></list-item>
<list-item><label>&#x25FE;</label> <p>La m&#x00E1;quina virtual puede implementar diversos dispositivos virtuales (disco, dispositivos de red, etc) diferentes a los de la plataforma real</p></list-item>
<list-item><label>&#x25FE;</label> <p>La tecnolog&#x00ED;a sobre M&#x00E1;quinas Virtuales est&#x00E1; muy madura. La terminolog&#x00ED;a, no. Es frecuente encontrarse con el distintos nombres para el mismo concepto, o incluso el mismo nombre para cosas distintas</p></list-item>
<list-item><label>&#x25FE;</label> <p><italic>Guest</italic>: Sistema Operativo de la m&#x00E1;quina virtual</p>
<p><italic>Host</italic>: Sistema Operativo de la m&#x00E1;quina real</p>
<p><fig>
<caption><title>Uno de los modelos posibles: m&#x00E1;quina virtual de sistema</title></caption>
<graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-2.jpg"/></fig></p></list-item>
<list-item><label>&#x25FE;</label> <p>La m&#x00E1;quina virtual se comporta como una aplicaci&#x00F3;n m&#x00E1;s en el <italic>host</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>El <italic>guest</italic> percibe la m&#x00E1;quina virtual como si fuera hardware real</p></list-item>
</list>
</sec>
<sec id="c6-s1">
<label><bold>6.1.</bold></label>
<title><target target-type="page" id="pges_34"/><bold>Utilidad de las m&#x00E1;quinas virtuales</bold></title>
<sec id="c6-s1-1">
<title><bold>Utilidad de las m&#x00E1;quinas virtuales</bold></title>
<p>Tecnolog&#x00ED;a tradicional y actual, con muchas utilidades</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Ejecutar aplicaciones hechas para una plataforma sobre una plataforma diferente: p.e Microsoft Windows sobre Mac OS, Java Virtual Machine</p></list-item>
<list-item><label>&#x25FE;</label> <p>Ofrecer un entorno seguro donde experimentar (<italic>sandbox</italic>)</p>
<list list-type="bullet">
<list-item><p>Docencia</p></list-item>
<list-item><p>Probar aplicaciones en desarrollo</p></list-item>
<list-item><p>Probar aplicaciones o webs no confiables</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Se&#x00F1;uelos (<italic>Honeypots</italic>)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Empresas de <italic>hosting</italic> pueden ofrecer servidores virtuales (alimentaci&#x00F3;n y conectividad redundante, soporte 24/365, etc)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Respaldo (<italic>backup</italic>) de m&#x00E1;quinas enteras, no solo de datos. Ante un peque&#x00F1;o problema o un gran desastre, la m&#x00E1;quina virtual se recupera inmediatamente</p></list-item>
<list-item><label>&#x25FE;</label> <p>Seguridad: Cortafuegos, per&#x00ED;metros de seguridad,...</p></list-item>
<list-item><label>&#x25FE;</label> <p>Portabilidad: Moviendo un directorio se puede mover la m&#x00E1;quina virtual de una m&#x00E1;quina real a otra</p></list-item>
<list-item><label>&#x25FE;</label> <p>Independencia del Hardware, p.e. homogeneizar un conjunto de m&#x00E1;quinas diferentes</p></list-item>
<list-item><label>&#x25FE;</label> <p>...</p></list-item>
</list>
</sec>
</sec>
<sec id="c6-s2">
<label><bold>6.2.</bold></label>
<title><bold>Inconvenientes de las m&#x00E1;quinas virtuales</bold></title>
<sec id="c6-s2-1">
<title><bold>Inconvenientes de las m&#x00E1;quinas virtuales</bold></title>
<p>Inconveniente principal: p&#x00E9;rdida de rendimiento Aunque no siempre</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>La m&#x00E1;quina <italic>real</italic> tal vez no existe (p.e. java)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Existe, pero es una m&#x00E1;quina de prop&#x00F3;sito espec&#x00ED;fico.</p>
<p>Un guest sobre un host de prop&#x00F3;sito general puede ser m&#x00E1;s eficiente</p></list-item>
</list>
</sec>
</sec>
<sec id="c6-s3">
<label><bold>6.3.</bold></label>
<title><target target-type="page" id="pges_35"/><bold>Tipos de M&#x00E1;quina Virtual</bold></title>
<sec id="c6-s3-s1">
<label><bold>6.3.1.</bold></label>
<title><bold>MV de proceso y de sistema</bold></title>
<sec id="c6-s3-s1-1">
<title><bold>MV de proceso y de sistema</bold></title>
<p>Seg&#x00FA;n su grado de equivalencia sobre una m&#x00E1;quina hardware, las m&#x00E1;quinas virtuales se pueden clasificar en</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>M&#x00E1;quinas virtuales de sistema</p>
<p>Proporcionan un entorno completo y persistente para ejecutar un sistema operativo y sus procesos</p>
<p>Ej: VirtualBox, Xen</p></list-item>
<list-item><label>&#x25FE;</label> <p>M&#x00E1;quinas virtuales de proceso</p>
<p>Proporcionan una plataforma para ejecutar un &#x00FA;nico proceso</p>
<list list-type="bullet">
<list-item><p>Contenedores</p></list-item>
<list-item><p>M&#x00E1;quina virtual de java, .NET</p>
<p>Este tipo no lo consideramos dentro del &#x00E1;mbito de la administraci&#x00F3;n de sistemas y no lo tratamos aqu&#x00ED;</p></list-item>
</list></list-item>
</list>
</sec></sec>
<sec id="c6-s3-s2">
<label><bold>6.3.2.</bold></label>
<title><bold>Emulaci&#x00F3;n Completa o Virtualizaci&#x00F3;n Completa</bold></title>
<sec id="c6-s3-s2-1">
<title><bold>Emulaci&#x00F3;n Completa o Virtualizaci&#x00F3;n Completa</bold></title>
<p>Whole-system virtualization. Se emula memoria, disco y otros dispositivos, tambi&#x00E9;n la CPU:</p>
<p>Al emular la CPU, son especialmente lentos. La arquitectura Intel tradicional ofrec&#x00ED;a muy pocas facilidades</p>
<p>Permiten que <italic>guest</italic> y <italic>host</italic> trabajen con diferente ISA (<italic>instruction set architecture</italic>)</p>
<p>Ejemplos: QEMU, Bochs.</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Emulan una CPU intel, incluso cuando se ejecutan sobre intel.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Ambos son libres, disponibles para diversos <italic>hosts</italic>.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Pueden ejecutar distintos <italic>guest</italic>, pero siempre para intel</p></list-item>
</list>
</sec></sec>
<sec id="c6-s3-s3">
<label><bold>6.3.3.</bold></label>
<title><target target-type="page" id="pges_36"/><bold>Virtualizaci&#x00F3;n</bold></title>
<sec id="c6-s3-s3-1">
<title><bold>Virtualizaci&#x00F3;n</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Al virtualizador tambi&#x00E9;n se le llama <italic>hipervisor</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>Se emula memoria virtual, disco y dispositivos</p>
<p>Ejemplo: VMware emula tarjeta de audio SoundBlaster 16 y tarjeta ethernet AMD PCnet II. Cualquier aplicaci&#x00F3;n en el <italic>guest</italic> percibe este hardware</p></list-item>
<list-item><label>&#x25FE;</label> <p>No se emula la CPU. Por tanto guest y host tienen que usar la misma arquitectura</p></list-item>
</list>
<p>VMware. Virtualizador. Software muy maduro. Versiones comerciales y versiones <italic>freeware</italic> (con los a&#x00F1;os va aumentando el n&#x00FA;mero de versiones freeware)</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>VMware Workstation, workstation player. Para host Windows y Linux. Permite crear y ejecutar m&#x00E1;quinas virtuales</p></list-item>
<list-item><label>&#x25FE;</label> <p>VMware Fusion. Similar a VMWare Workstation, para Mac OS</p></list-item>
<list-item><label>&#x25FE;</label> <p>VMware ESXi. Verdarero Sistema Operativo. Se ejecuta directamente sobre el hardware</p></list-item>
<list-item><label>&#x25FE;</label> <p>VMware vSphere. Computaci&#x00F3;n en la nube. Basado en ESXi</p></list-item>
</list>
<p>Parallels Desktop</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Virtualizador para los Mac OS basados en Intel</p></list-item>
<list-item><label>&#x25FE;</label> <p><italic>guest</italic> soportados: Microsoft Windows, Linux, FreBSD, Sun Solaris y algunos otros</p></list-item>
</list>
<p>(los Mac posteriores a 2006, basados en Intel, pueden ejecutar Windows en nativo con Boot Camp)</p>
<p>VirtualBox</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Virtualizador, muy similar a VMware</p></list-item>
<list-item><label>&#x25FE;</label> <p>Desarrollado por Innotek. Sun compra Innotek en 2008. Oracle compra Sun en 2009</p>
<list list-type="bullet">
<list-item><p>Virtual Box Open Source Edition</p></list-item>
<list-item><p><target target-type="page" id="pges_37"/>VirtualBox. Software Comercial. Gratuito para uso personal y acad&#x00E9;mico</p>
<p>Incluye alguna caracter&#x00ED;stica adicional, como soporte USB. sATA, iSCSI, Remote Display Protocol (RDP) Server</p></list-item>
</list></list-item>
</list>
</sec></sec>
<sec id="c6-s3-s4">
<label><bold>6.3.4.</bold></label>
<title><bold>Paravirtualizaci&#x00F3;n</bold></title>
<sec id="c6-s3-s4-1">
<title><bold>Paravirtualizaci&#x00F3;n</bold></title>
<p>Similar a la virtualizaci&#x00F3;n, pero exige un versi&#x00F3;n ligeramente modificada del <italic>guest</italic></p>
<p>El rendimiento es normalmente mayor que el de los tipos anteriores Xen</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Muy extendido</p></list-item>
<list-item><label>&#x25FE;</label> <p>Hay una versi&#x00F3;n libre que permite Linux sobre Linux</p></list-item>
<list-item><label>&#x25FE;</label> <p>Hay versiones comerciales que permiten Windows sobre Windows</p></list-item>
<list-item><label>&#x25FE;</label> <p>Los drivers est&#x00E1;n paravirtualizados, son m&#x00E1;s eficientes. (En un virtualizador, los drivers son drivers hw normales)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Tambi&#x00E9;n hay que modificar el guest (Xen lo llama Dom0)</p></list-item>
</list>
</sec></sec>
<sec id="c6-s3-s5">
<label><bold>6.3.5.</bold></label>
<title><bold>Virtualizaci&#x00F3;n nativa</bold></title>
<sec id="c6-s3-s5-1">
<title><bold>Virtualizaci&#x00F3;n asistida por hardware</bold></title>
<p>Tambi&#x00E9;n llamada <italic>virtualizaci&#x00F3;n nativa</italic></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Es una emulaci&#x00F3;n completa, pero realizada por la CPU con lo que el rendimiento es pr&#x00F3;ximo al nativo</p></list-item>
<list-item><label>&#x25FE;</label> <p>Exige soporte en la CPU. Para Intel aparece en el a&#x00F1;o 2006 con KVM: Kernel-based Virtual Machine. Infraestructura para virtualizaci&#x00F3;n completa del n&#x00FA;cleo de Linux</p></list-item>
<list-item><label>&#x25FE;</label> <p>Soportado por Xen</p></list-item>
</list>
<p>Procesadores que lo soportan:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_38"/>Intel virtualization (VT-x)</p>
<p>Pentium 4 6x2, Pentium D 9x0, Xeon 3xxx/5xxx/7xx, Intel Core, Intel Core 2, Intel Quad-Core. Algunos atom (serie Z5xx)</p></list-item>
<list-item><label>&#x25FE;</label> <p>AMD-V</p>
<p>AMD con Socket AM2, Socket S1 y Socket F. Tambi&#x00E9;n procesadores Athlon 64 y Turion 64 a partir de mayo de 2006</p></list-item>
</list>
</sec>
</sec>
</sec>
<sec id="c6-s4">
<label><bold>6.4.</bold></label>
<title><bold>Contenedores</bold></title>
<sec id="c6-s4-s1">
<label><bold>6.4.1.</bold></label>
<title><bold>Caracter&#x00ED;sticas de los contenedores</bold></title>
<sec id="c6-s4-s1-1">
<title><bold>Contenedores</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Un contenedor es una encapsulaci&#x00F3;n de una aplicaci&#x00F3;n y todas sus dependencias</p></list-item>
<list-item><label>&#x25FE;</label> <p>Se pueden considerar una versi&#x00F3;n aligerada de las m&#x00E1;quinas virtuales tradicionales</p></list-item>
<list-item><label>&#x25FE;</label> <p>Su nombre es una met&#x00E1;fora de los contenedores empleados en el transporte</p>
<list list-type="bullet">
<list-item><p>Recipientes de carga que puede transportarse f&#x00E1;cilmente en cami&#x00F3;n, barco o tren sin manipular la mercanc&#x00ED;a de su interior</p></list-item>
<list-item><p>Revolucionaron la industria en los a&#x00F1;os 1930</p></list-item>
<list-item><p>Desde los a&#x00F1;os 1970 son est&#x00E1;ndares mundiales</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Virtualizaci&#x00F3;n a nivel del sistema operativo</p></list-item>
<list-item><label>&#x25FE;</label> <p>Son m&#x00E1;quinas virtuales de proceso, t&#x00ED;picamente ejecutan un &#x00FA;nico proceso, como mucho unos pocos</p></list-item>
</list>
<p>Cada aplicacion se ejecuta en su propio contenedor. Cada una de ellas:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Comparte el mismo sistema operativo</p></list-item>
<list-item><label>&#x25FE;</label> <p>Tiene la percepci&#x00F3;n de acceso exclusivo a los recursos</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_39"/>No percibe a las dem&#x00E1;s</p></list-item>
</list>
<fig><caption><title>Contenedores (diagrama simplificado)</title></caption>
<graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-3.jpg"/>
</fig>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>No confundir con web container, tambi&#x00E9;n llamado servlet container que es algo completamente distinto:</p>
<list list-type="bullet">
<list-item><p>En los servidores web basados en java, un contenedor web es el subsistema que interact&#x00FA;a con los servlets java</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Los contenedores son t&#x00ED;picamente un orden de magnitud m&#x00E1;s eficientes que las m&#x00E1;quinas virtuales tradicionales</p>
<list list-type="bullet">
<list-item><p>Una m&#x00E1;quina virtual suele tardar en arrancar muchos segundos o algunos minutos. Un contenedor, d&#x00E9;cimas de segundo</p></list-item>
<list-item><p>El rendimiento del proceso en ejecuci&#x00F3;n es casi igual al nativo</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>No son incompatibles con las m&#x00E1;quinas virtuales tradicionales, al contrario, es muy habitual ejecutarlos dentro de m&#x00E1;quinas virtuales</p></list-item>
<list-item><label>&#x25FE;</label> <p>Los contenedores solucionan el t&#x00ED;pico problema de <italic>en mi m&#x00E1;quina funcionaba</italic></p></list-item>
</list>
<p>El desarrollador</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Incluye dentro del contenedor todo lo necesario para que la aplicaci&#x00F3;n se ejecute</p></list-item>
<list-item><label>&#x25FE;</label> <p>Sabe que la aplicaci&#x00F3;n funcionar&#x00E1; de forma id&#x00E9;ntica en cualquier entorno (un servidor hardware tradicional, una m&#x00E1;quina virtual, un servidor en la nube, un port&#x00E1;til...)</p></list-item>
</list>
<p>El administrador</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_40"/>Pueden concentrarse en los recursos de red, sin perder tiempo configurando entornos y dependencias en el sistema</p></list-item>
</list>
<p>El poco peso de los contendores</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Permite ejecutar docenas de ellos simult&#x00E1;neamente en cualquier m&#x00E1;quina</p></list-item>
<list-item><label>&#x25FE;</label> <p>Facilita su uso en la nube</p></list-item>
</list>
<p>Los contenedores son una tecnolog&#x00ED;a que est&#x00E1; cambiando la forma en la que se desarrolla, distribuye y ejecuta el software.</p>
</sec>
<sec id="c6-s4-s1-2">
<title><bold>Historia de los contenedores</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>A&#x00F1;o 1979. Sistema chroot de Unix V7. En cierta forma pueden considerarse los primeros contenedores, aunque solo encapsulan el sistema de ficheros (no los procesos, ni los usuarios, ni la red...)</p></list-item>
<list-item><label>&#x25FE;</label> <p>A&#x00F1;o 2000. BSD <italic>Jails</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>A&#x00F1;o 2001. Linux VServer (a&#x00F1;o 2001)</p></list-item>
<list-item><label>&#x25FE;</label> <p>A&#x00F1;o 2004. Solaris Containers</p></list-item>
<list-item><label>&#x25FE;</label> <p>A&#x00F1;o 2008. LXC (Linux Containers)</p></list-item>
<list-item><label>&#x25FE;</label> <p>A&#x00F1;o 2013. Solomon Hykes libera Docker como software libre</p></list-item>
</list>
</sec>
<sec id="c6-s4-s1-3">
<title><bold>Docker</bold></title>
<p>Madurez de los contenedores: Docker</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Hasta la aparici&#x00F3;n de Docker, los contenedores eran una herramienta de nicho. Ten&#x00ED;an su utilidad, pero no se puede decir que su uso fuera masivo</p></list-item>
<list-item><label>&#x25FE;</label> <p>Docker es una herramienta muy f&#x00E1;cil de usar y muy eficiente, hace que los contenedores pasen a ser muy populares</p></list-item>
<list-item><label>&#x25FE;</label> <p>Los organismos que principalmente contribuyen a su desarrollo son, adem&#x00E1;s del <italic>docker team</italic>, Cisco, Google, Huawei, IBM, Microsoft y Red Hat</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_41"/>Est&#x00E1; integrado con las principales plataformas y herramientas: Amazon Web Services, Ansible, CFEngine, Chef, Google Cloud Platform, IBM Bluemix, Jelastic, Jenkins, Kubernetes, Microsoft Azure, OpenStack Nova, Oracle Container Cloud Service, Puppet, Vagrant, VMware vSphere...</p></list-item>
</list>
</sec>
<sec id="c6-s4-s1-4">
<title><bold>Fundamentos de Docker</bold></title>
<p>Docker se basa en la funcionalidad de virtualizaci&#x00F3;n ofrecida por el nucleo de Linux:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>cgroups</p>
<p>El t&#x00E9;rmino proviene de <italic>control groups</italic>. A&#x00F1;o 2008. Permiten limitar y aislar los recursos consumidos por un grupo de procesos (CPU, memoria, E/S, red...)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Linux kernel namespaces</p>
<p>Grupos de procesos que no pueden <italic>ver</italic> los procesos de otros grupos. Quedan aislados los PID, los interfaces de red, las tablas de encaminamiento, el cortafuegos, el nombre de host, los puntos de montaje del sistema de ficheros, la intercomunicaci&#x00F3;n entre procesos y los identificadores de usuario</p></list-item>
</list>
</sec>
<sec id="c6-s4-s1-5">
<title><bold>Union Filesystem</bold></title>
<p>Otra tecnolog&#x00ED;a fundamental integrada en Docker es <italic>Union Filesystem</italic>, tambi&#x00E9;n llamado <italic>Union Mounting</italic></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Docker soporta diversos <italic>drivers</italic> para el UFS: overlay2, aufs, OverlayFS, entre otros</p>
<p>Emplear uno u otro depende fundamentalmente de la plataforma, el usuario de Docker normalmente no necesita ocuparse de esto</p></list-item>
<list-item><label>&#x25FE;</label> <p>Las im&#x00E1;genes de los contenedores est&#x00E1;n formadas por varias capas apiladas</p>
<list list-type="bullet">
<list-item><p>Todas las capas son de solo lectura, excepto la &#x00FA;ltima, que es de lectura y escritura</p></list-item>
<list-item><p>UFS permite que cada contenedor perciba todas las capas como un &#x00FA;nico sistema de ficheros ordinario</p></list-item>
<list-item><p>Pero cada capa de solo lectura puede ser compartida por varios contenedores, de forma que solo la capa de lectura/escritura es exclusiva para cada contenedor</p></list-item>
</list></list-item>
</list>
<p><target target-type="page" id="pges_42"/>Estas capas diferenciales apiladas representan un enorme ahorro de espacio en el sistema de ficheros</p>
<p>Ejemplo:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>10 im&#x00E1;genes similares de una m&#x00E1;quina virtual de 2 Gb ocupar&#x00E1;n necesariamente 20 Gb</p></list-item>
<list-item><label>&#x25FE;</label> <p>10 im&#x00E1;genes similares de un contenedor pueden ocupar 2.2 Gb</p></list-item>
</list>
</sec></sec>
<sec id="c6-s4-s2">
<label><bold>6.4.2.</bold></label>
<title><target target-type="page" id="pges_43"/><bold>Inconvenientes de los contenedores</bold></title>
<sec id="c6-s4-s2-1">
<title><bold>Inconvenientes de los contenedores (1)</bold></title>
<p>Todos los contenedores comparten el mismo kernel con el host</p>
<p>Esta es la principal ventaja (son ligeros) pero tambi&#x00E9;n el principal inconveniente (no son muy seguros)</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Una vulnerabilidad o un kernel panic en un contenedor afecta a toda la m&#x00E1;quina</p></list-item>
<list-item><label>&#x25FE;</label> <p>Cargar un m&#x00F3;dulo del kernel en el contenedor es cargarlo en el host</p></list-item>
<list-item><label>&#x25FE;</label> <p>Existe el riesgo de ataques DOS (Deny of Service)</p></list-item>
<list-item><label>&#x25FE;</label> <p>No hay un espacio de nombres propio para los usuarios en el contenedor. Si un usuario es <code>root</code> en el contenedor y consigue salir del contenedor, es <code>root</code> en el host</p></list-item>
</list>
</sec>
<sec id="c6-s4-s2-2">
<title><bold>Inconvenientes de los contenedores (2)</bold></title>
<p>Para poder lanzar un contener son necesarios privilegios de superusuario en el host. Es necesario</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>O bien ser <code>root</code> o usar sudo</p></list-item>
<list-item><label>&#x25FE;</label> <p>O bien pertenecer al grupo <italic>docker</italic>, lo que resulta equivalente</p></list-item>
</list>
<p>Previsiblemente este problema se resolver&#x00E1; en futuras versiones de Docker, con un espacio de nombres propio para los UID</p>
</sec>
<sec id="c6-s4-s2-3">
<title><bold>Inconvenientes de los contenedores (3)</bold></title>
<p>Otros elementos compartidos por host y contenedores:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Los dispositivos: discos, tarjetas gr&#x00E1;ficas, de sonido...</p></list-item>
<list-item><label>&#x25FE;</label> <p>La hora</p></list-item>
<list-item><label>&#x25FE;</label> <p>El anillo de claves del n&#x00FA;cleo</p></list-item>
</list>
</sec></sec>
<sec id="c6-s5">
<label><bold>6.5.</bold></label>
<title><bold>Otros tipos de virtualizaci&#x00F3;n</bold></title>
<sec id="c6-s5-1">
<title><bold>Otros tipos de virtualizaci&#x00F3;n</bold></title>
<p>Como acabamos de ver, hipervisores, paravirtualizadores, virtualizaci&#x00F3;n nativa y, recientemente, contenedores, son las herramientas de virtualizaci&#x00F3;n m&#x00E1;s habituales en administraci&#x00F3;n de sistemas</p>
<p>Pero hay muchas t&#x00E9;cnicas posibles, entre otras</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>M&#x00E1;quinas virtuales cooperativas</p></list-item>
<list-item><label>&#x25FE;</label> <p>User Mode Linux</p></list-item>
</list>
</sec>
<sec id="c6-s5-2">
<title><bold>M&#x00E1;quinas Virtuales Cooperativas</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><italic>Cooperative Virtual Machines</italic>. UML y similares</p></list-item>
<list-item><label>&#x25FE;</label> <p>T&#x00E9;rmino no demasiado extendido, acu&#x00F1;ado para coLinux</p></list-item>
<list-item><label>&#x25FE;</label> <p>Dos sistemas operativos en paralelo acceden al Hw</p></list-item>
<list-item><label>&#x25FE;</label> <p>El Hw no se virtualiza</p></list-item>
<list-item><label>&#x25FE;</label> <p>No muy usado</p></list-item>
</list>
</sec>
<sec id="c6-s5-3">
<title><bold>User Mode Linux</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>UML. No confundir con <italic>Unified Modeling Language</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>Es un tipo de m&#x00E1;quina virtual muy diferente a las anteriores: Un nucleo Linux ligeramente modificado para ejecutarse como un proceso de usuario sobre otro nucleo Linux</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_44"/>Permite ejecutar diferentes versiones de Linux sobre diferentes versiones de Linux</p></list-item>
<list-item><label>&#x25FE;</label> <p>Dise&#x00F1;ado para Intel, hay versiones para IA-64 y PowerPC</p></list-item>
<list-item><label>&#x25FE;</label> <p>Los dispositivos del guest no est&#x00E1;n virtualizados. Por tanto en el guest se percibe el hardware real</p></list-item>
<list-item><label>&#x25FE;</label> <p>No muy usado</p></list-item>
</list>
<p>Netkit</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Entorno basado en UML para emular redes: PCs, routers, conmutadores</p></list-item>
<list-item><label>&#x25FE;</label> <p>Software libre, desarrollado por la Universidad de Roma</p></list-item>
</list>
<p>NetGUI</p>
<fig><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-4.jpg"/></fig>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><italic>Front-end</italic> gr&#x00E1;fico para Netkit</p></list-item>
<list-item><label>&#x25FE;</label> <p>Desarrollado en GSyC</p></list-item>
</list>
</sec>
<sec id="c6-s5-4">
<title><bold>coLinux, AndLinux</bold></title>
<p>coLinux:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>A&#x00F1;o 2004. Basado en UML</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_45"/>Actualmente en desuso</p></list-item>
<list-item><label>&#x25FE;</label> <p>Versi&#x00F3;n del n&#x00FA;cleo de Linux que se ejecuta sobre otro S.O, como Windows</p></list-item>
</list>
<p>AndLinux</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Distribuci&#x00F3;n basada en Ubuntu con versi&#x00F3;n del n&#x00FA;cleo de Linux para ejecutarse sobre Windows 2000, XP, 2003, Vista, 7 (solo las versiones de 32 bits)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Usa coLinux</p></list-item>
<list-item><label>&#x25FE;</label> <p>Algunos servicios van sobre Windows nativo: Servidor de X Window (Xming), servidor de sonido (Pulse Audio)</p></list-item>
</list>
</sec>
<sec id="c6-s5-5">
<title><bold>Windows Subsystem for Linux (WSL)</bold></title>
<p>WSL 1</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>A&#x00F1;o 2016</p></list-item>
<list-item><label>&#x25FE;</label> <p>Capa de compatibilidad que permite hacer funcionar ejecutables Linux sobre Microsoft Windows. An&#x00E1;logo a Wine, pero al rev&#x00E9;s</p></list-item>
<list-item><label>&#x25FE;</label> <p>Mucho m&#x00E1;s ligero que una m&#x00E1;quina virtual. El hardware no est&#x00E1; virtualizado, el subsistema Linux comparte recursos con los procesos Windows</p></list-item>
<list-item><label>&#x25FE;</label> <p>El n&#x00FA;cleo de Windows se modifica para ejecutar directamente procesos Linux</p></list-item>
</list>
<p>WSL 2</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>A&#x00F1;o 2019</p></list-item>
<list-item><label>&#x25FE;</label> <p>Incluye un verdadero n&#x00FA;cleo de Linux</p></list-item>
<list-item><label>&#x25FE;</label> <p>El hardware se virtualiza, pero con una m&#x00E1;quina muy ligera (ligera como p.e. un contenedor)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Compatible con practicamente cualquier ejecutable Windows, no solo x64, tambi&#x00E9;n x32</p></list-item>
<list-item><label>&#x25FE;</label> <p>Permite usar m&#x00F3;dulos Linux convencionales</p></list-item>
</list>
<p><target target-type="page" id="pges_46"/>En ambos casos (WSL 1, WSL 2)</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Es una herramienta est&#x00E1;ndar en Windows, desarrollada por Microsoft. Gratuita (aunque no libre)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Hay varias distribuciones Linux preparadas para ejecutarse en este entorno, siende Ubuntu la m&#x00E1;s habitual</p></list-item>
<list-item><label>&#x25FE;</label> <p>Permite usar aplicaciones gr&#x00E1;ficas mediante un servidor X Window para Microsoft Windows.</p></list-item>
<list-item><label>&#x25FE;</label> <p>El objetivo es que los programadores puedan desarrollar c&#x00F3;digo para Linux en sus m&#x00E1;quinas Windows con mejor rendimiento y mayor comodidad que en una m&#x00E1;quina virtual tradicional. No poner servicios en producci&#x00F3;n</p></list-item>
<list-item><label>&#x25FE;</label> <p>Podr&#x00ED;amos usarlo para esta asignatura, parece funcionar bastante bien. El problema es que es un Linux un poco <italic>raro</italic>, con algunas peculiaridades</p>
<list list-type="bullet">
<list-item><p>A diferencia de un Linux dentro de una m&#x00E1;quina virtual tradicional, pr&#x00E1;cticamente indistinguible de un Linux en nativo</p></list-item>
</list></list-item>
</list>
</sec></sec></sec>
<sec id="c6-s6">
<label><bold>6.6.</bold></label>
<title><bold>T&#x00E9;cnicas sin virtualizaci&#x00F3;n</bold></title>
<sec id="c6-s6-1">
<title><bold>T&#x00E9;cnicas sin virtualizaci&#x00F3;n</bold></title>
<p>Instalaci&#x00F3;n, reconfiguraci&#x00F3;n y replicaci&#x00F3;n autom&#x00E1;tica, independecia de la plataforma, portabilidad... son caracter&#x00ED;sticas deseables en una buena administraci&#x00F3;n de sistemas</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Todas ellas pueden conseguirse mediante virtualizaci&#x00F3;n</p></list-item>
<list-item><label>&#x25FE;</label> <p>Pero la virtualizaci&#x00F3;n no es la &#x00FA;nica forma. Hay multitud de t&#x00E9;cnicas de administraci&#x00F3;n alternativas que tambi&#x00E9;n ofrecen estas cualidades</p></list-item>
<list-item><label>&#x25FE;</label> <p>Un buen administrador de sistemas evaluar&#x00E1; lo m&#x00E1;s adecuado para cada caso</p></list-item>
</list>
<p>Veremos a continuaci&#x00F3;n alguna de estas t&#x00E9;cnicas</p>
</sec>
<sec id="c6-s6-2">
<title><bold>Jaulas chroot</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Se cambia el directorio raiz que percibe un proceso, (y sus hijos) de forma que no puede acceder fuera de cierto directorio.</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_47"/>No se aisla el acceso a otros procesos, memoria, CPU, red u otros dispositivos</p></list-item>
</list>
</sec>
<sec id="c6-s6-3">
<title><bold>Simuladores</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Simulan algunas caracter&#x00ED;sticas del comportamiento externo de un sistema. P.e. simuladores de red (GloMoSim, JSIM, ns-2, OPNET, OMNet, etc)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Los mal llamados simulador de Zx-Spectrum para PC, simulador de Commodore 64 para PC, etc, no son simuladores. Son emuladores completos.</p></list-item>
</list>
</sec>
<sec id="c6-s6-4">
<title><bold>Capas de Compatibilidad</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Wine. Reimplementaci&#x00F3;n de la API de Win16 y Win32 para sistemas operativos basados en Unix bajo plataformas Intel. Permite ejecutar algunas aplicaciones para Windows en Linux.</p>
<p>Cedega es un <italic>fork</italic> comercial de Wine</p></list-item>
<list-item><label>&#x25FE;</label> <p>Cygwin. A&#x00F1;o 1995. Entorno para portar software POSIX a Windows, compuesto por:</p>
<list list-type="order">
<list-item><p>DLL que ofrece la funcionalidad de las llamadas al sistema de Linux</p></list-item>
<list-item><p>Colecci&#x00F3;n de herramientas habituales en sistemas Unix</p></list-item>
</list>
<p>Siempre es necesario recompilar las aplicaciones</p></list-item>
</list>
</sec>
<sec id="c6-s6-5">
<title><bold>Implementaci&#x00F3;n de protocolos de red</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>En redes Windows los directorios e impresoras se exportan mediante los protocolos smb/cifs NetBIOS.</p>
<p>Samba es una implementaci&#x00F3;n de estos protocolos, permite usar m&#x00E1;quinas Unix en redes Windows</p></list-item>
<list-item><label>&#x25FE;</label> <p>En Unix los directorios se exportan normalmente mediante NFS. Hay implementaciones de NFS para Windows. Permiten acceder a directorios Unix desde m&#x00E1;quinas Windows</p>
<list list-type="bullet">
<list-item><p>En Unix las impresoras se exportan normalmente mediante LPD (<italic>Line Printer Daemon Protocol</italic>). Est&#x00E1;ndar basado en TCP, RFC 1179.</p>
<p>Windows entiende este procolo, no hace falta software adicional</p></list-item>
</list></list-item>
</list>
</sec>
<sec id="c6-s6-6">
<title><target target-type="page" id="pges_48"/><bold>Clonaci&#x00F3;n</bold></title>
<p>Permite replicar el disco de una m&#x00E1;quina, y con ello todo su S.O. , configuraci&#x00F3;n, aplicaciones y datos</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Normalmente exige m&#x00E1;quinas id&#x00E9;nticas</p></list-item>
<list-item><label>&#x25FE;</label> <p>Las herramientas suelen poder clonar cualquier m&#x00E1;quina, con independencia de su S.O.</p>
<list list-type="bullet">
<list-item><p>Clonezilla. Libre, multiplataforma</p></list-item>
<list-item><p>Norton Ghost. Soft propietario para Windows</p></list-item>
<list-item><p>Acronis True Image. Soft propietario para Windows</p></list-item>
<list-item><p>Partition Saving. Freeware para Windows</p></list-item>
<list-item><p>Partimage. Soft libre, basado en linux, permite clonar cualquier >S.O. Viene incluido en <italic>SystemRescueCd</italic>, una distro <italic>live</italic> orientada a recuperar y reparar un sistema</p></list-item>
<list-item><p>SystemImager. Soft libre para Linux.</p>
<p>Uso t&#x00ED;pico: Se instala un PC, el <italic>cliente de oro</italic>. La imagen se almacena en el servidor. Esta imagen de distribuye por la red (local), clonando el PC. Si es necesario recuperar una imagen, solo se distribuyen los cambios</p></list-item>
</list></list-item>
</list>
</sec>
<sec id="c6-s6-7">
<title><bold>Instalaci&#x00F3;n autom&#x00E1;tica del S.O.</bold></title>
<p>Sistema que contesta autom&#x00E1;ticamente a las preguntas que hace un SO en su instalaci&#x00F3;n.</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><italic>preseed</italic> (debian)</p></list-item>
<list-item><label>&#x25FE;</label> <p><italic>kickstart (Red Hat)</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p><italic>nLite</italic> (Windows XP)</p></list-item>
<list-item><label>&#x25FE;</label> <p><italic>vLite</italic> (Windows Vista)</p></list-item>
</list>
</sec>
<sec id="c6-s6-8">
<title><bold>Instalaci&#x00F3;n autom&#x00E1;tica de aplicaciones web</bold></title>
<p>Librer&#x00ED;as de scripts que instalan aplicaciones web</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Muy usadas por los servicios de hosting</p></list-item>
<list-item><label>&#x25FE;</label> <p>El interfaz de usuario suele ser via web</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_49"/>Las aplicaciones que soportan suelen ser aplicaciones para el web</p></list-item>
<list-item><label>&#x25FE;</label> <p>Ejemplos: Softaculous, Installatron, Fantastico</p></list-item>
</list>
</sec>
<sec id="c6-s6-9">
<title><bold>Herramientas de administraci&#x00F3;n centralizada</bold></title>
<p>Herramientas que se encargan de que los ficheros de configuraci&#x00F3;n se <italic>mantengan</italic> en cierto estado (sin necesidad de preparar scripts que busquen las inconsistencias y las corrijan)</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>cfengine</p>
<p>Herramienta tradicional, muy potente. Manejo de cierta complejidad</p></list-item>
<list-item><label>&#x25FE;</label> <p>landscape</p>
<p>Para ubuntu. De pago</p></list-item>
<list-item><label>&#x25FE;</label> <p>spacewalk</p>
<p>Para Red Hat y CentOS</p></list-item>
<list-item><label>&#x25FE;</label> <p>Puppet</p>
<p>Herramienta muy popular. Basada en Ruby. Algo pesada</p></list-item>
<list-item><label>&#x25FE;</label> <p>Ansible</p>
<p>Muy ligera, no necesita demonio en el cliente, solo ssh. En auge</p></list-item>
<list-item><label>&#x25FE;</label> <p>MSCCM (Microsoft System Center Configuration Manager) Herramienta nativa en Windows para administraci&#x00F3;n centralizada</p></list-item>
<list-item><label>&#x25FE;</label> <p>Chef, Bcfg2, otras alternativas<target target-type="page" id="pges_50"/></p></list-item>
</list>
</sec></sec>
</body>
</book-part>
<book-part id="c7" book-part-type="chapter">
<book-part-meta>
<book-part-id book-part-id-type="publisher-id">URJC</book-part-id>
<title-group>
<label>7.</label>
<title><target target-type="page" id="pges_51"/>CONFIGURACI&#x00D3;N DE VIRTUALBOX</title>
</title-group>
</book-part-meta>
<body>
<sec id="c7-s1">
<label><bold>7.1.</bold></label>
<title><bold>Estructura de los laboratorios del GSyC</bold></title>
<sec id="c7-s1-1">
<title><bold>Estructura de los laboratorios del GSyC</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Para las pr&#x00E1;cticas de esta asignatura, tendr&#x00E1;s una cuenta en los laboratorios Linux del Departamento GSyC</p></list-item>
<list-item><label>&#x25FE;</label> <p>La misma cuenta la usar&#x00E1;s en las pr&#x00E1;cticas de muchas asignaturas del Departamento, durante toda la carrera/todo el m&#x00E1;ster</p></list-item>
</list>
<p>Para conocer la direcci&#x00F3;n IP de la m&#x00E1;quina en la que est&#x00E1;s trabajando puedes usar <code>hostname -i</code></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Las direcciones IP de cada m&#x00E1;quina pueden consultarse en el fichero <code>/etc/hosts</code> de cualquier equipo</p>
<list list-type="bullet">
<list-item><p>Este fichero equivale a</p>
<p><code>%SystemRoot%\system32\drivers\etc\hosts</code> (MS Windows)</p>
<p><code>/private/etc/hosts</code> (Mac OS)</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>La misma cuenta permite entrar en todas las m&#x00E1;quinas</p></list-item>
<list-item><label>&#x25FE;</label> <p>Cada usuario ver&#x00E1; el mismo <italic>home</italic> en todas las m&#x00E1;quinas de Fuenlabrada</p></list-item>
<list-item><label>&#x25FE;</label> <p>Cada usuario ver&#x00E1; el mismo <italic>home</italic> en todas las m&#x00E1;quinas de M&#x00F3;stoles, distinto al de Fuenlabrada</p></list-item>
<list-item><label>&#x25FE;</label> <p>Los servidores est&#x00E1;n dimensionados para mover ficheros del orden de KBytes o MBytes, no GBytes</p></list-item>
<list-item><label>&#x25FE;</label> <p>Los ficheros de los directorios <code>/tmp/</code> y <code>/var/tmp</code> son locales a cada ordenador</p></list-item>
<list-item><label>&#x25FE;</label> <p>Como en todo linux,</p>
<list list-type="bullet">
<list-item><p>El directorio <code>/tmp</code> se borra cada vez que se reinicia el ordenador</p></list-item>
<list-item><p>El directorio <code>/var/tmp</code> se borra cada vez que al administador le parece oportuno, sin que debamos esperar aviso previo</p></list-item>
</list></list-item>
</list>
</sec></sec>
<sec id="c7-s2">
<label><bold>7.2.</bold></label>
<title><target target-type="page" id="pges_52"/><bold>Im&#x00E1;genes de m&#x00E1;quinas virtuales</bold></title>
<sec id="c7-s2-1">
<title><bold>Im&#x00E1;genes de m&#x00E1;quinas virtuales</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Una de las ventajas de las m&#x00E1;quinas virtuales es que pueden clonarse (copiarse) de un host a otro. Para ello basta copiar un fichero o ficheros: la imagen de la m&#x00E1;quina</p></list-item>
<list-item><label>&#x25FE;</label> <p>VirtualBox llama a estas im&#x00E1;genes <italic>servicio virtualizado</italic>. En VirtualBox 4, es un fichero .ova <xref ref-type="fn" rid="FN2"><sup>2</sup></xref></p></list-item>
</list>
<p>Para clonar una m&#x00E1;quina virtual de un <italic>host</italic> a otro</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>En el <italic>host</italic> origen, exportamos la imagen indicando d&#x00F3;nde queremos guardar el .ova</p></list-item>
<list-item><label>&#x25FE;</label> <p>Llevamos este ficheros al <italic>host</italic> destino</p></list-item>
<list-item><label>&#x25FE;</label> <p>Importaremos el .ova, esto generar&#x00E1; autom&#x00E1;ticamente una nueva copia del disco duro virtual, en el directorio especificado en</p></list-item>
</list>
<preformat>
Archivo|Preferencias|General|
Carpeta predeterminada de m&#x00E1;quinas
</preformat>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Podemos borrar el .ova, pero normalmente ser&#x00E1; preferible conservarlo por si queremos en el futuro otr&#x00E1; m&#x00E1;quina <italic>como nueva</italic></p></list-item>
</list>
<p>Observa que entonces tenemos 3 copias del disco duro virtual</p>
<list list-type="order">
<list-item><p>La del <italic>host</italic> origen, en formato <code>.vmdk</code></p></list-item>
<list-item><p>La <italic>que viaja</italic>, incluida dentro del fichero <code>.ova</code></p></list-item>
<list-item><p>La del <italic>host</italic> destino, en formato <code>.vmdk</code></p></list-item>
</list>
</sec>
<sec id="c7-s2-2">
<title><bold>Ejemplo t&#x00ED;pico de uso de m&#x00E1;quinas virtuales</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Un profesor instala una m&#x00E1;quina virtual partiendo de cero</p>
<p>Crea una m&#x00E1;quina, especifica su tama&#x00F1;o de disco, de memoria, etc</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_53"/>El profesor exporta la m&#x00E1;quina virtual como fichero .ova (que dentro lleva un .vmdk ) y la deja en alg&#x00FA;n lugar, como p.e. el directorio <code>/var/lib/vms</code> de las m&#x00E1;quinas de sus alumnos</p></list-item>
<list-item><label>&#x25FE;</label> <p>Cada alumno instala la m&#x00E1;quina virtual en su VirtualBox partiendo de la imagen creada por el profesor</p></list-item>
<list-item><label>&#x25FE;</label> <p>Ahora el alumno podr&#x00ED;a exportar de nuevo la m&#x00E1;quina virtual y llev&#x00E1;rsela en un <italic>pendrive</italic> al pc de su casa</p></list-item>
</list>
<p>Algo muy parecido podr&#x00ED;a hacerlo un administrador con los equipos de sus usuarios, o un administrador que quiera conservar un servidor reci&#x00E9;n instalado para recuperarlo r&#x00E1;pidamente si hay problemas</p>
</sec>
<sec id="c7-s2-3">
<title><bold>Instalaci&#x00F3;n de una m.v. partiendo de cero</bold></title>
<p>Si ya contamos con una imagen de la m.v, podemos omitir estos pasos. Pero en otro caso:</p>
<list list-type="order">
<list-item><p>Lanzamos <code>VirtualBox desde</code> la shell</p></list-item>
<list-item><p>Pulsamos el bot&#x00F3;n <italic>nueva</italic>, que ejecuta el asistente para crear una nueva m&#x00E1;quina virtual</p></list-item>
<list-item><p>Indicamos el nombre de la m.v. (p.e. <code>pc01, ro01, auditor01</code>)</p>
<p>Sistema operativo Linux, Versi&#x00F3;n Ubuntu</p></list-item>
<list-item><p>El tama&#x00F1;o de memoria base depender&#x00E1; de lo que tenga el <italic>host</italic> y necesite en <italic>guest</italic>. Como referencia:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>OpenWrt: 32 Mb</p></list-item>
<list-item><label>&#x25FE;</label> <p>Ubuntu Server: 192 Mb</p></list-item>
<list-item><label>&#x25FE;</label> <p>Ubuntu con gr&#x00E1;ficos, BackTrack con gr&#x00E1;ficos: 512 Mb</p></list-item>
</list></list-item>
<list-item><p>Activamos <italic>Crear disco virtual</italic> y seguimos el asistente</p>
<p>El tama&#x00F1;o del disco depender&#x00E1; de lo que tenga el <italic>host</italic> y necesite en <italic>guest. Como referencia:</italic></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>OpenWrt: 64 Mb</p></list-item>
<list-item><label>&#x25FE;</label> <p>Ubuntu, Backtrack: 8Gb</p></list-item>
</list></list-item>
<list-item><p>En la ventana <italic>resumen</italic> revisamos todo y pulsamos <italic>Terminar</italic></p></list-item>
</list>
<p><target target-type="page" id="pges_54"/>Una vez que hemos especificado los componentes de la m&#x00E1;quina, habr&#x00E1; que instalar el sistema operativo, que normalmente tendremos en un cdrom/dvd o en una imagen iso de un cdrom/dvd</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>En el apartado de configuraci&#x00F3;n de la m&#x00E1;quina virtual, en
<preformat>
Almacenamiento | controlador IDE
(pulsamos en el icono que representa un CD con el signo +)
(pulsamos en el CD reci&#x00E9;n creado, llamado &#x0022;vacio&#x0022;)
(Vamos a &#x0022;dispositivo cd/dvd")
</preformat></p>
<p>Aqu&#x00ED; indicamos si usaremos el lector f&#x00ED;sico del <italic>host</italic> (anfitri&#x00F3;n) o una imagen iso del cdrom/dvd</p></list-item>
</list>
</sec>
<sec id="c7-s2-4">
<title><bold>Instalaci&#x00F3;n de una m.v. partiendo de una imagen</bold></title>
<p>Desde VirtualBox</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Comprobamos en
<preformat>
Archivo|Preferencias|General|
Carpeta predeterminada de m&#x00E1;quinas
</preformat></p>
<p>que el disco duro virtual quedar&#x00E1; copiado en el lugar adecuado.</p>
<list list-type="bullet">
<list-item><p>En casa, el lugar por omisi&#x00F3;n es v&#x00E1;lido:</p>
<p><code>~/VirtualBox VMs</code></p></list-item>
<list-item><p>En el laboratorio, es imprescindible que sea <code>/var/tmp/tulogin</code></p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p><code>Archivo|Importar servicio virtualizado|Seleccionar</code></p>
<p>(Elegimos el fichero .ova)</p></list-item>
<list-item><label>&#x25FE;</label> <p>En la ventana <italic>Configuraci&#x00F3;n de importaci&#x00F3;n de servicios virtualizados</italic> podemos cambiar algunos par&#x00E1;metros del <italic>guest</italic> (nombre, memoria, disco....)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Si dos m&#x00E1;quinas van a compartir segmento de red, es necesario cambiar su direcci&#x00F3;n MAC</p></list-item>
<list-item><label>&#x25FE;</label> <p>Si la imagen original se llama p.e. <code>pcNN</code>, haciendo clic sobre este nombre en la pantalla de configuraci&#x00F3;n de importaci&#x00F3;n, podemos cambiarlo. P.e. para llamarla <code>pc01</code></p></list-item>
</list>
<p><target target-type="page" id="pges_55"/>Este es el nombre de la m&#x00E1;quina visto desde VirtualBox.</p>
<p>Para cambiar el nombre visto desde dentro de la propia m&#x00E1;quina, hay que</p>
<fig><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-5.jpg"/></fig>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>O bien</p>
<list list-type="order">
<list-item><p>Editar /etc/hostname</p></list-item>
<list-item><p>En ubuntu 18.04 y posteriores:</p>
<p>Editar /etc/cloud/cloud.cfg para poner la opci&#x00F3;n preserve_hostname a true</p></list-item>
</list>
<p>Esto es persistente pero tiene efecto en el pr&#x00F3;ximo reinicio</p></list-item>
<list-item><label>&#x25FE;</label> <p>O bien ejecutar la orden
<preformat>
hostname &#x003C;NUEVO_NOMBRE&#x003E;
</preformat></p>
<p>Esto es inmediato pero no es persistente</p></list-item>
</list>
</sec>
<sec id="c7-s2-5">
<title><bold>Fragmentaci&#x00F3;n de ficheros</bold></title>
<p>Si necesitas trocear una imagen de gran tama&#x00F1;o en ficheros que quepan en un <italic>pendrive</italic> o cdrom</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Empaquetar y comprimir un directorio:
<preformat>
tar -cvzf mi_imagen.tgz mi_directorio
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Mostrar contenido:
<preformat>
tar -tzf mi_imagen.tgz
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Trocear:
<preformat>
# tama&#x00F1;o fichero prefijo split -b 500MB mi_imagen.tgz mi_ima
</preformat></p>
<p>(Observa que el segundo par&#x00E1;metro es igual al primero, pero a&#x00F1;adiendo un punto)</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_56"/>Habremos generado
<preformat>
mi_imagen.tgz.aa mi_imagen.tgz.ab mi_imagen.tgz.ac
</preformat></p></list-item>
</list>
<fig><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-6.jpg"/></fig>
<p>En la m&#x00E1;quina destino (no importa si en el <italic>host</italic> el S.O. es distinto)</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Unir los fragmentos <code>cat mi_imagen.tgz.* &#x003E; mi_imagen.tgz</code></p>
<p>(En MS Windows para este paso podemos emplear HjSplit, Free File Splitter o cualquier otro programa similar)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Descomprimir y desempaquetar:
<preformat>
tar -xvzf mi_imagen.tgz
</preformat></p>
<p>(En MS Windows podemos usar 7-Zip o similares)</p></list-item>
</list>
</sec></sec>
<sec id="c7-s3">
<label><bold>7.3.</bold></label>
<title><bold>Interfaces de red en VirtualBox</bold></title>
<sec id="c7-s3-1">
<title><bold>Interfaces de red de VirtualBox</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Cada m&#x00E1;quina virtual puede tener hasta 4 interfaces <italic>aka</italic> adaptadores de red</p>
<p>adaptador 1 ser&#x00E1; eth0, adaptador 2 ser&#x00E1; eth1, etc</p></list-item>
<list-item><label>&#x25FE;</label> <p>Cada interfaz puede conectarse a 5 tipos de segmento de red: No conectado, NAT, Adaptador puente, Red interna, Solo anfitri&#x00F3;n</p></list-item>
<list-item><label>&#x25FE;</label> <p>Not attached / No conectado</p>
<p>Emula una tarjeta con el cable de red desconectado</p></list-item>
<list-item><label>&#x25FE;</label> <p>Network Address Translation (NAT)</p>
<p>Configuraci&#x00F3;n por defecto. El <italic>guest</italic> tiene acceso al exterior (t&#x00ED;picamente internet) a trav&#x00E9;s de NAT. El <italic>host</italic> no tiene acceso al <italic>guest</italic></p>
<p>Podemos usar varios <italic>guest</italic>, pero cada uno tiene su propio NAT y est&#x00E1; aislado en su propio segmento de red
<fig><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-7.jpg"/></fig></p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_57"/>Bridged networking / Adaptador puente</p>
<p>Interfaz en el <italic>guest</italic> conectado virtualmente al mismo <italic>hub</italic> (real) que el <italic>host</italic></p>
<p>El <italic>guest</italic> est&#x00E1; en el segmento de red <italic>normal</italic> del <italic>host</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>Internal networking / Red interna</p>
<p>Red entre diferentes <italic>guests</italic> en un mismo <italic>host</italic></p>
<p>Sin acceso al <italic>host</italic> ni al exterior</p></list-item>
<list-item><label>&#x25FE;</label> <p>Host-only networking / S&#x00F3;lo anfitri&#x00F3;n</p>
<p>Red entre el <italic>guest</italic> y el <italic>host</italic>, sin acceso al exterior</p>
<p>Permite tener varios <italic>guests</italic> en el mismo segmento de red</p></list-item>
</list>
<p>Supongamos que deseamos configurar dos <italic>guest</italic> de esta forma:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>En pc01, el interfaz eth0 estar&#x00ED;a conectado a NAT</p>
<p>Dentro de la m&#x00E1;quina virtual, lo configurar&#x00ED;amos para obtener sus par&#x00E1;metros por DHCP</p></list-item>
<list-item><label>&#x25FE;</label> <p>En pc01, el interfaz eth1 lo conectar&#x00ED;amos a una red interna. El nombre por omisi&#x00F3;n de este segmento de red es intnet (atenci&#x00F3;n, significa internal net, no internet)</p>
<p>Podemos ponerle el nombre que queramos al segmento</p></list-item>
<list-item><label>&#x25FE;</label> <p>Dentro de la m&#x00E1;quina virtual pc01, configurar&#x00ED;amos est&#x00E1;ticamente los par&#x00E1;metros de eth1</p></list-item>
<list-item><label>&#x25FE;</label> <p>En pc02, conectar&#x00ED;amos eth0 a una red interna, con el mismo nombre que la red interna de eth1 en pc01 (en este ejemplo, <italic>intnet</italic>)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Dentro de pc02, configurar&#x00ED;amos est&#x00E1;ticamente los par&#x00E1;metros de eth0</p></list-item>
</list>
</sec></sec>
<sec id="c7-s4">
<label><bold>7.4.</bold></label>
<title><bold><target target-type="page" id="pges_58"/>Configuraci&#x00F3;n que seguiremos en pr&#x00E1;cticas</bold></title>
<sec id="c7-s4-1">
<title><bold>Uso de VirtualBox en los laboratorios del GSyC</bold></title>
<p>Un disco duro virtual ser&#x00E1; t&#x00ED;picamente un fichero de varios GBytes almacenado en</p>
<preformat>
~/VirtualBox VMs
</preformat>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>En tu PC esto no ser&#x00E1; un problema</p></list-item>
<list-item><label>&#x25FE;</label> <p>En el laboratorio s&#x00ED;, el rendimiento ser&#x00ED;a muy pobre. Por tanto cambiaremos la ubicaci&#x00F3;n por omisi&#x00F3;n de los discos duros virtuales</p>
<list list-type="bullet">
<list-item><p>En el <italic>host</italic>
<preformat>
mkdir /var/tmp/tulogin
</preformat></p>
<p>(Donde <italic>tulogin</italic> es tu usuario del laboratorio, p.e. mgarcia, jperez...)</p></list-item>
<list-item><p>En VirtualBox:
<preformat>
Archivo|Preferencias|General|
Carpeta predeterminada de m&#x00E1;quinas
</preformat></p>
<p>Indicamos
<preformat>
/var/tmp/tulogin
</preformat></p>
<p>Muy importante: aseg&#x00FA;rate de cambiar esta preferencia y mantenerla siempre. De lo contrario, cargar&#x00E1;s mucho el servidor, perjudicandote a t&#x00ED; y a tus compa&#x00F1;eros</p></list-item>
</list></list-item>
</list>
</sec>
<sec id="c7-s4-2">
<title><bold>Problema: las im&#x00E1;genes son ficheros grandes</bold></title>
<p>Con lo visto hasta ahora, ya podr&#x00ED;amos hacer las pr&#x00E1;cticas de la asignatura. Pero ser&#x00ED;a poco pr&#x00E1;ctico.</p>
<p>Supongamos una pr&#x00E1;ctica que consista en configurar en red 3 m&#x00E1;quinas virtuales</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Cada alumno tendr&#x00ED;a que guardar en su cuenta del laboratorio 3 im&#x00E1;genes (con sus 3 discos duros virtuales completos)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Para trabajar en casa, tendr&#x00ED;a que llevarse las 3 im&#x00E1;genes con sus 3 discos duros virtuales completos</p></list-item>
<list-item><label>&#x25FE;</label> <p>Para que 70 alumnos entreguen su pr&#x00E1;ctica, el profesor tendr&#x00ED;a que manejar 210 discos duros virtuales completos</p></list-item>
</list>
<p><target target-type="page" id="pges_59"/>Si en la asignatura se hacen 2 o 3 pr&#x00E1;cticas, seguimos multiplicando...</p>
</sec>
<sec id="c7-s4-3">
<title><bold>Soluci&#x00F3;n: almacenar solo los ficheros importantes</bold></title>
<p>Administrar un Unix/Linux consiste en editar diversos ficheros de texto</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Solamente manejaremos estos ficheros, que estar&#x00E1;n guardados en la cuenta de bilo y respaldados en la nube de Dropbox</p></list-item>
<list-item><label>&#x25FE;</label> <p>Las m&#x00E1;quinas virtuales ser&#x00E1;n <italic>de usar y tirar</italic>, tomar&#x00E1;n la configuraci&#x00F3;n de estos ficheros</p></list-item>
<list-item><label>&#x25FE;</label> <p>Dentro de las m&#x00E1;quinas virtuales, los ficheros de configuraci&#x00F3;n ser&#x00E1;n enlaces simb&#x00F3;licos a ficheros en un directorio, que a su vez estar&#x00E1; montado por red desde un directorio en el laboratorio</p></list-item>
</list>
</sec>
<sec id="c7-s4-4">
<title><bold>Ejemplo:</bold></title>
<p>Para configurar los interfaces de red de una m&#x00E1;quina, hay que editar <code>/etc/network/interfaces</code></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Dentro de la m&#x00E1;quina virtual pc01, este fichero ser&#x00E1; un enlace simb&#x00F3;lico que apuntar&#x00E1; a <code>/media/nube/interfaces</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>El directorio <code>/media/nube</code> de <code>pc01</code>, estar&#x00E1; montado desde el directorio <code>~/Dropbox/pc01</code> del laboratorio</p></list-item>
<list-item><label>&#x25FE;</label> <p>Por tanto, <code>/etc/network/interfaces</code> en la m&#x00E1;quina virtual pc01 y <code>~/Dropbox/pc01/interfaces</code> en el laboratorio ser&#x00E1;n el mismo fichero, podr&#x00E1; editarse indistintamente cualquiera de los dos</p></list-item>
</list>
<p>Para montar el directorio remoto, aqu&#x00ED; emplearemos sshfs Ventaja principal:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Basta con tener acceso por ssh a la m&#x00E1;quina remota para poder montar un directorio</p></list-item>
</list>
<p>Pero esta idea de tener los ficheros importantes por separado y luego colocarlos autom&#x00E1;ticamente en su sitio puede aplicarse de muchas otras formas</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Tanto en m&#x00E1;quinas f&#x00ED;sicas como virtuales</p></list-item>
<list-item><label>&#x25FE;</label> <p>En un entorno docente, dom&#x00E9;stico, de oficina, granja de servidores...</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_60"/>Mediante nfs, o scp, o rsync, o unison, o smb/cifs, o vboxsf...</p></list-item>
<list-item><label>&#x25FE;</label> <p>Con la oportuna atenci&#x00F3;n a la seguridad si se trata de un sistema en producci&#x00F3;n</p></list-item>
</list>
</sec>
<sec id="c7-s4-5">
<title><bold>Montar un directorio con sshfs</bold></title>
<p>Punto de montaje: directorio local donde veremos el directorio remoto</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Montar el <italic>home</italic> remoto:
<preformat>
sshfs usuario@maquina: /punto/de/montaje
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Montar un directorio remoto cualquiera
<preformat>
sshfs usuario@maquina:/un/directorio /punto/de/montaje
</preformat></p>
<p>(Siempre path absoluto, no soporta ~)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Desmontar:
<preformat>
fusermount -u /punto/de/montaje
</preformat></p></list-item>
</list>
<p>No siempre es necesario tener privilegios de <code>root</code> (es configurable)</p>
<p>En conexiones lentas puede ser conveniente a&#x00F1;adir la opci&#x00F3;n -C para que comprima el tr&#x00E1;fico</p>
<preformat>
sshfs -C usuario@maquina:/path /punto/de/montaje
</preformat>
</sec></sec>
<sec id="c7-s5">
<label><bold>7.5.</bold></label>
<title><bold>Cambio de host en el laboratorio</bold></title>
<sec id="c7-s5-1">
<title><bold>Cambio de host en el laboratorio</bold></title>
<p>La m.v. est&#x00E1; en un directorio local del pc donde trabajas, no en tu cuenta de pantuflo/bilo</p>
<p>Si te sientas en un puesto del laboratorio distinto al del dia anterior:</p>
<list list-type="order">
<list-item><p>Sal de VirtualBox y borra la m&#x00E1;quina vieja
<preformat>
rm -rf ~/.VirtualBox
rm -rf /var/tmp/tulogin/* # si este directorio existe
</preformat></p></list-item>
<list-item><p><target target-type="page" id="pges_61"/>Vuelve a indicar en
<preformat>
Archivo|Preferencias|General|
Carpeta predeterminada de m&#x00E1;quinas
</preformat></p>
<p>que la carpeta predetermina de m&#x00E1;quinas tiene que ser <code>/var/tmp/tulogin</code></p></list-item>
<list-item><p>Vuelve a importar el servicio virtualizado (O copia <code>/var/tmp/tulogin</code> desde el host anterior)</p></list-item>
</list>
</sec>
<sec id="c7-s5-2">
<title><bold>Observaciones</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Recuerda que el administrador puede borrar tu m&#x00E1;quina virtual en cualquier momento, no guardes dentro nada de valor, todos tus ficheros deben estar en el directorio compartido</p></list-item>
<list-item><label>&#x25FE;</label> <p>En esta asignatura, cada m&#x00E1;quina Ubuntu tendr&#x00E1; por omisi&#x00F3;n un usuario de nombre <code>user</code> y contrase&#x00F1;a <code>user</code> autorizado a ejecutar la orden sudo</p>
<list list-type="bullet">
<list-item><p>Recuerda que en el caso de Ubuntu, se espera que no empleemos el usuario <code>root</code></p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>En las m&#x00E1;quinas sin gr&#x00E1;ficos, podemos usar varias consolas pulsando <sc>Alt F1, Alt F2</sc>, etc</p></list-item>
<list-item><label>&#x25FE;</label> <p>Si dejamos la m&#x00E1;quina virtual desatendida algunos minutos, puede saltar el salvapantallas y quedarse en negro. En tal caso, llevamos el foco a la m&#x00E1;quina virtual (haciendo clic dentro) y pulsamos cualquier tecla</p></list-item>
</list>
</sec>
<sec id="c7-s5-3">
<title><bold>Algunos errores posibles</bold></title>
<p>Si has empezado a importar una m&#x00E1;quina virtual, te has equivocado en algo y has vuelto a empezar, VirtualBox puede mostrar&#x00E1; un error indicando que ese disco duro ya est&#x00E1; registrado y no puede importarse de nuevo</p>
<p>Soluciones</p>
<list list-type="order">
<list-item><p>En
<preformat>
Archivo | Administrador de medios virtuales
</preformat></p>
<p>Elimina esa imagen de la lista de medios conocidos, o elim&#x00ED;nala por completo (una ventana te informar&#x00E1;). F&#x00ED;jate si es la imagen <italic>que viaja</italic> o la del <italic>host destino</italic></p></list-item>
<list-item><p><target target-type="page" id="pges_62"/>Alternativa m&#x00E1;s dr&#x00E1;stica: Cerrar VirtualBox y borrar todo el directorio <code>~/.VirtualBox</code></p>
<p>(esto elimina toda la configuraci&#x00F3;n y todas las m&#x00E1;quinas)</p></list-item>
</list>
<p>Si intentamos usar dos instancias de un <italic>guest</italic> en el mismo <italic>host</italic> nos dar&#x00E1; un error indicando que ambos discos tienen el mismo identificador. En este caso, hay que clonar el disco</p>
<p>Ejecutamos desde la shell</p>
<preformat>
VBoxManage clonehd &#x003C;filename&#x003E; &#x003C;outputfilename&#x003E;
</preformat>
</sec>
<sec id="c7-s5-4">
<title><bold>Reiniciar VirtualBox</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Para borrarlo todo y volver a empezar, elimina los directorios <code>~/.VirtualBox y /var/tmp/tulogin</code></p>
<p>Pero recuerda volver a indicar <code>/var/tmp/tulogin</code> en
<preformat>
Archivo|Preferencias|General|
Carpeta predeterminada de m&#x00E1;quinas
</preformat></p></list-item>
</list>
</sec></sec>
<sec id="c7-s6">
<label><bold>7.6.</bold></label>
<title><bold>Configuraci&#x00F3;n del teclado</bold></title>
<sec id="c7-s6-1">
<title><bold>Configuraci&#x00F3;n del teclado</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>El teclado habitual en los PCs espa&#x00F1;oles es el pc105 (O el pc102 si no tiene teclas men&#x00FA;, windows)</p></list-item>
<list-item><label>&#x25FE;</label> <p>El equivalente en los PCs estadounidenses es el pc104 y el pc101, respectivamente</p></list-item>
<list-item><label>&#x25FE;</label> <p>Programadores y usuarios normalmente trabaja con versiones del SO adaptadas a su idioma</p></list-item>
<list-item><label>&#x25FE;</label> <p>Un administrador frecuentemente se encontrar&#x00E1; con un SSOO en ingl&#x00E9;s</p>
<list list-type="bullet">
<list-item><p>Normalmente podr&#x00E1; configurarlo para que admita su propio idioma (si no en men&#x00FA;s y documentaci&#x00F3;n, s&#x00ED; en la configuraci&#x00F3;n del teclado)</p></list-item>
<list-item><p>Pero mientras lo configura, tendr&#x00E1; que saber manejarse m&#x00ED;nimamente con el teclado desconfigurado</p></list-item>
</list></list-item>
</list>
<p>Si el ordenador tiene X Window (Gr&#x00E1;ficos), podemos configurarlo con</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_63"/><code>setxkbmap us</code> Fija el teclado en la disposici&#x00F3;n pc104</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>setxkbmap es</code> Fija el teclado en la disposici&#x00F3;n pc105</p></list-item>
</list>
<p>En Debian/Ubuntu podemos usar</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>dpkg-reconfigure console-setup</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>O m&#x00E1;s globalmente
<preformat>
sudo dpkg-reconfigure locales
</preformat></p></list-item>
</list>
<p>En gnome:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>Sistema | Preferencias | Teclado | Distribuciones |</code>
<preformat>
| A&#x00F1;adir | Espa&#x00F1;ol | Subir (hasta colocarlo el primero)
</preformat></p>
<p>Necesitar&#x00E1;s cambiar esto por ejemplo si entras desde casa al laboratorio por VNC y lanzas una m&#x00E1;quina virtual</p></list-item>
</list>
<p>En OpenWrt esto no est&#x00E1; disponible, la mejor opci&#x00F3;n es entrar por ssh (lo que exige que la red ya funcione)</p>
<p>Si nuestro teclado es espa&#x00F1;ol, pero el sistema operativo espera un teclado norteamericano:</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>Obtener</code></p></td>
<td valign="top" align="left"><p><code>Pulsar</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>Esc</code></p></td>
<td valign="top" align="left"><p><code>Esc</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>:</code></p></td>
<td valign="top" align="left"><p><code>&#x00D1;</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>;</code></p></td>
<td valign="top" align="left"><p><code>&#x00F1;</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/</code></p></td>
<td valign="top" align="left"><p><code>_</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>!</code></p></td>
<td valign="top" align="left"><p><code>!</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>|</code></p></td>
<td valign="top" align="left"><p><code>shift &#x00C7;</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>&#x0027;</code></p></td>
<td valign="top" align="left"><p><code>, (ap&#x00F3;stofre, coma)</code><target target-type="page" id="pges_64"/></p></td>
</tr>
</tbody>
</table>
</table-wrap>
</sec></sec>
</body>
<back>
<fn-group>
<fn id="FN2"><p><sup>2</sup> En VirtualBox 3 eran 3 ficheros: .ovf .vmdk .mf</p></fn>
</fn-group>
</back>
</book-part>
<book-part id="c8" book-part-type="chapter">
<book-part-meta>
<book-part-id book-part-id-type="publisher-id">URJC</book-part-id>
<title-group>
<label>8.</label>
<title><target target-type="page" id="pges_65"/>DOCKER</title>
</title-group>
</book-part-meta>
<body>
<sec id="c8-s1">
<label><bold>8.1.</bold></label>
<title><bold>Prerrequisitos</bold></title>
<sec id="c8-s1-1">
<title><bold>Plataformas para ejecuta docker</bold></title>
<p>Docker tiene arquitectura cliente servidor</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>El cliente acepta la entrada del usuario, le muestra la salida, maneja los ficheros con los que preparar las im&#x00E1;genes</p></list-item>
<list-item><label>&#x25FE;</label> <p>El servidor ejecuta el contenedor</p></list-item>
</list>
<p>El servidor solo est&#x00E1; disponible para Linux 64 bits</p>
<p>Hay versiones para macOS y Microsoft Windows, donde el cliente se ejecuta en nativo contra un servidor dentro de una m&#x00E1;quina virtual</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Esta virtualizaci&#x00F3;n es transparente para el usuario</p></list-item>
</list>
</sec>
<sec id="c8-s1-2">
<title><bold>Docker dentro de una m&#x00E1;quina virtual</bold></title>
<p>Si vamos a ejecutar docker dentro de una m&#x00E1;quina virtual,</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>guest y host deben tener arquitectura 64 bits</p></list-item>
<list-item><label>&#x25FE;</label> <p>Es necesario que el host tenga soporte para Intel VT-x</p>
<list list-type="bullet">
<list-item><p>Los equipos muy antiguos o muy baratos no lo permiten (VT-x es del a&#x00F1;o 2006, pero no se generaliza en los equipos de gama b&#x00E1;sica/media hasta varios a&#x00F1;os despu&#x00E9;s)</p></list-item>
<list-item><p>Muchos equipos actuales tienen esta opci&#x00F3;n deshabilitada por omisi&#x00F3;n en la BIOS/UEFI</p></list-item>
</list></list-item>
</list>
</sec>
<sec id="c8-s1-3">
<title><bold>Algunos conceptos</bold></title>
<p>Im&#x00E1;genes:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>La imagen de un contenedor (o simplemente <italic>imagen</italic>) es un fichero en el sistema de ficheros del host</p></list-item>
<list-item><label>&#x25FE;</label> <p>Un contenedor se ejecuta a partir de una imagen</p></list-item>
</list>
<p><target target-type="page" id="pges_66"/>Esto es an&#x00E1;logo a un proceso que se ejecuta a partir de un fichero Manejaremos diversos identificadores, que no debemos confundir</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Nombre de la imagen. Ejemplos:</p>
<p><italic>debian</italic></p>
<p><italic>test/c01</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>Identificador de la imagen. Ejemplo:</p>
<p>cc8393a39248</p></list-item>
<list-item><label>&#x25FE;</label> <p>Nombre de contenedor. Si no lo indicamos expl&#x00ED;citamente, docker usar&#x00E1; nombres aleatorios como <italic>focused_yonath</italic> o <italic>wonderfuld_goldberg</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>Identificador de contenedor</p>
<p>Ejemplo: 18009dd9f349</p></list-item>
<list-item><label>&#x25FE;</label> <p>Nombre de host</p>
<p>Nombre de m&#x00E1;quina que se percibir&#x00E1; dentro del contenedor. (variable de entorno <code>$HOST</code>, nombre en el <italic>prompt</italic>, fichero <code>/etc/hostname</code>, etc)</p>
<p>Atenci&#x00F3;n: Este <italic>host</italic> de Docker se corresponde con lo que en VirtualBox ser&#x00ED;a el <italic>guest</italic></p></list-item>
</list>
</sec>
<sec id="c8-s1-4">
<title><bold>Nombres de imagen</bold></title>
<p>El nombre de la imagen</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Un nombre sin prefijo, por ejemplo <italic>debian</italic> indica una imagen oficial aprobada por docker</p></list-item>
<list-item><label>&#x25FE;</label> <p>Un nombre con prefijo, por ejemplo <italic>test/c01</italic> es una imagen no oficial. El prefijo puede ser una etiqueta que hayamos definido o un nombre de usuario en un registro de im&#x00E1;genes</p></list-item>
</list>
</sec></sec>
<sec id="c8-s2">
<label><bold>8.2.</bold></label>
<title><bold>Instalaci&#x00F3;n de Docker</bold></title>
<sec id="c8-s2-1">
<title><bold>Instalaci&#x00F3;n de docker</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Podemos instalar el paquete incluido en nuestra distribuci&#x00F3;n de ubuntu
<preformat>
apt update; apt upgrade -y ; apt install docker.io
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_67"/>Si por alg&#x00FA;n motivo ese paquete resulta antiguo y necesitamos la &#x00FA;ltima versi&#x00F3;n estable disponible de docker, ejecutamos el script disponible en <ext-link ext-link-type="uri" xlink:href="https://get.docker.com">https://get.docker.com</ext-link>
<preformat>
wget <ext-link ext-link-type="uri" xlink:href="https://get.docker.com">https://get.docker.com</ext-link> -0 get-docker.sh #letra &#x0022;0&#x0022; may&#x00FA;scula bash get-docker.sh
</preformat></p></list-item>
</list>
</sec></sec>
<sec id="c8-s3">
<label><bold>8.3.</bold></label>
<title><bold>Ejecuci&#x00F3;n de im&#x00E1;genes</bold></title>
<sec id="c8-s3-1">
<title><bold>Lanzar una imagen</bold></title>
<p>Para ejecutar docker, tenemos dos opciones</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>A&#x00F1;adir nuestro usuario al grupo docker
<preformat>
addgroup docker
adduser $USER docker
# (abrir una nueva sesi&#x00F3;n)
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Ejecutar docker con sudo</p></list-item>
</list>
<p>Para comprobar que la instalaci&#x00F3;n ha sido correcta, lanzamos una imagen sencilla</p>
<preformat>
docker run debian echo &#x0022;hola,mundo&#x0022;
</preformat>
<p>Esto busca en el <italic>registry</italic> oficial de docker una imagen llamada <italic>debian</italic>, ejecuta en ella la orden indicada, muestra su salida por stdout y concluye</p>
</sec>
<sec id="c8-s3-2">
<title><bold>Otro holamundo</bold></title>
<preformat>
koji@mazinger:~$ docker run hello-world
Unable to find image &#x0027;hello-world:latest&#x0027; locally
latest: Pulling from library/hello-world
5b0f327be733: Pull complete
Digest : sha256:1f19634d26995c320618d94e6f29c09c6589d5df3c063287a00e6de8458f8242
Status: Downloaded newer image for hello-world:latest
</preformat>
<preformat>
Hello from Docker!
This message shows that your installation appears to be working correctly.
</preformat>
<preformat>
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the &#x0022;hello-world&#x0022; image from the Docker Hub.
3. <target target-type="page" id="pges_68"/>The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it to your terminal.
</preformat>
<preformat>
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
</preformat>
</sec>
<sec id="c8-s3-3">
<title><bold>Servidor docker remoto</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>En la configuraci&#x00F3;n m&#x00E1;s sencilla, el servidor de docker est&#x00E1; en la misma m&#x00E1;quina que el cliente, el ejecutable incluye ambas funciones</p></list-item>
<list-item><label>&#x25FE;</label> <p>Pero tambi&#x00E9;n puede ubicarse en una m&#x00E1;quina remota. Esto es &#x00FA;til, por ejemplo</p>
<list list-type="bullet">
<list-item><p>Cuando el cliente no es linux 64 bits</p></list-item>
<list-item><p>Cuando el cliente no tiene privilegios de <code>root</code> en la m&#x00E1;quina local (nuestro caso en el laboratorio)</p></list-item>
<list-item><p>Para arquitecturas distribuidas, equilibrio de carga, en la nube, etc</p></list-item>
</list></list-item>
</list>
<p>Configuraci&#x00F3;n del servidor remoto</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Es necesario el paquete docker.io</p></list-item>
<list-item><label>&#x25FE;</label> <p>El usuario tiene que poder entrar por ssh en la m&#x00E1;quina remota</p></list-item>
<list-item><label>&#x25FE;</label> <p>Deber&#x00ED;a poder autenticarse por ssh sin escribir la contrase&#x00F1;a cada vez (lo contrario ser&#x00ED;a muy inc&#x00F3;modo)</p></list-item>
<list-item><label>&#x25FE;</label> <p>El usuario debe pertenecer al grupo docker</p></list-item>
</list>
<p>Configuraci&#x00F3;n del cliente</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Es necesario el paquete docker.io</p></list-item>
<list-item><label>&#x25FE;</label> <p>El usuario necesita la variable de entorno
<preformat>
DOCKER_HOST=&#x0022;ssh://jperez@servidor_remoto&#x0022;
</preformat></p></list-item>
</list>
<p>Recuerda que:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_69"/>Puedes crear la variable de entorno en <code>~/.bashrc</code>. Pero los cambios no son inmediatos, es necesario una nueva sesi&#x00F3;n o leer el fichero expl&#x00ED;citamente con <code>source</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>Puedes comprobar las variable de entorno con <code>env</code></p></list-item>
</list>
</sec>
<sec id="c8-s3-4">
<title><bold>Repositorio de im&#x00E1;genes</bold></title>
<p>Adem&#x00E1;s de guardarse localmente, las im&#x00E1;genes est&#x00E1;n disponibles en los registry</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Registro (Registry)</p>
<p>Servicio responsable de almacenar y distribuir im&#x00E1;genes. El registro por omisi&#x00F3;n es <code><ext-link ext-link-type="uri" xlink:href="https://hub.docker.com">https://hub.docker.com</ext-link></code></p>
<p>Aunque hay otros similares, p&#x00FA;blicos. Y quien lo desee puede establecer su propio registro</p></list-item>
<list-item><label>&#x25FE;</label> <p>Repositorio (Repository)</p>
<p>Una colecci&#x00F3;n de im&#x00E1;genes relacionadas, normalmente ofrecen diferentes versiones de la misma aplicaci&#x00F3;n o servicio</p></list-item>
<list-item><label>&#x25FE;</label> <p>Etiqueta (Tag)</p>
<p>Identificador alfanum&#x00E9;rico asociado a una &#x00FA;nica imagen</p></list-item>
</list>
</sec>
<sec id="c8-s3-5">
<title><bold>Docker run</bold></title>
<p>Esta instrucci&#x00F3;n lanza un contenedor a partir de una imagen</p>
<p><code>docker run &#x003C;opciones&#x003E; &#x003C;imagen&#x003E;</code></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>La imagen se puede identificar mediante su nombre o mediante su id</p></list-item>
<list-item><label>&#x25FE;</label> <p>Las opciones -i y -t normalmente se usan juntas, para indicar que queremos una sesi&#x00F3;n interactiva en un terminal</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>--name &#x003C;nombre_contenedor&#x003E;</code></p></list-item>
<list-item><label>&#x25FE;</label> <p><code>-h &#x003C;nombre_host&#x003E;</code>
<preformat>
--hostname=&#x003C;nombre_host&#x003E;
</preformat></p></list-item>
</list>
<p>Ejemplo</p>
<preformat>
docker run -it --name c01 -h c01 test/im01
</preformat>
</sec>
<sec id="c8-s3-6">
<title><target target-type="page" id="pges_70"/><bold>Consulta de im&#x00E1;genes y contenedores</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>docker ps</code></p>
<p>Muestra los contenedores</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>docker images</code></p>
<p>Muestra las im&#x00E1;genes</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>docker inspect &#x003C;imagen&#x003E;</code></p>
<p>Muestra un json con descripci&#x00F3;n detallada del contenedor</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>docker diff &#x003C;imagen&#x003E;</code></p>
<p>Muestra los cambios en el sistema de ficheros del contenedor</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>docker logs &#x003C;imagen&#x003E;</code></p>
<p>Muestra las instrucciones ejecutadas en el contenedor</p></list-item>
</list>
</sec>
<sec id="c8-s3-7">
<title><bold>Exited containers</bold></title>
<p>Cuando un contenedor finaliza su ejecuci&#x00F3;n, queda en estado <italic>exited</italic>, al que informalmente se suele llama <italic>parado</italic></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>docker ps -a</code></p>
<p>Muestra los contenedores, incluyendo los parados</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>docker rm &#x003C;contenedor&#x003E;</code></p>
<p>Borra un contenedor</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>docker rmi &#x003C;imagen&#x003E;</code></p>
<p>Borra una imagen</p></list-item>
</list>
<p>Si el contenedor se lanza con la opci&#x00F3;n <code>--rm</code>, se borrar&#x00E1; autom&#x00E1;ticamente al concluir</p>
</sec>
<sec id="c8-s3-8">
<title><bold>Borrado de im&#x00E1;genes y contenedores</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Borrar todas las im&#x00E1;genes (que no est&#x00E9;n siendo usadas)
<preformat>
docker rmi $(docker images -a -q)
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_71"/>Borrar todos los contenedores detenidos
<preformat>
docker rm $(docker ps -a -f status=exited -q)
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Borrar todos los contenedores creados (y nunca ejecutados)
<preformat>
docker rm $(docker ps -a -f status=created -q)
</preformat></p></list-item>
</list>
</sec></sec>
<sec id="c8-s4">
<label><bold>8.4.</bold></label>
<title><bold>Creaci&#x00F3;n de im&#x00E1;genes</bold></title>
<sec id="c8-s4-1">
<title><bold>Creaci&#x00F3;n de im&#x00E1;genes</bold></title>
<p>La orden docker build nos permite construir im&#x00E1;genes.</p>
<p>Para construir una imagen, normalmente usaremos tres cosas:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Un directorio contexto, que ser&#x00E1; un directorio vacio en nuestra m&#x00E1;quina, donde iremos a&#x00F1;adiendo los ficheros necesario para construir la imagen</p></list-item>
<list-item><label>&#x25FE;</label> <p>Un fichero <code>Dockerfile</code> dentro del directorio contexto, con las instrucciones para crear la imagen</p></list-item>
<list-item><label>&#x25FE;</label> <p>Un fichero <code>entrypoint.sh</code>, que ser&#x00E1; un script de shell que</p>
<list list-type="bullet">
<list-item><p>Crearemos en el directorio contexto</p></list-item>
<list-item><p>Llevaremos a la imagen</p></list-item>
<list-item><p>Se ejecutar&#x00E1; cada vez que se lance un contenedor con esa imagen</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Si la imagen es muy sencilla, puede que no necesite <code>entrypoint.sh</code></p>
<p>Ejemplo:
<preformat>
FROM ubuntu:20.04
RUN apt update &#x0026;&#x0026; apt upgrade -y
ENTRYPOINT /bin/bash
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Tambi&#x00E9;n es posible crear una imagen sin usar un fichero Dockerfile Para ello basta con</p>
<list list-type="order">
<list-item><p>Entrar en el contenedor</p></list-item>
<list-item><p>Configurarlos: instalar paquetes, a&#x00F1;adir ficheros, modificar ficheros...</p></list-item>
<list-item><p><code>docker commit &#x003C;CONTENEDOR&#x003E; &#x003C;IMAGEN&#x003E;</code></p>
<p><code>&#x003C;CONTENEDOR&#x003E;</code>: Nombre o id del contenedor que ser&#x00E1; punto de partida da la imagen</p>
<p><code>&#x003C;IMAGEN&#x003E;:</code> Nombre que tendr&#x00E1; la imagen</p></list-item>
</list></list-item>
</list>
</sec>
<sec id="c8-s4-2">
<title><target target-type="page" id="pges_72"/><bold>Ejemplo: banner</bold></title>
<p>Vamos a crear una imagen llamada <italic>test/banner</italic> basada en la orden <italic>banner</italic> que al ejecutarse mostrar&#x00E1; los siguiente:</p>
<preformat>
koji@mazinger:~/lagrs/banner$ docker run -h c01 --name c01 test/banner
</preformat>
<fig><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-8.jpg"/></fig>
<p>Creamos un <italic>directorio contexto</italic> en el host, y en &#x00E9;l escribimos un fichero <code>entrypoint.sh</code></p>
<preformat>
#!/bin/bash
banner bienvenido
banner a
banner $HOSTNAME
</preformat>
<p>Cuando sea posible, es muy conveniente probar este script antes de construir la imagen, los errores aqu&#x00ED; son uno de los problemas m&#x00E1;s habituales preparando contenedores</p>
<p>En el directorio contexto tambi&#x00E9;n creamos un fichero Dockerfile</p>
<preformat>
FROM ubuntu:20.04
RUN apt update &#x0026;&#x0026; apt upgrade -y &#x0026;&#x0026; apt install -y sysvbanner
COPY entrypoint.sh /
ENTRYPOINT [&#x0022;/entrypoint.sh&#x0022;]
</preformat>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>La instrucci&#x00F3;n FROM indica la imagen de partida</p></list-item>
<list-item><label>&#x25FE;</label> <p>La instrucci&#x00F3;n RUN indica las modificaciones a realizar en la imagen</p>
<p>Puede haber m&#x00E1;s de un RUN, pero eso crea im&#x00E1;genes intermedias, por lo que lo habitual es encadenar varias &#x00F3;rdenes de shell con <code>&#x0026;&#x0026;</code></p>
<list list-type="bullet">
<list-item><p><target target-type="page" id="pges_73"/>Aunque todas las instrucciones apt tiene que estar todas en la misma sentencia RUN, para asegurarnos de que se encadenan correctamente</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>La opci&#x00F3;n -y en
<preformat>
apt upgrade
apt install
</preformat></p>
<p>es imprescindible (contesta a todas las preguntas con <italic>yes</italic>)</p></list-item>
<list-item><label>&#x25FE;</label> <p>La instrucci&#x00F3;n COPY copia un fichero desde el directorio contexto (que est&#x00E1; en el host) hasta el sistema de ficheros del futuro contenedor que se ejecute a partir de la imagen</p></list-item>
<list-item><label>&#x25FE;</label> <p>La instrucci&#x00F3;n ENTRYPOINT especifica el fichero que se ejecutar&#x00E1; al iniciar cada contenedor</p>
<p>Es habitual llamarlo <code>entrypoint.sh</code> y colocarlo en el directorio raiz del contenedor</p></list-item>
<list-item><label>&#x25FE;</label> <p>En el Dockerfile se pueden crear comentarios con el caracter <code>#</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>El contenido del Dockerfile es <italic>case insensitive</italic>, aunque el convenio es usar may&#x00FA;sculas para las instrucciones</p></list-item>
<list-item><label>&#x25FE;</label> <p>Si no existe un fichero <code>Dockerfile</code>, docker busca un fichero dockerfile</p></list-item>
</list>
<p>Una vez preparados los ficheros, construimos la imagen</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Desde el directorio padre del directorio contexto ejecutamos
<preformat>
docker build -t test/banner directorio_contexto
</preformat></p></list-item>
</list>
<p>Recuerda que los nombres de las im&#x00E1;genes que crearemos siempre llevar&#x00E1;n prefijo (puesto que no son im&#x00E1;genes oficiales)</p>
<p>Almacenamiento de la configuraci&#x00F3;n:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>La configuraci&#x00F3;n de docker se guarda en /var/lib/docker</p></list-item>
<list-item><label>&#x25FE;</label> <p>Las im&#x00E1;genes, depende del driver que docker use para el almacenamiento. Por omisi&#x00F3;n se usa aufs, que guarda las im&#x00E1;genes en <code>/var/lib/docker/aufs</code></p></list-item>
</list>
</sec>
<sec id="c8-s4-3">
<title><target target-type="page" id="pges_74"/><bold>Gesti&#x00F3;n de datos en docker</bold></title>
<p>El sistema de ficheros interior al contenedor es vol&#x00E1;til</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Todo lo escrito durante la ejecuci&#x00F3;n del contenedor se pierde al borrar el contenedor</p></list-item>
<list-item><label>&#x25FE;</label> <p>Es complicado acceder a esos datos sin usar el mismo contenedor</p></list-item>
</list>
<p>Podr&#x00ED;amos guardar datos en una nueva capa creando una nueva imagen, pero ser&#x00ED;a poco pr&#x00E1;ctico, no es recomendable</p>
<p>Un contenedor no deber&#x00ED;a tener estado. O en su defecto, el m&#x00ED;nimo estado posible</p>
<p>Docker ofrece 3 mecanismos para la persistencia de datos</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Bind mounts</p></list-item>
<list-item><label>&#x25FE;</label> <p>Volumes</p></list-item>
<list-item><label>&#x25FE;</label> <p>tmpfs</p></list-item>
</list>
<p>Por supuesto, dentro del contenedor se puede usar cualquier otro protocolo o servicio no espec&#x00ED;fico de Docker: NFS, sshfs, SMB, rsync, almacenamiento en la nube, bases de datos relacionales, bases de datos no relacionales...</p>
</sec>
<sec id="c8-s4-4">
<title><bold>Bind mounts</bold></title>
<p>Un <italic>bind mount</italic> es un directorio del host que se comparte con uno (o varios) contenedores</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Muy eficientes</p></list-item>
<list-item><label>&#x25FE;</label> <p>Muy pr&#x00E1;cticos para compartir datos con el host</p></list-item>
<list-item><label>&#x25FE;</label> <p>Dependen del sistema de ficheros del host y de su estructura, con lo que tienen problemas de portabilidad</p></list-item>
<list-item><label>&#x25FE;</label> <p>Evidentes problemas potenciales de seguridad, al tener el contenedor acceso directo al sistema de ficheros del host</p></list-item>
</list>
<p>Para hacer un bind mount, basta a&#x00F1;adir los siguientes par&#x00E1;metros a la orden docker run</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_75"/>Sintaxis tradicional
<preformat>
-v &#x003C;DIR_ORIGEN&#x003E;:&#x003C;DIR_DESTINO&#x003E;
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Sintaxis moderna, disponible a partir de Docker 17.06
<preformat>
--mount type=bind, source=&#x003C;DIR_ORIGEN&#x003E;, target=&#x003C;DIR_DESTINO&#x003E;
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p><code>DIR_ORIGEN</code> es el directorio en el host</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>DIR_DESTINO</code> es el directorio en el contenedor</p>
<list list-type="bullet">
<list-item><p>En el montaje de ficheros tradicional en Unix, es necesario que exista el punto de montaje. Aqu&#x00ED;, no</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Ambos directorios deben estar especificados con path absoluto</p></list-item>
<list-item><label>&#x25FE;</label> <p>No puede haber espacios antes ni despu&#x00E9;s de la coma</p></list-item>
</list>
<p>Suponiendo que los nombres de usuario coincidan en el host y en el contenedor, podr&#x00ED;amos hacer por ejemplo</p>
<preformat>
docker run -it -h jperbind01 --name jperbind01 --rm \
-v $HOME:/home/$USER \
jperez/bind
</preformat>
<p>Es necesario prestar mucha atenci&#x00F3;n a los montajes bind, son potencialmente peligrosos. El usuario que accede al sistema de ficheros fichero en el servidor de contenedores es el mismo que en el contenedor</p>
<p>Ejemplos</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Un proceso que sea <code>root</code> en el contenedor, tambi&#x00E9;n puede acceder como <code>root</code> al servidor. Por eso es tan delicado que un usuario pueda lanzar un contenedor</p></list-item>
<list-item><label>&#x25FE;</label> <p>Supongamos el <italic>home</italic> de un usuario del servidor configurado para que solo &#x00E9;l tenga acceso</p>
<list list-type="bullet">
<list-item><p>Para que un usuario del contenedor pueda acceder a este directorio con un montaje bind, el usuario dentro del docker tiene que tener el mismo id que el usuario en el servidor (no importa el nombre, solo el id)</p></list-item>
<list-item><p>Sucede lo mismo con el gid: el gid del usuario dentro del docker ser&#x00E1; el gid que vea el servidor</p></list-item>
</list></list-item>
</list>
<p><target target-type="page" id="pges_76"/>Una vez m&#x00E1;s: los montajes bind son potencialmente peligrosos Diferencia importante:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>En las m&#x00E1;quinas virtuales tradicionales (p.e. hipervisores) es extremadamente dif&#x00ED;cil que un proceso del guest consiga escaparse y acceder al host</p></list-item>
<list-item><label>&#x25FE;</label> <p>En los contenedores, muy f&#x00E1;cil</p></list-item>
</list>
</sec>
<sec id="c8-s4-5">
<title><bold>Volumen</bold></title>
<p>Es un disco virtual creado y gestionado por docker.</p>
<p>Se puede almacenar</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Como subdirectorio del host (en linux, por omisi&#x00F3;n en /var/lib/docker/volumes) Aunque no se recomienda que el host acceda directamente al volumen</p></list-item>
<list-item><label>&#x25FE;</label> <p>En host remotos o en la nube, Docker ofrece para ello diferentes drivers</p></list-item>
</list>
<p>Caracter&#x00ED;sticas:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Son m&#x00E1;s f&#x00E1;ciles de transportar y respaldar que los bind mounts</p></list-item>
<list-item><label>&#x25FE;</label> <p>Tienen mejores prestaciones para ser compartidos entre varios contenedores</p></list-item>
<list-item><label>&#x25FE;</label> <p>Se pueden cifrar</p></list-item>
</list>
</sec>
<sec id="c8-s4-6">
<title><bold>tmpfs</bold></title>
<p>Un montaje de tipo <italic>tmpfs</italic> se usa para datos temporales</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Es un sistema de ficheros especialmente eficiente porque se almacena en RAM</p></list-item>
<list-item><label>&#x25FE;</label> <p>Si creamos una imagen a partir del contenedor, el contenido de los montajes tmpfs no se almacena</p></list-item>
</list>
</sec>
<sec id="c8-s4-7">
<title><target target-type="page" id="pges_77"/><bold>Uso de sshfs</bold></title>
<p>Como hemos visto, los bind mounts permiten montar dentro de un contenedor directorios ubicados en el host docker</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Para montar directorios en cualquier otro lugar de internet, podemos usar por ejemplo sshfs</p></list-item>
<list-item><label>&#x25FE;</label> <p>Para ello es necesario a&#x00F1;adir a la orden docker run los siguientes par&#x00E1;metros</p>
<list list-type="bullet">
<list-item><p>En docker 17.10
<preformat>
--privileged
</preformat></p></list-item>
<list-item><p>En versiones m&#x00E1;s modernas de docker
<preformat>
--cap-add SYS_ADMIN --device /dev/fuse
--security-opt apparmor:unconfined
</preformat></p></list-item>
</list>
<p>Para averiguar tu versi&#x00F3;n de docker: <code>docker --version</code></p>
<list list-type="bullet">
<list-item><p>En un entorno de producci&#x00F3;n habr&#x00ED;a que usar estas opciones con precauci&#x00F3;n, puesto que incremeta mucho los privilegios del contenedor dentro del host</p></list-item>
</list></list-item>
</list>
</sec>
<sec id="c8-s4-8">
<title><bold>docker hub</bold></title>
<p>Para subir nuestras im&#x00E1;genes al registro docker hub</p>
<list list-type="order">
<list-item><p>Creamos una cuenta en <code><ext-link ext-link-type="uri" xlink:href="http://hub.docker.com">hub.docker.com</ext-link></code></p></list-item>
<list-item><p>Creamos nuestras im&#x00E1;genes usando como prefijo nuestro login en dockerhub
<preformat>
docker build -t mi_usuario/mi_imagen
</preformat></p></list-item>
<list-item><p>Abrimos una sesi&#x00F3;n en docker hub desde la shell con la orden
<preformat>
docker login
</preformat></p></list-item>
<list-item><p>Subimos la imagen
<preformat>
docker push mi_usuario/mi_imagen
</preformat></p></list-item>
</list>
</sec>
<sec id="c8-s4-9">
<title><bold>Usuarios dentro del contenedor</bold></title>
<p>La orden para crear usuario en Unix/Linux es <italic>adduser</italic></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_78"/>Solicita de forma interactiva contrase&#x00F1;a, nombre, etc</p></list-item>
<list-item><label>&#x25FE;</label> <p>Para construir una imagen con docker build, usamos otra orden distinta, useradd, que no hace preguntas sino que permite introducir la informaci&#x00F3;n mediante opciones</p></list-item>
</list>
<p>En el Dockerfile a&#x00F1;adimos
<preformat>
RUN useradd -rm -d /home/jperez -s /bin/bash -u 1001 jperez
</preformat></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>-rm</code></p>
<p>Cuenta de sistema, con directorio <italic>home</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p><code>-d</code></p>
<p>Directorio <italic>home</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p><code>-s</code></p>
<p>Especifica la shell</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>-u</code></p>
<p>Especifica uid</p></list-item>
</list>
<p>Para especificar qu&#x00E9; usuario ejecuta el contenedor, a&#x00F1;adimos al Dockerfile</p>
<preformat>
USER jperez
WORKDIR /home/jperez
</preformat>
<p>Tendremos el usuario ejecutando una shell sin necesidad de escribir contrase&#x00F1;a, pero si queremos a&#x00F1;adirla</p>
<preformat>
RUN echo &#x0027;jperez:sesamo&#x0027; | chpasswd
</preformat>
</sec></sec>
<sec id="c8-s5">
<label><bold>8.5.</bold></label>
<title><bold>Networking</bold></title>
<sec id="c8-s5-1">
<title><bold>Configuraci&#x00F3;n de red</bold></title>
<p>Al instalar docker se crean 3 redes</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>bridge</p>
<p>Segmento privado dentro del host, <code>172.17.0.0/16</code>, al que se conectan por omisi&#x00F3;n todos los contenedores</p></list-item>
<list-item><label>&#x25FE;</label> <p>null</p>
<p>Red nula, aisla los contenedores de la red</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_79"/>host</p>
<p>El contenedor comparte la red con el host, mismos interfaces, direcciones y puertos</p></list-item>
</list>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" colspan="4"><p><code>koji@mazinger:~$ docker network ls</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p>NETWORK ID</p></td>
<td valign="top" align="left"><p>NAME</p></td>
<td valign="top" align="left"><p>DRIVER</p></td>
<td valign="top" align="left"><p>SCOPE</p></td>
</tr>
<tr>
<td valign="top" align="left"><p>787cf305d42c</p></td>
<td valign="top" align="left"><p>bridge</p></td>
<td valign="top" align="left"><p>bridge</p></td>
<td valign="top" align="left"><p>local</p></td>
</tr>
<tr>
<td valign="top" align="left"><p>256d470b6133</p></td>
<td valign="top" align="left"><p>host</p></td>
<td valign="top" align="left"><p>host</p></td>
<td valign="top" align="left"><p>local</p></td>
</tr>
<tr>
<td valign="top" align="left"><p>086e801223bb</p></td>
<td valign="top" align="left"><p>none</p></td>
<td valign="top" align="left"><p>null</p></td>
<td valign="top" align="left"><p>local</p></td>
</tr></tbody>
</table></table-wrap>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Para conectar un contenedor a una red, basta lanzarlo con <code>--network=&#x003C;nombre_red&#x003E;</code></p>
<p>Ejemplo
<preformat>
docker run -it -h c03 --name c03 --rm --network=host test/im03
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Para crear una nueva red (un nuevo segmento)
<preformat>
docker network create --subnet 192.168.12.1/24 mired
</preformat></p></list-item>
</list>
</sec>
<sec id="c8-s5-2">
<title><bold>Servidor de SSH en el contenedor</bold></title>
<p>Para un contenedor en producci&#x00F3;n, no es recomendable habilitar el demonio de ssh</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Implica tener un segundo proceso, que no es natural en Docker</p></list-item>
<list-item><label>&#x25FE;</label> <p>No es buena idea dejar contrase&#x00F1;as dentro de un contenedor &#x00BF;c&#x00F3;mo actualizarlas?</p></list-item>
<list-item><label>&#x25FE;</label> <p>El c&#x00F3;digo dentro del contenedor es responsabilidad del equipo de desarrollo. Pero el acceso y las pol&#x00ED;ticas, compete a explotaci&#x00F3;n</p></list-item>
</list>
<p>Sin embargo, en esta asignatura s&#x00ED; configuraremos un servidor de ssh dentro de un contenedor, porque el objetivo es ense&#x00F1;ar c&#x00F3;mo funciona el acceso por ssh, que es lo habitual en m&#x00E1;quinas f&#x00ED;sicas y m&#x00E1;quinas virtuales tradicionales</p>
<p>&#x00BF;Es necesario acceder por ssh?</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Para actualizar el sistema</p>
<p>No. El contenedor entonces tendr&#x00ED;a estado (las actualizaciones). Lo recomendable es crear un nuevo contenedor con la actualizaci&#x00F3;n.</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_80"/>Para ver logs</p>
<p>No. El contenedor tendr&#x00ED;a estado. Lo recomendable es llevar los logs a un volumen</p></list-item>
<list-item><label>&#x25FE;</label> <p>Para iniciar y detener demonios</p>
<p>No. Se pueden enviar se&#x00F1;ales</p></list-item>
<list-item><label>&#x25FE;</label> <p>Para editar la configuraci&#x00F3;n</p>
<p>No. Lo recomendable es crear un nuevo contenedor</p></list-item>
<list-item><label>&#x25FE;</label> <p>Para depurar el servicio</p>
<p>No. Se puede abrir una shell desde el servidor de contenedores</p></list-item>
</list>
<p>Si a pesar de esto queremos instalar sshd en un contenedor:</p>
<p>Dockerfile</p>
<preformat>
FROM ubuntu:20.04
RUN apt update &#x0026;&#x0026; apt install -y openssh-serve
RUN mkdir /var/run/sshd
# Con sshd, ENV no funciona. Para fijar una variable de entorno:
# RUN echo &#x0022;export MI_VARIABLE=mivalor&#x0022; &#x003E;&#x003E; /etc/profile
COPY entrypoint.sh /
EXPOSE 22
ENTRYPOINT [&#x0022;/entrypoint.sh&#x0022;]
</preformat>
<p>entrypoint.sh</p>
<preformat>
#!/bin/bash
/usr/sbin/sshd
/bin/bash
</preformat>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>La instrucci&#x00F3;n EXPOSE indica en qu&#x00E9; puerto (TCP) atiende peticiones el contenedor</p></list-item>
<list-item><label>&#x25FE;</label> <p>Realmente esta instrucci&#x00F3;n no hace nada, es solo un <italic>mensaje</italic> del autor del contenedor para quien vaya a usar el contenedor</p></list-item>
</list>
</sec>
<sec id="c8-s5-3">
<title><bold>Configuraci&#x00F3;n en espa&#x00F1;ol</bold></title>
<p>Las im&#x00E1;genes base de las distribuciones son esqueletos m&#x00ED;nimos, normalmente tendremos que personalizarlas</p>
<p><target target-type="page" id="pges_81"/>Por ejemplo, para configurar el idioma. En nuestro caso, espa&#x00F1;ol. Instalaremos en la imagen el paquete locales, invocaremos a localedef con los par&#x00E1;metros adecuados y definiremos la variable de entorno LANG</p>
<preformat>
FROM ubuntu:20.04
RUN apt update &#x0026;&#x0026;apt upgrade -y &#x0026;&#x0026; \
&#x00A0;&#x00A0;apt install -y locales &#x0026;&#x0026; \
&#x00A0;&#x00A0;localedef -i es_ES -c -f UTF-8 \
&#x00A0;&#x00A0;-A /usr/share/locale/locale.alias es_ES.UTF-8
ENV LANG es_ES.UTF-8
COPY entrypoint.sh /
ENTRYPOINT [&#x0022;/entrypoint.sh&#x0022;]
</preformat>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>La instrucci&#x00F3;n ENV del Dockerfile define variables de entorno dentro de la imagen</p></list-item>
</list>
<p>Lo habitual es usar una &#x00FA;nica instrucci&#x00F3;n RUN en cada Dockerfile, para evitar las im&#x00E1;genes intermedias.</p>
<p>Pero tambi&#x00E9;n podemos usar varias instrucciones, para que resulte m&#x00E1;s legible.</p>
<p>Ejemplo:</p>
<preformat>
FROM ubuntu:20.04
RUN apt update &#x0026;&#x0026; apt upgrade -y
RUN apt install -y locales
RUN localedef -i es_ES -c -f UTF-8 \
&#x00A0;&#x00A0;-A /usr/share/locale/locale.alias es_ES.UTF-8 ENV LANG es_ES.UTF-8
COPY entrypoint.sh /
ENTRYPOINT [&#x0022;/entrypoint.sh&#x0022;]
</preformat>
<p>Observa que</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>El slash invertido al final de l&#x00ED;nea (\) une dos l&#x00ED;neas f&#x00ED;sicas en una misma l&#x00ED;nea l&#x00F3;gica</p></list-item>
<list-item><label>&#x25FE;</label> <p>El doble ampersand (<code>&#x0026;&#x0026;</code>) separa sentencias dentro de la misma instrucci&#x00F3;n RUN</p></list-item>
</list>
</sec>
<sec id="c8-s5-4">
<title><target target-type="page" id="pges_82"/><bold>Sesiones gr&#x00E1;ficas</bold></title>
<p>En un contenedor podemos lanzar aplicaciones gr&#x00E1;ficas</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Si el cliente y el servidor de Docker est&#x00E1;n en la misma m&#x00E1;quina y ambas son Unix, podemos usar <italic>X11 Forwarding</italic>
<preformat>
docker run -ti --rm \
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;-e DISPLAY=$DISPLAY \
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;-v /tmp/.X11-unix:/tmp/.X11-unix \
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;mi_imagen
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Una soluci&#x00F3;n m&#x00E1;s general es VNC</p>
<p>Aqu&#x00ED; se describe:</p>
<p><code><ext-link ext-link-type="uri" xlink:href="http://gsyc.urjc.es/~mortuno/vnc.pdf">http://gsyc.urjc.es/~mortuno/vnc.pdf</ext-link></code></p></list-item>
</list>
<fig><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-9.jpg"/></fig>
<p><target target-type="page" id="pges_83"/><code><ext-link ext-link-type="uri" xlink:href="https://www.vagrantup.com">https://www.vagrantup.com</ext-link></code></p>
</sec>
<sec id="c8-s5-5">
<title><bold>Vagrant</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Es una herramienta para construir y gestionar entornos de m&#x00E1;quinas virtuales.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Creado en 2010, es software libre, muy popular</p></list-item>
<list-item><label>&#x25FE;</label> <p>Funciona sobre Linux, FreeBSD, macOS, y Microsoft Windows</p></list-item>
<list-item><label>&#x25FE;</label> <p>Soporta las principales plataformas de virtualizaci&#x00F3;n: Docker, VirtualBox, VMware, AWS, Azure, entre otras.</p>
<p>Vagrant las denomina <italic>providers</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>Su funci&#x00F3;n b&#x00E1;sica es reemplazar el interfaz (tanto gr&#x00E1;fico como de texto) de estas plataformas, proporcionando un interfaz de texto, programable y homog&#x00E9;neo que permite preparar las m&#x00E1;quinas, levantarlas, configurarlas, etc</p></list-item>
<list-item><label>&#x25FE;</label> <p>Usando Vagrant, resulta muy sencillo migrar entre diferentes tecnolog&#x00ED;as de virtualizaci&#x00F3;n</p></list-item>
<list-item><label>&#x25FE;</label> <p>Para la configuraci&#x00F3;n, se integra con Ansible, Chef y Puppet, entre otras</p></list-item>
</list>
</sec>
<sec id="c8-s5-6">
<title><bold>Uso de Vagrant</bold></title>
<p>Con Vagrant, es muy f&#x00E1;cil crear y poner en marcha una m&#x00E1;quina virtual, por ejemplo con VirtualBox</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Si no indicamos el <italic>provider</italic>, Vagrant usa VirtualBox. Es necesario haberlo instalado previamente</p></list-item>
<list-item><label>&#x25FE;</label> <p>No es necesario lanzar el GUI de VirtualBox, pero tambi&#x00E9;n podemos usarlo simult&#x00E1;neamente</p></list-item>
<list-item><label>&#x25FE;</label> <p>Todo lo relativo a una m&#x00E1;quina virtual a manejar con vagrant se guarda en un directorio denominado <italic>project directory</italic></p></list-item>
</list>
</sec>
<sec id="c8-s5-7">
<title><bold><target target-type="page" id="pges_84"/>Vagrant Box</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Vagrant cuenta con repositorios de im&#x00E1;genes preconfiguradas. Las denomina boxes. Hay boxes oficiales, y tambi&#x00E9;n cualquier usuario puede preparar sus boxes y hacerlos p&#x00FA;blicas gratuitamente</p>
<list list-type="bullet">
<list-item><p>Se pueden preparar boxes privados, estos son de pago</p></list-item>
</list></list-item>
</list>
</sec>
<sec id="c8-s5-8">
<title><bold>Puesta en marcha de un Box</bold></title>
<list list-type="order">
<list-item><p>Creamos en nuestro <italic>host</italic> el <italic>project directory</italic></p></list-item>
<list-item><p>Accedemos al <italic>project directory</italic></p></list-item>
<list-item><p>Ejecutamos <code>vagrant init &#x003C;NOMBRE_DE_BOX&#x003E;</code></p>
<p>p.e.</p>
<p><code>vagrant init ubuntu/focal64</code></p></list-item>
<list-item><p>Encendemos la m&#x00E1;quina
<preformat>
vagrant up
</preformat></p></list-item>
<list-item><p>Entramos en la m&#x00E1;quina
<preformat>
vagrant ssh
</preformat></p></list-item>
</list>
<p>Observa que nunca indicamos con qu&#x00E9; m&#x00E1;quina queremos trabajar, basta con lanzar la orden <code>vagrant</code> desde el <italic>project directory</italic> que necesitemos en cada momento</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Vagrant redirecciona autom&#x00E1;ticamente un puerto del host al puerto 22 del guest para poder hacer ssh</p>
<p>Si est&#x00E1; libre, el 2222. Si no, usar&#x00E1; otro. Lo indicar&#x00E1; en el arranque de la m&#x00E1;quina</p></list-item>
<list-item><label>&#x25FE;</label> <p>Vagrant monta autom&#x00E1;ticamente el <italic>project directory</italic> del <italic>host</italic> en el directorio <code>/vagrant</code> del <italic>guest</italic></p></list-item>
</list>
</sec>
<sec id="c8-s5-9">
<title><bold>Parada de una m&#x00E1;quina</bold></title>
<p>Tres formas distintas:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>vagrant suspend</code></p>
<p>Duerme la m&#x00E1;quina</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_85"/><code>vagrant halt</code></p>
<p>Para la m&#x00E1;quina</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>vagrant destroy</code></p>
<p>Para la m&#x00E1;quina, borra su imagen y todos sus ficheros</p></list-item>
</list>
<p>Naturalmente, estas instrucciones debemos ejecutarlas desde la m&#x00E1;quina donde est&#x00E1; vagrant, esto es, el <italic>host</italic>, no el <italic>guest</italic></p>
</sec>
<sec id="c8-s5-10">
<title><bold>Vagrantfile</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>La orden <code>vagrant init</code> crea autom&#x00E1;ticamente en el <italic>project directory</italic> un fichero <code>Vagrantfile</code>, que es el fichero de configuraci&#x00F3;n de la m&#x00E1;quina virtual</p></list-item>
<list-item><label>&#x25FE;</label> <p>Las opciones de configuraci&#x00F3;n se escriben entre las l&#x00ED;neas
<preformat>
Vagrant.configure(&#x0022;2&#x0022;) do |config|
</preformat></p>
<p>y</p>
<p><code>end</code></p></list-item>
</list>
<p>Cambiar el nombre de la m&#x00E1;quina virtual (el nombre que usa el <italic>provider</italic></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>config.vm.hostname= &#x0022;MI_MAQUINA&#x0022;</code></p></list-item>
</list>
<p>Cambiar el nombre de host:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>config.vm.define &#x0022;MI_MAQUINA&#x0022; # sin &#x0027;=&#x0027;</code></p></list-item>
</list>
<p>Redireccionamiento de un puerto del <italic>host</italic> al <italic>guest</italic> al</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>config.vm.network &#x0022;forwarded_port&#x0022;, guest: 80, host: 8080,</code></p>
<p>host_ip: &#x0022;127.0.0.1&#x0022;</p></list-item>
</list>
<p>Los boxes preconfigurados suelen tener un usuario <italic>vagrant</italic>, su claves se guardan en el <italic>project directory</italic>, en</p>
<preformat>
.vagrant/machines/default/virtualbox/private_key
</preformat>
<fig id="fig2">
<label><bold>Figura 2:</bold></label>
<caption><title><target target-type="page" id="pges_86"/>El Sistema Operativo</title></caption>
<graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-10.jpg"/>
</fig>
</sec>
</sec>
</body>
</book-part>
<book-part id="c9" book-part-type="chapter">
<book-part-meta>
<book-part-id book-part-id-type="publisher-id">URJC</book-part-id>
<title-group>
<label>9.</label>
<title><target target-type="page" id="pges_87"/>SHELL: INT&#x00C9;RPRETE DE &#x00D3;RDENES</title>
</title-group>
</book-part-meta>
<body>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>La shell m&#x00E1;s habitual es bash, pero hay muchas otras sh, csh, dash</p></list-item>
<list-item><label>&#x25FE;</label> <p>Las <bold>&#x00F3;rdenes</bold> generalmente son solo peque&#x00F1;os programas ejecutables</p></list-item>
<list-item><label>&#x25FE;</label> <p>El nombre original es shell command. En espa&#x00F1;ol puede decirse comando, orden o mandato.</p></list-item>
</list>
<sec id="c9-s1">
<label><bold>9.1.</bold></label>
<title><bold>&#x00BF;Qui&#x00E9;n soy? &#x00BF;D&#x00F3;nde estoy? &#x00BF;Qu&#x00E9; tengo?</bold></title>
<sec id="c9-s1-1">
<title><bold>&#x00BF;Qui&#x00E9;n soy? &#x00BF;D&#x00F3;nde estoy? &#x00BF;Qu&#x00E9; tengo?</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>whoami</code></p>
<p>Muestra el usuario</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>id</code></p>
<p>Muestra usuario y grupos</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>uname</code>
<preformat>
uname -a
</preformat></p>
<p>Versi&#x00F3;n de Linux</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>hostname</code></p>
<p>Nombre de m&#x00E1;quina</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>pwd</code></p>
<p>Directorio de trabajo actual</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>w</code></p>
<p>Usuarios conectados a la m&#x00E1;quina</p></list-item>
<list-item><label>&#x25FE;</label> <p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>du</code></p></td>
<td valign="top" align="left"><p>Espacio de disco ocupado por los ficheros de un directorio</p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>du -s</code></p></td>
<td valign="top" align="left"><p>Espacio de disco ocupado por un directorio</p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>du -h</code></p></td>
<td valign="top" align="left"><p>Unidades legibles para un humano</p></td>
</tr>
</tbody>
</table>
</table-wrap></p></list-item>
<list-item><label>&#x25FE;</label> <p>df</p>
<p>Espacio de disco libre</p></list-item>
<list-item><label>&#x25FE;</label> <p>lsblk -f</p>
<p>Listado de todos los discos (dispositivos de bloques)</p></list-item>
<list-item><label>&#x25FE;</label> <p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><target target-type="page" id="pges_88"/><code>ls -l</code></p></td>
<td valign="top" align="left"><p>Formato largo</p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>ls -a</code></p></td>
<td valign="top" align="left"><p>Muestra ficheros ocultos (empiezan por punto)</p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>ls -lh</code></p></td>
<td valign="top" align="left"><p>Formato largo, unidades legibles por humano</p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>ls -R</code></p></td>
<td valign="top" align="left"><p>Recursivo</p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>ls -ld</code></p></td>
<td valign="top" align="left"><p>Lista el directorio, no su contenido</p></td></tr>
</tbody></table>
</table-wrap></p></list-item>
</list>
<p>Unix es <italic>case sensitive</italic></p>
</sec></sec>
<sec id="c9-s2">
<label><bold>9.2.</bold></label>
<title><bold>Metacaracteres de la Shell</bold></title>
<sec id="c9-s2-1">
<title><bold>Metacaracteres de la Shell</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>$</code> Variable</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>*</code> 0 o m&#x00E1;s caracteres cualquiera</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>?</code> exactamente 1 car&#x00E1;cter cualquiera</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>[]</code> 1 car&#x00E1;cter de la clase</p></list-item>
</list>
<p>ejemplo:</p>
<p><code>ls *.txt</code></p>
<p>el shell lo expande a</p>
<p><code>ls texto1.txt texto2.txt texto3.txt</code></p>
<p>La orden recibe 3 argumentos, no sabe nada de metacaracteres</p>
</sec></sec>
<sec id="c9-s3">
<label><bold>9.3.</bold></label>
<title><bold>Funcionamiento de la shell</bold></title>
<sec id="c9-s3-1">
<title><bold>Funcionamiento (simplificado) de la shell</bold></title>
<p>La shell:</p>
<list list-type="order">
<list-item><p>Lee texto de fichero stdin (por ejemplo, el teclado). Aporta algunas facilidades al usuario (borrar, autocompletar)</p></list-item>
<list-item><p>Analiza el texto (expande metacaracteres y variables)</p></list-item>
<list-item><p>Toma la primera palabra y busca una orden con ese nombre en los directorios indicados por PATH</p></list-item>
<list-item><p>Si puede, ejecuta la orden y se queda dormida esperando a que acabe Por ejemplo<target target-type="page" id="pges_89"/>
<preformat>
koji@mazinger:~$ xcalc
</preformat></p>
<p>(Mientras usamos la calculadora, la shell permanece inactiva)</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Si queremos que la shell siga activa, lanzamos el proceso en segundo plano (<italic>background</italic>)
<preformat>
koji@mazinger:~$ xcalc&#x0026;
</preformat></p></list-item>
</list></list-item></list>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Una aplicaci&#x00F3;n lanzada sin &#x0026;, se dice que est&#x00E1; lanzada en primer plano (<italic>foreground</italic>).</p></list-item>
<list-item><label>&#x25FE;</label> <p>La shell se cierra con la orden exit. (O con ctrl d, que representa el fin de fichero)</p></list-item>
</list>
</sec>
<sec id="c9-s3-2">
<title><bold>Autocompletado</bold></title>
<p>Con frecuencia pasaremos a los mandatos nombres de fichero (como argumento). La funci&#x00F3;n de autocompletar evita teclear nombres completos</p>
<p>Supongamos que tenemos dos ficheros en el directorio actual</p>
<preformat>
.
|-- mi_fichero_del_martes
&#x0060;-- un_fichero_ejemplo
</preformat>
<p>No es necesario teclear</p>
<preformat>
koji@mazinger:~$ ls -l mi_fichero_del_martes
</preformat>
<p>Como solo hay un fichero que empiece por <italic>mi</italic>, basta escribir</p>
<preformat>
koji@mazinger:~$ ls -l mi
</preformat>
<p>y luego pulsar tab</p>
<p>Si hay m&#x00E1;s de un fichero que empiece por <italic>mi</italic></p>
<preformat>
.
|-- mi_fichero_del_martes
|-- mi_fichero_del_miercoles
&#x0060;-- un_fichero_ejemplo
</preformat>
<preformat>
koji@mazinger:~$ ls -l mi_fichero_del_m
mi_fichero_del_martes mi_fichero_del_miercoles
</preformat>
<p><target target-type="page" id="pges_90"/>Autocompletar rellena hasta donde puede, nos ofrece los ficheros que encajan en lo que hemos escrito, y espera a que introduzcamos una letra m&#x00E1;s para deshacer la ambig&#x00FC;edad (en este ejemplo, &#x0027;<code>a</code>&#x0027; o &#x0027;<code>i</code>&#x0027;)</p>
<p>La shell tambi&#x00E9;n autocompleta nombres de ejecutables (si tienen permiso de ejecuci&#x00F3;n y est&#x00E1;n en el path)</p>
<preformat>
koji@mazinger:~$ pass&#x003C;TAB&#x003E;
</preformat>
<p>Se autocompleta a</p>
<preformat>
koji@mazinger:~$ passwd
</preformat>
<p>De esta manera no hace falta teclear todas las letras. Ni recordar el nombre exacto de &#x00F3;rdenes largas, basta saber c&#x00F3;mo empiezan</p>
<p>history</p>
<p>La shell recuerda las &#x00FA;ltimas &#x00F3;rdenes ejecutadas. Podemos desplazarnos sobre ellas con los cursores arriba/abajo</p>
</sec></sec>
<sec id="c9-s4">
<label><bold>9.4.</bold></label>
<title><bold>Variables</bold></title>
<sec id="c9-s4-1">
<title><bold>Variables</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>variable=valor</code>
<preformat>
echo $variable
</preformat></p>
<p>Sin espacios antes y despues del igual</p>
<p>con $ para acceder al contenido de la variable</p>
<p>sin $ en la asignaci&#x00F3;n</p>
<p>s&#x00F3;lo son visibles en ese proceso</p></list-item>
</list>
<preformat>
nombre=juan
echo $nombre
</preformat>
</sec>
<sec id="c9-s4-s1">
<label><bold>9.4.1.</bold></label>
<title><bold>Variables de entorno</bold></title>
<p>Variables de entorno</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>export VARIABLE=valor</code></p>
<p>hace que los procesos hijos del proceso donde se declara la variable, la reciban. Por convenio se usan may&#x00FA;sculas</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_91"/>Para que el cambio sea permanente, hay que exportar la variable en alg&#x00FA;n fichero de configuraci&#x00F3;n como p.e. .bashrc</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>printenv</code></p>
<p>muestra todas las variables de entorno</p></list-item>
<list-item><label>&#x25FE;</label> <p>HOME</p></list-item>
<list-item><label>&#x25FE;</label> <p>HOSTNAME</p></list-item>
<list-item><label>&#x25FE;</label> <p>USER</p></list-item>
<list-item><label>&#x25FE;</label> <p>PATH</p>
<p>Contiene la lista de directorios donde la shell buscar&#x00E1; los ejecutables (si no se indica path expl&#x00ED;cito)</p></list-item>
</list>
</sec>
<sec id="c9-s4-s1-1">
<title><bold>La variable de entorno HOME</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Indica el directorio <italic>hogar</italic> de un usuario: el sitio donde se espera que cada usuario escriba sus cosas<target target-type="page" id="pges_92"/>
<preformat>
koji@mazinger:~$ echo $HOME
/home/koji
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Se le suele llamar $HOME, pero esto no es muy preciso</p>
<list list-type="bullet">
<list-item><p>La variable se llama HOME, el d&#x00F3;lar se antepone a todas las variables en bash cuando se est&#x00E1;n referenciando (y no cuando se asignan)</p></list-item>
<list-item><p>Es un error frecuente intentar usar $HOME en otros lenguajes o en cualquier programa. Solo es v&#x00E1;lido en bash y shells similares</p></list-item>
</list></list-item>
</list>
</sec>
<sec id="c9-s4-s1-2">
<title><bold>Virgulilla</bold></title>
<p>La virgulilla (~) representa el directorio <italic>home</italic> de un usuario</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Equivale a <code>$HOME</code>, con la ventaja de que se puede usar en muchos lenguajes, aplicaciones y librer&#x00ED;as (no todos)</p></list-item>
<list-item><label>&#x25FE;</label> <p>No aparece en los teclados, pero est&#x00E1; accesible en <code>AltGr 4</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>Seguida de un nombre de usuario, representa el <italic>HOME</italic> de ese usuario
<preformat>
koji@mazinger:~$ echo ~jperez
/home/jperez
</preformat></p></list-item>
</list>
<p>Si el nombre del usuario no es una cadena literal sino una variable es necesario volver a evaluar la expresi&#x00F3;n</p>
<preformat>
koji@mazinger:~$ nombre=koji
koji@mazinger:~$ echo ~$nombre
~koji
koji@doublas:~$ eval echo ~$nombre
/home/koji
</preformat>
</sec>
<sec id="c9-s4-s1-3">
<title><bold>La variable de entorno PATH</bold></title>
<p>Un usuario principiante ejecuta</p>
<preformat>
koji@mazinger:~/pruebas$ ls -l
total 4
-rw-r--r-- 1 koji koji 27 2009-10-07 19:02 holamundo
</preformat>
<p>Intenta invocar el mandato <italic>holamundo</italic> escribiendo</p>
<preformat>
koji@mazinger:~/pruebas$ holamundo
</preformat>
<p><target target-type="page" id="pges_93"/>pero obtiene</p>
<preformat>
bash: holamundo: orden no encontrada
</preformat>
<p>Problema 1</p>
<p>El fichero no ten&#x00ED;a permisos de ejecuci&#x00F3;n</p>
<p>Problema 1: Soluci&#x00F3;n</p>
<preformat>
koji@mazinger:~/pruebas$ chmod ugo+x holamundo
</preformat>
<p>&#x00BF;Problema resuelto?</p>
<preformat>
koji@mazinger:~/pruebas$ ls -l
total 4
-rwxr-xr-x 1 koji koji 27 2009-10-07 19:02 holamundo
</preformat>
<p>No ha bastado. El usuario vuelve a ejecutar</p>
<preformat>
koji@mazinger:~/pruebas$ holamundo
</preformat>
<p>pero vuelve a obtener</p>
<p>bash: holamundo: orden no encontrada</p>
<p>Problema 2</p>
<p>Aunque el fichero est&#x00E1; en el directorio actual (directorio <italic>punto</italic>), la shell no lo buscar&#x00E1; all&#x00ED;, sino donde indique la variable de entorno PATH, que contiene una lista de directorios, separados por el car&#x00E1;cter <italic>dos puntos</italic></p>
<preformat>
koji@mazinger:~/pruebas$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
</preformat>
<p>Lo buscar&#x00E1; en <code>/usr/local/sbin</code></p>
<p>Si no lo encuentra, lo buscar&#x00E1; en <code>/usr/local/bin</code></p>
<p>Si sigue sin encontrarlo, lo buscar&#x00E1; en <code>/usr/local/sbin</code></p>
<p>etc</p>
<p>Pero no lo buscar&#x00E1; en el directorio <italic>punto</italic></p>
<p>Problema 2: Soluci&#x00F3;n 1 (recomendada)</p>
<p>Invocar el mandato indicando expl&#x00ED;citamente que el fichero est&#x00E1; en el directorio <italic>punto</italic></p>
<preformat>
koji@mazinger:~/pruebas$ ./holamundo
&#x00A1;hola mundo!
</preformat>
<p>Problema 2: Soluci&#x00F3;n 2</p>
<p>Indicar el trayecto absoluto del mandato</p>
<preformat>
koji@mazinger:~/pruebas$ /home/koji/pruebas/holamundo
&#x00A1;hola mundo!
</preformat>
<p>Problema 2: Soluci&#x00F3;n 3</p>
<p>Modificamos la variable de entorno PATH para a&#x00F1;adir al final el directorio <italic>punto</italic></p>
<p><target target-type="page" id="pges_94"/>Como queremos que el cambio sea permanente, debemos modificar la variable en un fichero de configuraci&#x00F3;n <xref ref-type="fn" rid="FN3"><sup>3</sup></xref> , por ejemplo ~/.bashrc</p>
<preformat>
export PATH=$PATH:.
</preformat>
<p>El cambio no se produce de inmediato, sino cuando se ejecute de nuevo <code>~/.bashrc</code></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Al invocarlo expl&#x00ED;citamente
<preformat>
koji@mazinger:~/pruebas$ source ~/.bashrc
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Al abrir una nueva terminal</p></list-item>
</list>
<p>Problema 2: Soluci&#x00F3;n 4 &#x00A1;Muy peligrosa!</p>
<p>Modificamos la variable de entorno PATH para a&#x00F1;adir al principio el directorio <italic>punto</italic></p>
<preformat>
export PATH=.:$PATH
</preformat>
<p>Supongamos que un atacante escribe un script con el nombre ls y el contenido</p>
<preformat>
#!/bin/bash
rm -rf $HOME
</preformat>
<p>Al escribir la orden ls, se ejecutar&#x00ED;a este script, y no /bin/ls</p>
</sec></sec>
<sec id="c9-s5">
<label><bold>9.5.</bold></label>
<title><bold>Ficheros</bold></title>
<sec id="c9-s5-s1">
<label><bold>9.5.1.</bold></label>
<title><bold>&#x00C1;rbol de directorios</bold></title>
<sec id="c9-s5-s1-1">
<title><bold>&#x00C1;rbol de directorios</bold></title>
<fig><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-11.jpg"/></fig>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>&#x00C1;rbol, todo cuelga de un &#x00FA;nico directorio raiz</p></list-item>
<list-item><label>&#x25FE;</label> <p>Dentro de cada directorio, habr&#x00E1; ficheros o subdirectorios</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_95"/>jerarqu&#x00ED;a cl&#x00E1;sica unix:</p>
<list list-type="bullet">
<list-item><p>/bin</p></list-item>
<list-item><p>/etc</p></list-item>
<list-item><p>/home</p></list-item>
<list-item><p>/var</p></list-item>
<list-item><p>(...)</p></list-item>
</list></list-item>
</list>
</sec>
<sec id="c9-s5-s1-2">
<title><bold>Nombres de fichero</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Hasta 256 caracteres</p></list-item>
<list-item><label>&#x25FE;</label> <p>May&#x00FA;sculas y min&#x00FA;sculas son distintas</p>
<list list-type="bullet">
<list-item><p>Se puede tener en un mismo directorio los ficheros ejemplo, EJEMPLO y <code>EjemPlO</code></p></list-item>
<list-item><p>Pero si llevamos estos ficheros a una unidad externa (pendrive, disco) que mantenga su formato por omisi&#x00F3;n (FAT32), deja de ser legal</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Los que empiezan por punto (.)se consideran ocultos (por defecto no se muestran), suelen usarse para ficheros o directorios de configuraci&#x00F3;n</p></list-item>
<list-item><label>&#x25FE;</label> <p>Casi cualquier car&#x00E1;cter es legal, pero es preferible usar solo n&#x00FA;meros, letras, gui&#x00F3;n y barra baja.</p>
<list list-type="bullet">
<list-item><p>Es preferible evitar los espacios</p></list-item>
<list-item><p>Tambi&#x00E9;n es buena idea evitar e&#x00F1;es y tildes (Naturalmente, hablamos del nombre del fichero, no de su contenido)</p></list-item>
</list></list-item>
</list>
</sec></sec>
<sec id="c9-s5-s2">
<label><bold>9.5.2.</bold></label>
<title><target target-type="page" id="pges_96"/><bold>Permisos</bold></title>
<sec id="c9-s5-s2-1">
<title><bold>Permisos</bold></title>
<p>ls -l: Muestra los contenidos de los directorios en <bold>formato largo</bold>:</p>
<preformat>
drwxr-xr-x 2 jperez al-07-08 4096 2007-10-09 22:51 d1
-rw-r--r-- 1 jperez al-07-08 8152 2007-10-16 09:42 f1
-rw-r--r-- 1 jperez al-07-08 24 2007-10-16 &#x00A0;&#x00A0;09:42 f3
</preformat>
<p>El primer car&#x00E1;cter indica:</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>-</code></p></td>
<td valign="top" align="left"><p><code>Regular file - Fichero ordinario</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>d</code></p></td>
<td valign="top" align="left"><p><code>Directory - Directorio</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>l</code></p></td>
<td valign="top" align="left"><p><code>(Symbolic) Link - Enlace simb&#x00F3;lico</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>p</code></p></td>
<td valign="top" align="left"><p><code>Named pipe - Pipe con nombre</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>s</code></p></td>
<td valign="top" align="left"><p><code>Socket - Socket</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>c</code></p></td>
<td valign="top" align="left"><p><code>Character device - Dispositivo orientado a car&#x00E1;cter</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>b</code></p></td>
<td valign="top" align="left"><p><code>Block device - Dispositivo orientado a bloque</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<p>Para cada entrada, aparece, adem&#x00E1;s:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>permisos: Los 9 primeros caracteres</p></list-item>
<list-item><label>&#x25FE;</label> <p>n&#x00FA;mero de nombres del fichero (enlaces duros)</p></list-item>
<list-item><label>&#x25FE;</label> <p>usuario del due&#x00F1;o</p></list-item>
<list-item><label>&#x25FE;</label> <p>grupo del due&#x00F1;o</p></list-item>
<list-item><label>&#x25FE;</label> <p>tama&#x00F1;o en bytes</p></list-item>
<list-item><label>&#x25FE;</label> <p>fecha y hora de la &#x00FA;ltima modificaci&#x00F3;n</p></list-item>
<list-item><label>&#x25FE;</label> <p>nombre</p></list-item>
</list>
<p>En espa&#x00F1;ol habitualmente usamos la palabra <italic>permisos</italic> para referirnos al</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>access mode, cuya traducci&#x00F3;n literal ser&#x00ED;a modo de acceso</p></list-item>
</list>
<p>Los permisos se indican en una secuencia de caracteres como p.e.</p>
<preformat>
rwxr-x---
</preformat>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Los primeros tres caracteres representan los permisos para el due&#x00F1;o del fichero</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_97"/>Los siguientes tres caracteres, los permisos de los usuarios del mismo grupo que el fichero</p></list-item>
<list-item><label>&#x25FE;</label> <p>Los &#x00FA;ltimos tres caracteres, los permismos del resto de usuarios</p></list-item>
</list>
<fig><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-12.jpg"/></fig>
<p>En cada grupo de tres caracteres</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>La letra <code>r</code> en la primera posici&#x00F3;n significa permiso de lectura concedido</p></list-item>
<list-item><label>&#x25FE;</label> <p>La letra <code>w</code> en la segunda posici&#x00F3;n significa permiso de escritura concedido</p></list-item>
<list-item><label>&#x25FE;</label> <p>La letra <code>x</code> en la tercera posici&#x00F3;n significa permiso de ejecuci&#x00F3;n concedido</p></list-item>
<list-item><label>&#x25FE;</label> <p>Si en vez de la letra aparece un gui&#x00F3;n, el permiso est&#x00E1; denegado</p></list-item>
</list>
<p>Los permisos se suelen indicar tambi&#x00E9;n como un d&#x00ED;gito decimal<xref ref-type="fn" rid="FN4"><sup>4</sup></xref></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Si el permiso est&#x00E1; concedido, se considera un d&#x00ED;gito 1 binario</p></list-item>
<list-item><label>&#x25FE;</label> <p>En otro caso, un d&#x00ED;gito 0 binario</p>
<p><table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="center"><p><code>Permisos</code></p></td>
<td valign="top" align="center"><p><code>Binario</code></p></td>
<td valign="top" align="center"><p><code>Decimal</code></p></td>
</tr>
<tr>
<td valign="top" align="center"><p><code>---</code></p></td>
<td valign="top" align="center"><p><code>000</code></p></td>
<td valign="top" align="center"><p><code>0</code></p></td>
</tr>
<tr>
<td valign="top" align="center"><p><code>--x</code></p></td>
<td valign="top" align="center"><p><code>001</code></p></td>
<td valign="top" align="center"><p><code>1</code></p></td>
</tr>
<tr>
<td valign="top" align="center"><p><code>-w-</code></p></td>
<td valign="top" align="center"><p><code>010</code></p></td>
<td valign="top" align="center"><p><code>2</code></p></td>
</tr>
<tr>
<td valign="top" align="center"><p><code>-wx</code></p></td>
<td valign="top" align="center"><p><code>011</code></p></td>
<td valign="top" align="center"><p><code>3</code></p></td>
</tr>
<tr>
<td valign="top" align="center"><p><code>r--</code></p></td>
<td valign="top" align="center"><p><code>100</code></p></td>
<td valign="top" align="center"><p><code>4</code></p></td>
</tr>
<tr>
<td valign="top" align="center"><p><code>r-x</code></p></td>
<td valign="top" align="center"><p><code>101</code></p></td>
<td valign="top" align="center"><p><code>5</code></p></td>
</tr>
<tr>
<td valign="top" align="center"><p><code>rw-</code></p></td>
<td valign="top" align="center"><p><code>110</code></p></td>
<td valign="top" align="center"><p><code>6</code></p></td>
</tr>
<tr>
<td valign="top" align="center"><p><code>rwx</code></p></td>
<td valign="top" align="center"><p><code>111</code></p></td>
<td valign="top" align="center"><p><code>7</code></p></td>
</tr>
</tbody>
</table>
</table-wrap></p></list-item>
<list-item><label>&#x25FE;</label> <p>Permisos de un fichero:</p>
<list list-type="bullet">
<list-item><p>El de <bold>lectura</bold>: permite ver su contenido</p></list-item>
<list-item><p><target target-type="page" id="pges_98"/>El de <bold>escritura</bold>: permite modificar su contenido</p></list-item>
<list-item><p>El de <bold>ejecuci&#x00F3;n</bold>: permite ejecutarlo</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Permisos de un directorio:</p>
<list list-type="bullet">
<list-item><p>El de <bold>lectura</bold>: permite hacer ls del contenido</p></list-item>
<list-item><p>El de <bold>escritura</bold>: permite crear y borrar ficheros y subdirectorios dentro de &#x00E9;l</p></list-item>
<list-item><p>El de <bold>ejecuci&#x00F3;n</bold>: permite hacer <code>cd</code> a &#x00E9;l</p></list-item>
</list></list-item>
</list>
<p>Para cambiar permisos se usa chmod, que tiene dos sintaxis equivalentes, se puede usar la que resulte m&#x00E1;s c&#x00F3;moda</p>
<list list-type="order">
<list-item><p>chmod 754 mi_fichero</p>
<p>No importan los permisos que tuviera previamente el fichero, pasa a tener:</p>
<p><table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>7 5 4</code></p></td>
<td valign="top" align="left"><p><code>(decimal)</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>111 101 100</code></p></td>
<td valign="top" align="left"><p><code>(binario)</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>rwx r-x r--</code></p></td>
<td valign="top" align="left"><p></p></td>
</tr>
</tbody>
</table>
</table-wrap></p></list-item>
<list-item><p><preformat>
chmod [ugo] [+-] [rwx] mi_fichero
chmod o+x mi_fichero
</preformat></p>
<p>A partir de los permisos que tuviera el fichero, se suman o se restan los permisos indicados a u,g,o (user, group, other)</p></list-item>
</list>
</sec>
<sec id="c9-s5-s2-2">
<title><bold>Permisos de los directorios</bold></title>
<p>chmod -R Cambia permisos recursivamente</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>r</code> y <code>x</code> normalmente van juntos. (Ambos o ninguno).</p>
<p>Permiten entrar en el directorio y listar</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>w</code> permite a&#x00F1;adir a&#x00F1;adir ficheros o borrarlos</p></list-item>
</list>
<p>Muy Importante:</p>
<p>Comprueba los permisos de tu HOME, en muchos sistemas por omisi&#x00F3;n est&#x00E1; abierto</p>
<p><target target-type="page" id="pges_99"/>Atenci&#x00F3;n,</p>
<p>un fichero sin permisos de escritura, p.e. rwxr-xr-x pero con permiso de escritura en el directorio que lo contiene, rwxrwxrwx no podr&#x00E1; ser modificado pero s&#x00ED; borrado o renombrado</p>
</sec></sec>
<sec id="c9-s5-s3">
<label><bold>9.5.3.</bold></label>
<title><bold>path</bold></title>
<sec id="c9-s5-s3-1">
<title><bold>Directorios Especiales</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Todo directorio contiene dos subdirectorios especiales:</p>
<p><table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>.</code></p></td>
<td valign="top" align="left"><p><code>El subdirectorio . de un directorio es &#x00E9;l mismo</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>..</code></p></td>
<td valign="top" align="left"><p><code>El subdirectorio .. de un directorio es su directorio padre</code></p></td>
</tr>
</tbody>
</table>
</table-wrap></p>
<p><fig><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-13.jpg"/></fig></p></list-item>
<list-item><label>&#x25FE;</label> <p>Ejemplos:</p>
<list list-type="bullet">
<list-item><p>El subdirectorio <code>.</code></p>
<p>de <code>juan</code> es <code>juan</code></p></list-item>
<list-item><p>El subdirectorio <code>..</code></p>
<p>de <code>luis</code> es <code>fuenla</code></p></list-item>
<list-item><p>El subdirectorio <code>..</code></p>
<p>de <code>home</code> es <code>/</code></p></list-item>
</list></list-item>
</list>
</sec></sec>
</sec>
<sec id="c9-s6">
<label><bold>9.6.</bold></label>
<title><target target-type="page" id="pges_100"/><bold>Operaciones b&#x00E1;sicas con ficheros y directorios</bold></title>
<sec id="c9-s6-1">
<title><bold>touch</bold></title>
<p>Cambia la fecha a un fichero, o lo crea si no existe</p>
<preformat>
touch &#x003C;fichero&#x003E;
</preformat>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Si <code>&#x003C;fichero&#x003E;</code> existe, le pone la fecha actual</p></list-item>
<list-item><label>&#x25FE;</label> <p>Si <code>&#x003C;fichero&#x003E;</code> no existe, crea un fichero vac&#x00ED;o con este nombre</p></list-item>
</list>
<preformat>
touch -d &#x003C;fecha/hora&#x003E; &#x003C;fichero&#x003E;
</preformat>
<p>Modifica la fecha de &#x00FA;ltimo acceso al fichero</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>touch -d 2007-02-28 fichero</code></p></td>
<td valign="top" align="left"><p><code># cambia la fecha</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>touch -d 15:41 fichero</code></p></td>
<td valign="top" align="left"><p><code># cambia la fecha</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
</sec>
<sec id="c9-s6-2">
<title><bold>mkdir: Creaci&#x00F3;n de directorios</bold></title>
<p>mkdir: Crea directorios (<italic>make directory</italic>)</p>
<preformat>
&#x25FE;mkdir &#x003C;fichero&#x003E;
</preformat>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>mkdir d3</code></p>
<p>Crea d3 como subdirectorio del directorio actual</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>mkdir d4 d5</code></p>
<p>Crea d4 y d5 como subdirectorios del directorio de trabajo actual</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>mkdir /tmp/ppp</code></p>
<p>Crea el directorio <code>/tmp/ppp</code></p></list-item>
<list-item><label>&#x25FE;</label> <p><code>mkdir -p d6/d7</code></p>
<p>Crea debajo de directorio de trabajo <code>d6</code> (si no existe), y crea <code>d7</code> debajo de <code>d6</code></p></list-item>
</list>
</sec>
<sec id="c9-s6-3">
<title><bold>Copiar, mover y renombrar</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>La &#x00F3;rden cp copia ficheros</p></list-item>
<list-item><label>&#x25FE;</label> <p>La &#x00F3;rden mv mueve y renombra ficheros</p></list-item>
</list>
<p>En primer lugar mostraremos el uso b&#x00E1;sico, despu&#x00E9;s las opciones completas</p>
<p>Copiar un fichero:</p>
<p><target target-type="page" id="pges_101"/>tengo</p>
<preformat>
/tmp/probando/quijote.txt
</preformat>
<p>quiero</p>
<preformat>
/tmp/probando/quijote.txt
/tmp/probando/quijote_repetido.txt
</preformat>
<p>hago</p>
<preformat>
cd /tmp/probando
cp quijote.txt quijote_repetido.txt
Renombrar un fichero:
</preformat>
<p>tengo</p>
<preformat>
/tmp/probando/quijote.txt
</preformat>
<p>quiero</p>
<preformat>
/tmp/probando/don_quijote.txt
</preformat>
<p>hago</p>
<preformat>
cd /tmp/probando
mv quijote.txt don_quijote.txt
</preformat>
<p>Copiar un fichero en un directorio distinto</p>
<preformat>
tengo
/tmp/probando/quijote.txt
</preformat>
<p>quiero</p>
<preformat>
/tmp/probando/quijote.txt
/tmp/otro_probando/quijote.txt
</preformat>
<p>voy al directorio destino</p>
<preformat>
cd /tmp/otro_probando/
</preformat>
<preformat>
#copio &#x0022;el fichero&#x0022; &#x00A0;&#x00A0;&#x0022;aqu&#x00ED;&#x0022;
cp /tmp/probando/quijote.txt .
</preformat>
<p><target target-type="page" id="pges_102"/>Mover un fichero a un directorio distinto</p>
<p>tengo</p>
<preformat>
/tmp/probando/quijote.txt
</preformat>
<p>quiero</p>
<preformat>
/tmp/otro_probando/quijote.txt
</preformat>
<p>voy al destino</p>
<preformat>
cd /tmp/otro_probando/
</preformat>
<preformat>
# muevo &#x0022;el fichero&#x0022; &#x0022;aqu&#x00ED;&#x0022;
mv /tmp/probando/quijote.txt .
</preformat>
</sec>
<sec id="c9-s6-4">
<title><bold>cp: Copiar 1 fichero ordinario</bold></title>
<preformat>
cp &#x003C;origen&#x003E; &#x003C;destino&#x003E;
</preformat>
<p>cp (<italic>copy</italic>) con dos argumentos. <code>&#x003C;origen&#x003E;</code> es un fichero ordinario</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Si el segundo argumento es un directorio</p>
<p>Hace una copia del fichero <code>&#x003C;origen&#x003E;</code> dentro del directorio <code>&#x003C;destino&#x003E;</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>Si el segundo argumento NO es un directorio (es un fichero o no existe nada con ese nombre)</p>
<p>Hace una copia del fichero <code>&#x003C;origen&#x003E;</code> y le pone como nombre <code>&#x003C;destino&#x003E;</code></p></list-item>
</list>
<p>Como siempre, tanto <code>&#x003C;origen&#x003E;</code> como <code>&#x003C;destino&#x003E;</code> pueden indicarse con trayecto relativo o con trayecto absoluto</p>
<p>Ejemplos:</p>
<preformat>
cp holamundo.py /tmp
cp ~/prueba.txt .
cp /home/jperez/prueba.txt prueba2.txt
</preformat>
</sec>
<sec id="c9-s6-5">
<title><bold>cp: Copiar 1 directorio</bold></title>
<preformat>
cp -r &#x003C;origen&#x003E; &#x003C;destino&#x003E;
</preformat>
<p>Si <code>&#x003C;origen&#x003E;</code> es un directorio, es necesario a&#x00F1;adir la opci&#x00F3;n <code>-r</code> (<italic>recursive</italic>)</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_103"/>Si <code>&#x003C;destino&#x003E;</code> es un fichero ordinario, se produce un error</p></list-item>
<list-item><label>&#x25FE;</label> <p>Si <code>&#x003C;destino&#x003E;</code> es un directorio, el directorio <code>&#x003C;origen&#x003E;</code> se copia dentro</p></list-item>
<list-item><label>&#x25FE;</label> <p>Si <code>&#x003C;destino&#x003E;</code> no existe, se le pone ese nombre a la copia</p></list-item>
</list>
<p>Ejemplos</p>
<preformat>
cp -r ~ /tmp
cp -r /var/tmp/aa .
cp -r ~ /tmp/copia_de_mi_home
</preformat>
</sec>
<sec id="c9-s6-6">
<title><bold>cp: Copiar varios ficheros ordinarios</bold></title>
<preformat>
cp &#x003C;origen1&#x003E; &#x003C;origen2&#x003E; .... &#x003C;destino&#x003E;
</preformat>
<p>cp (<italic>copy</italic>) con varios argumentos. Los ficheros <code>&#x003C;origen1&#x003E; &#x003C;origen2&#x003E; ....</code> se copian en el directorio <code>&#x003C;destino&#x003E;</code></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>&#x003C;destino&#x003E;</code> tiene que ser un directorio (o se producir&#x00E1; un error)</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>&#x003C;origen1&#x003E;, &#x003C;origen2&#x003E;,...</code> tienen que ser ficheros ordinarios (o un mensaje indicar&#x00E1; que no se est&#x00E1;n copiando)</p></list-item>
</list>
<p>Ejemplos:</p>
<preformat>
cp holamundo.py /home/jperez/prueba1.txt ../prueba2.txt /tmp
cp bin/*.py /tmp
</preformat>
</sec>
<sec id="c9-s6-7">
<title><bold>cp: Copiar varios ficheros o directorios</bold></title>
<preformat>
cp -r &#x003C;origen1&#x003E; &#x003C;origen2&#x003E; .... &#x003C;destino&#x003E;
</preformat>
<p>Este caso es id&#x00E9;ntico al anterior, solo que si <code>&#x003C;origen1&#x003E;</code> o <code>&#x003C;origen2&#x003E;</code> o ... son directorios, es necesaria la opci&#x00F3;n <code>-r</code></p>
<p>Ejemplos:</p>
<preformat>
cp -r holamundo.py /home/jperez /tmp
</preformat>
</sec>
<sec id="c9-s6-8">
<title><bold>mv: mover o renombrar ficheros y directorios</bold></title>
<preformat>
mv &#x003C;origen&#x003E; &#x003C;destino&#x003E;
</preformat>
<p><target target-type="page" id="pges_104"/>Mover dentro del mismo directorio equivale a renombrar</p>
<p><code>&#x003C;origen&#x003E;</code> es un fichero o un directorio</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Si el segundo argumento es un directorio</p>
<p>Mueve <code>&#x003C;origen&#x003E;</code> dentro del directorio <code>&#x003C;destino&#x003E;</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>Si el segundo argumento no existe</p>
<p>Mueve <code>&#x003C;origen&#x003E;</code> a <code>&#x003C;destino&#x003E;</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>Si <code>&#x003C;destino&#x003E;</code> es un fichero</p>
<list list-type="bullet">
<list-item><p>y <code>&#x003C;origen&#x003E;</code> es un fichero, <code>&#x003C;origen&#x003E;</code> pasa a llamarse <code>&#x003C;destino&#x003E;</code> y el anterior <code>&#x003C;destino&#x003E;</code> despararece</p></list-item>
<list-item><p>y el primero es un directorio, se produce un error</p></list-item>
</list></list-item>
</list>
<p>Ejemplos:</p>
<preformat>
mv holamundo.py /tmp
mv ~/prueba.txt .
mv /home/jperez/prueba.txt prueba2.txt
</preformat>
<p>mv con m&#x00E1;s de dos argumentos</p>
<p>mv <code>&#x003C;origen1&#x003E; &#x003C;origen2&#x003E; ... &#x003C;destino&#x003E;</code></p>
<p><code>&#x003C;destino&#x003E;</code> debe ser un directorio existente</p>
<p><code>&#x003C;origen1&#x003E;</code>, <code>&#x003C;origen2&#x003E;</code>... pueden ser ficheros ordinarios o directorios Ejemplos:</p>
<preformat>
mv holamundo.py /home/jperez/prueba1.txt ../prueba2.txt /tmp
mv *.txt texto
</preformat>
</sec>
<sec id="c9-s6-9">
<title><bold>Tipos de fichero</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Tradicionalmente en Unix los ficheros no llevaban extensi&#x00F3;n No hay un programa asociado a cada extension
<preformat>
file mifichero
</preformat></p>
<p>Indica el tipo del fichero. No importa si tiene extensi&#x00F3;n, si no la tiene, o si es err&#x00F3;nea</p></list-item>
</list>
<p>Supongamos que tenemos un fichero y no sabemos con qu&#x00E9; programa podemos abrirlo. P.e. desconocemos que tenemos instalado evince para abrir ficheros pdf</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_105"/>En Linux</p>
<list list-type="bullet">
<list-item><p>Si nuestro escritorio es gnome, podemos ejecutar
<preformat>
gnome-open fichero.extension
</preformat></p></list-item>
<list-item><p>Si usamos KDE, <code>kde-open fichero.extension</code></p></list-item>
<list-item><p>Para gnome, KDE y muchos otros <code>xdg-open fichero.extension</code></p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>En Mac OS
<preformat>
open fichero.extension
</preformat></p></list-item>
</list>
</sec>
<sec id="c9-s6-10">
<title><bold>Borrado de un fichero</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>rm</code> <italic>fichero</italic></p>
<p>borra fichero <xref ref-type="fn" rid="FN5"><sup>5</sup></xref></p>
<p><code>rm -r</code> <italic>directorio</italic></p></list-item>
<list-item><p>Borra un directorio y todo su contenido</p></list-item>
</list>
<p>Un usuario de MS-DOS podr&#x00ED;a intentar hacer</p>
<preformat>
mv *.txt *.doc # &#x00A1;MAL! No funciona, y puede ser fatal
</preformat>
<p>Supongamos que tenemos en el directorio actual</p>
<preformat>
carta1.txt
carta2.doc
</preformat>
<p>Tras expandir los asteriscos, el resultado es</p>
<preformat>
mv carta1.txt carta2.doc # destruimos el segundo fichero!
</preformat>
<p>Una soluci&#x00F3;n posible <xref ref-type="fn" rid="FN6"><sup>6</sup></xref>:</p>
<preformat>
#!/bin/bash
for fichero in *.txt
do
&#x00A0;&#x00A0;&#x00A0;nombre=$(echo $fichero | cut -d. -f1)
&#x00A0;&#x00A0;&#x00A0;extension=$(echo $fichero | cut -d. -f2)
&#x00A0;&#x00A0;&#x00A0;mv $fichero $nombre.doc
done
</preformat>
<fig id="fig3">
<label><bold>Figura 3:</bold></label>
<caption><title><target target-type="page" id="pges_106"/>Enlace Duro</title></caption>
<graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-14.jpg"/>
</fig>
</sec>
</sec>
<sec id="c9-s7">
<label><bold>9.7.</bold></label>
<title><bold>Enlaces</bold></title>
<sec id="c9-s7-1">
<title><bold>Enlace duro</bold></title>
<p>Un nuevo nombre para el fichero</p>
<preformat>
ln a b
</preformat>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Ambos nombres deben pertenecer al mismo sistema de ficheros</p></list-item>
<list-item><label>&#x25FE;</label> <p>Dado un fichero, se sabe cu&#x00E1;ntos nombres tiene. Para saber cu&#x00E1;les son sus nombres, habr&#x00ED;a que buscarlos</p></list-item>
<list-item><label>&#x25FE;</label> <p>La mayor&#x00ED;a de los S.O. no permiten enlaces duros a directorios, puesto que podr&#x00ED;a provocar bucles dif&#x00ED;ciles de detectar</p></list-item>
</list>
<p><code>rm</code> borra un nombre de un fichero</p>
<p>si es el &#x00FA;ltimo, borra el fichero.</p>
</sec>
<sec id="c9-s7-2">
<title><bold>Enlace blando o simb&#x00F3;lico</bold></title>
<p>Un nuevo fichero que apunta a un nombre</p>
<preformat>
ln -s /home/juan/b c
</preformat>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Sirven principalmente para mantener ficheros ordenados y <italic>a mano</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>Puede hacerse entre distintos sistemas de ficheros</p></list-item>
<list-item><label>&#x25FE;</label> <p>Puede enlazarse un directorio</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_107"/>Con enlaces simb&#x00F3;licos, si se borra el original el enlace queda roto</p></list-item>
</list>
<fig id="fig4">
<label><bold>Figura 4:</bold></label>
<caption><title>Enlace Simb&#x00F3;lico</title></caption>
<graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-15.jpg"/>
</fig>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>El fichero original podemos especificarlo</p>
<list list-type="bullet">
<list-item><p>Con su path absoluto</p></list-item>
<list-item><p>Con su path relativo</p>
<p>En este caso, si movemos el enlace simb&#x00F3;lico pero no movemos el original, se pierde la referencia</p></list-item>
</list></list-item>
</list>
</sec>
<sec id="c9-s7-3">
<title><bold>Utilidad de los enlaces</bold></title>
<p>Tanto los <italic>blandos</italic> como los <italic>duros</italic></p>
<p>son &#x00FA;tiles:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Para tener acceso a un fichero en un trayecto m&#x00E1;s c&#x00F3;modo, m&#x00E1;s a mano</p></list-item>
<list-item><label>&#x25FE;</label> <p>Si cambio de criterio sobre el lugar o el nombre de un fichero. Mediante un enlace, el fichero sigue accesible tanto por el nombre antiguo como por el nuevo</p></list-item>
</list>
<p>Ventaja de los enlaces duros:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Protegen frente a borrados accidentales de un nombre. Pero no frente a ning&#x00FA;n otro problema que pueda tener el fichero, por tanto su utilidad es m&#x00ED;nima</p></list-item>
</list>
<p>Ventaja de los enlaces simb&#x00F3;licos:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Se pueden establecer entre sistema de ficheros distintos</p></list-item>
<list-item><label>&#x25FE;</label> <p>Se pueden usar para directorios</p></list-item>
</list>
<p><target target-type="page" id="pges_108"/>Los enlaces simb&#x00F3;licos se usan mucho m&#x00E1;s que los enlaces duros</p>
</sec>
<sec id="c9-s7-4">
<title><bold>Directorio de Trabajo</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>La shell en todo momento se encuentra en un cierto punto del &#x00E1;rbol de ficheros. A ese punto se le llama <bold>directorio de trabajo</bold> (<italic>working directory</italic>)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Normalmente la shell indica el directorio de trabajo en el <italic>prompt</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>pwd: Muestra el directorio de trabajo actual (<italic>print working directory</italic>)
<preformat>
pwd
</preformat></p></list-item>
</list>
</sec></sec>
<sec id="c9-s7-5">
<title><bold>Trayectos (Paths)</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Un trayecto (path) consiste en escribir el camino hasta un fichero o directorio, incluyendo directorios intermedios separados por el car&#x00E1;cter <code>/</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>Trayecto absoluto:</p>
<list list-type="bullet">
<list-item><p>Escribe el camino desde el <bold>directorio r&#x00E1;&#x0131;z</bold></p></list-item>
<list-item><p><bold>Siempre</bold> empieza por /</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Trayecto relativo:</p>
<list list-type="bullet">
<list-item><p>Escribe el camino desde el directorio de trabajo</p></list-item>
<list-item><p><bold>Nunca</bold> empieza por <code>/</code></p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Cualquier programa acepta (o deber&#x00ED;a aceptar) que cuando se especifica un nombre de fichero, se use o bien la forma relativa o bien la forma absoluta.</p>
<p>Esto es aplicable a casi cualquier programa de casi cualquier Sistema Operativo</p></list-item>
</list>
<p>&#x00BF;Un trayecto con virgulilla es relativo o absoluto?</p>
<preformat>
~/mi_directorio
</preformat>
<p>En cierta forma es relativo</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_109"/>No empieza por /</p></list-item>
<list-item><label>&#x25FE;</label> <p>Depende del usuario que lo ejecuta</p></list-item>
</list>
<p>En cierta forma es absoluto</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>No depende del directorio de trabajo</p></list-item>
<list-item><label>&#x25FE;</label> <p>Lo que sucede realmente es que se reemplaza la virgulilla por el trayecto absoluto del <italic>home</italic> del usuario</p></list-item>
</list>
<p>Posiblemente lo m&#x00E1;s adecuado es considerarlo un caso un poco especial de path absoluto</p>
<fig><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-16.jpg"/></fig>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Ejemplos:</p>
<list list-type="bullet">
<list-item><p>Trayecto absoluto de f3:
<preformat>
/home/alcorcon/juan/d1/f3
</preformat></p></list-item>
<list-item><p>Trayecto relativo de <code>f3</code> si el directorio de trabajo es <code>juan</code>:
<preformat>
d1/f3
</preformat></p></list-item>
<list-item><p>Trayecto relativo de <code>f3</code> si el directorio de trabajo es <code>d2</code>:
<preformat>
../d1/f3
</preformat></p></list-item>
<list-item><p>Trayecto relativo de <code>var</code> si el directorio de trabajo es <code>luis</code>:
<preformat>
../../../var
</preformat></p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>cd: Cambia el directorio de trabajo (<italic>change directory</italic>)
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><target target-type="page" id="pges_110"/><code>cd d1</code></p></td>
<td valign="top" align="left"><p>Cambia desde el directorio de trabajo actual a su subdirectorio <code>d1</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>cd /home</code></p></td>
<td valign="top" align="left"><p>Cambia desde cualquier directorio al directorio <code>/home</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>cd ..</code></p></td>
<td valign="top" align="left"><p>Cambia desde el directorio de trabajo actual a su directorio padre (sube un directorio)</p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>cd</code></p></td>
<td valign="top" align="left"><p><code>Cambia al directorio por defecto (hogar) del usuario</code></p></td>
</tr>
</tbody></table>
</table-wrap></p></list-item>
<list-item><label>&#x25FE;</label> <p>ls: Muestra los contenidos de un directorio (<italic>list</italic>)
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>ls</code></p></td>
<td valign="top" align="left"><p>Muestra el contenido del directorio de trabajo</p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>ls d1</code></p></td>
<td valign="top" align="left"><p>Muestra el contenido del subdirectorio <code>d1</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>ls /home</code></p></td>
<td valign="top" align="left"><p>Muestra el contenido de <code>/home</code></p></td>
</tr>
</tbody>
</table>
</table-wrap></p></list-item>
</list>
</sec>
<sec id="c9-s8">
<label><bold>9.8.</bold></label>
<title><bold>Comandos de uso b&#x00E1;sico de la red</bold></title>
<sec id="c9-s8-1">
<title><bold>Mandatos de uso b&#x00E1;sico de la red</bold></title>
<p>ping: Comprueba si una m&#x00E1;quina responde en la red</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>ping gsyc.es</code></p></td>
<td valign="top" align="left"><p>Sondea la m&#x00E1;quina gsyc.es indefinidamente mostrando el doble de la latencia con ella. <code>CTRL-c</code> para terminar y mostrar un resumen</p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>ping -c 4 gsyc.es</code></p></td>
<td valign="top" align="left"><p>Sondea la m&#x00E1;quina <code>gsyc.es</code> 4 veces</p></td>
</tr>
</tbody></table>
</table-wrap>
<p>traceroute: Muestra encaminadores intermedios hasta un destino</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>traceroute gsyc.es</code></p></td>
<td valign="top" align="left"><p>Muestra encaminadores intermedios desde la m&#x00E1;quina en la que se est&#x00E1; hasta <code>gsyc.es</code>. Muestra el doble de las latencias hasta cada punto intermedio.</p></td>
</tr>
</tbody>
</table>
</table-wrap>
<preformat>
traceroute to gsyc (193.147.71.64), 30 hops max, 60 byte packets
1 ap (192.168.1.1) 0.730 ms 1.376 ms 1.345 ms
2 10.213.0.1 (10.213.0.1) 9.927 ms 15.040 ms 15.029 ms
3 10.127.46.153 (10.127.46.153) 15.003 ms 15.632 ms 15.607 ms
4 mad-b1-link.telia.net (213.248.90.85) 28.549 ms 28.720 ms 28.691 ms
5 dante-ic-125710-mad-b1.c.telia.net (213.248.81.26) 28.822 ms 28.959 ms 35.580 ms
6 nac.xe0-1-0.eb-madrid0.red.rediris.es (130.206.250.22) 36.344 ms 35.077 ms 34.967 ms
7 cam-router.red.rediris.es (130.206.215.66) 34.940 ms 12.015 ms 12.689 ms
8 * * *
9 gsyc.escet.urjc.es (193.147.71.64) 14.675 ms 14.934 ms 15.500 ms
</preformat>
</sec>
<sec id="c9-s8-2">
<title><bold>ssh</bold></title>
<p><target target-type="page" id="pges_111"/>Ejecuta mandatos de shell en una m&#x00E1;quina remota</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>ssh <ext-link ext-link-type="uri" xlink:href="mailto:jperez@zeta12.pantuflo.es">jperez@zeta12.pantuflo.es</ext-link></code></p></td>
<td valign="top" align="left"><p>Se conecta a la m&#x00E1;quina <code>zeta12.pantuflo.es</code> (pide password) y permite ejecutar mandatos en ella.</p>
<p>Toda la sesi&#x00F3;n entre la m&#x00E1;quina</p>
<p>origen y destino viaja cifrada por la red</p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>ssh <ext-link ext-link-type="uri" xlink:href="mailto:jperez@zeta12.pantuflo.es">jperez@zeta12.pantuflo.es</ext-link> ls /</code></p></td>
<td valign="top" align="left"><p><code>Se conecta a la m&#x00E1;quina zeta12.pantuflo.es (pide login y password), ejecuta el mandato ls / y sale de ella.</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>La primera vez que abrimos una sesi&#x00F3;n en una m&#x00E1;quina, ssh nos indica la huella digital de la m&#x00E1;quina remota
<preformat>
The authenticity of host &#x0027;gamma23 (212.128.4.133)&#x0027; can&#x0027;t be established.
RSA key fingerprint is de:fa:e1:02:dc:12:8d:ab:a8:79:8e:8f:c9:7d:99:eb.
Are you sure you want to continue connecting (yes/no)?
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Si necesitamos la certeza absoluta de que esta m&#x00E1;quina es quien dice ser, deber&#x00ED;amos comprobar esta huella digital por un medio seguro, alternativo</p></list-item>
<list-item><label>&#x25FE;</label> <p>La sesi&#x00F3;n se cierra cerrando la shell remota (exit o ctrl d)</p></list-item>
</list>
</sec>
<sec id="c9-s8-3">
<title><bold>scp</bold></title>
<preformat>
scp [[loginname@]maquina:]&#x003C;origen&#x003E; [[loginname@]maquina:]&#x003C;destino&#x003E;
</preformat>
<p>Copia ficheros desde/hacia m&#x00E1;quinas remotas. El contenido de los ficheros viaja cifrado por la red.</p>
<p>Igual que cp, pero ahora hay que a&#x00F1;adir o bien a origen o bien a destino</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>&#x00BF;Cu&#x00E1;l es la m&#x00E1;quina remota?</p></list-item>
<list-item><label>&#x25FE;</label> <p>&#x00BF;Qu&#x00E9; nombre de usuario tenemos en la m&#x00E1;quina remota?
<preformat>
usuario@maquina:
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>En caso de que el nombre de usuario en la m&#x00E1;quina local sea el mismo que en la m&#x00E1;quina remota, puede omitirse usuario@</p></list-item>
<list-item><label>&#x25FE;</label> <p>Los dos puntos del final nunca pueden omitirse</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_112"/>No puede haber espacios despu&#x00E9;s de los dos puntos</p></list-item>
<list-item><label>&#x25FE;</label> <p>La m&#x00E1;quina se puede indicar por su nombre o por su direcci&#x00F3;n IP</p></list-item>
<list-item><label>&#x25FE;</label> <p>Naturalmente, origen y destino pueden indicarse con trayecto relativo o con trayecto absoluto</p>
<list list-type="bullet">
<list-item><p>En la m&#x00E1;quina remota, los trayectos relativos parten del <italic>home</italic> del usuario remoto</p></list-item>
</list></list-item>
</list>
<p>Ejemplos:</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>scp f1 jperez@alpha.aulas.gsyc.urjc.es:d1/f1</code></p></td>
<td valign="top" align="left"><p>Lleva una copia del fichero <code>f1</code> desde la m&#x00E1;quina local hasta la m&#x00E1;quina <code>alpha</code>, entrando como usuario <code>jperez</code>, con trayecto <code>~jperez/f1</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>scp f1 jperez@alpha.aulas.gsyc.urjc.es:</code></p></td>
<td valign="top" align="left"><p>Lleva una copia del fichero <code>f1</code> desde la m&#x00E1;quina local hasta la m&#x00E1;quina <code>alpha</code> , entrando como usuario <code>jperez</code>, con trayecto <code>~jperez/f1</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>scp jperez@alpha.aulas.gsyc.urjc.es:f1 .</code></p></td>
<td valign="top" align="left"><p>Trae desde la m&#x00E1;quina <code>alpha</code>, entrando con el usuario <code>jperez</code>, el fichero ~jperez/f1 hasta el directorio de trabajo de la m&#x00E1;quina local</p></td>
</tr>
</tbody>
</table>
</table-wrap>
<p>Recuerda:</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>~jperez</code></p></td>
<td valign="top" align="left"><p><italic>home</italic> de jperez</p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>~/dir1</code></p></td>
<td valign="top" align="left"><p>subdirectorio dir1 dentro de mi <italic>home</italic></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<p>Si scp resulta nuevo para t&#x00ED; y no quieres equivocarte, puedes seguir estos pasos:</p>
<list list-type="order">
<list-item><p>Ten dos sesiones abiertas, una la m&#x00E1;quina origen y otra en la m&#x00E1;quina destino</p></list-item>
<list-item><p>Mediante <code>cd</code>, vete al directorio origen en la m&#x00E1;quina origen y haz <code>pwd</code> para asegurarte de que est&#x00E1;s donde debes</p></list-item>
<list-item><p>Mediante <code>cd</code>, vete al directorio destino en la m&#x00E1;quina destino y haz <code>pwd</code> para asegurarte de que est&#x00E1;s donde debes</p></list-item>
<list-item><p>En la m&#x00E1;quina origen, haz ls del fichero, indicando el path de forma absoluta. El <code>pwd</code> anterior te ayudar&#x00E1;. Si te equivocas, te dar&#x00E1;s cuenta ahora
<preformat>
ls /path/absoluto/al/fichero.txt
</preformat></p></list-item>
<list-item><p>Ejecuta el <code>scp</code> en la m&#x00E1;quina destino. Especifica el origen con la ayuda <target target-type="page" id="pges_113"/>de un copia-y-pega del paso anterior. Especifica el destino con &#x2018;.&#x2019;
<preformat>
scp usuario@maquina:/path/absoluto/al/fichero.txt .
</preformat></p></list-item>
</list>
</sec></sec>
<sec id="c9-s9">
<label><bold>9.9.</bold></label>
<title><bold>Entrada y salida</bold></title>
<sec id="c9-s9-1">
<title><bold>Entrada y salida</bold></title>
<p>Todo proceso en Unix / Linux tiene:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Una entrada est&#x00E1;ndar (<italic>stdin</italic>), de donde puede leer texto</p></list-item>
<list-item><label>&#x25FE;</label> <p>Una salida estandar (<italic>stdout</italic>), donde puede escribir sus resultados, en modo texto</p></list-item>
<list-item><label>&#x25FE;</label> <p>Una salida de error (<italic>stderr</italic>), donde puede describir los errores que se produzcan, en modo texto</p></list-item>
</list>
<p>Inicialmente la entrada se toma desde la <italic>consola</italic> (el teclado) y las salidas tambi&#x00E9;n se escriben en la consola (la pantalla). Pero estas entradas y salidas se pueden cambiar con las <italic>redirecciones</italic></p>
</sec>
<sec id="c9-s9-2">
<title><bold>Paso de argumentos a &#x00F3;rdenes</bold></title>
<p>Muchas &#x00F3;rdenes se comportan as&#x00ED; (no todas)</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Sin argumentos: Entrada est&#x00E1;ndar
<preformat>
wc
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>1 argumento: Nombre de fichero
<preformat>
wc fichero
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>n nombres de fichero
<preformat>
wc fichero1 fichero2
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p><code>cat</code></p>
<p>lee lo que hay en stdin y lo escribe en stdout (Ctrl D: fin de fichero)</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>cat fichero1 fichero2</code></p>
<p>lee los ficheros que se pasan como argumento y los escribe (concatenados) en stdout</p>
<p>(Ctrl D: fin de fichero)</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_114"/><code>echo argumento</code></p>
<p>escribe en stdout el texto que se le pasa como argumento. A&#x00F1;ade retorno de carro</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>echo -n argumento</code></p>
<p>escribe en stdout el texto que se le pasa como argumento</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>less fichero escribe</code></p>
<p>un fichero en stdout, permitiendo paginaci&#x00F3;n</p></list-item>
</list>
</sec>
<sec id="c9-s9-3">
<title><bold>Redirecciones</bold></title>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>&#x003C;</code></p></td>
<td valign="top" align="left"><p><code>redirige stdin desde fichero</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>&#x003E;</code></p></td>
<td valign="top" align="left"><p><code>redirige stdout a fichero, reemplazando</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>&#x003E;&#x003E;</code></p></td>
<td valign="top" align="left"><p><code>redirige stdout a fichero, a&#x00F1;adiendo</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>|</code></p></td>
<td valign="top" align="left"><p><code>redirige stdout de un proceso a stdin del siguiente</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>cat</code></p></list-item>
<list-item><label>&#x25FE;</label> <p><code>cat file1 file2 &#x003E; file3</code>
<preformat>
cat file1 | less
</preformat></p>
<p><code>cat &#x003E; file1</code></p></list-item>
<list-item><label>&#x25FE;</label> <p><code>less fichero</code>
<preformat>
cat fichero | less
less &#x003C; fichero
</preformat></p>
<p>(El resultado es el mismo, pero es importante distinguirlo)</p></list-item>
</list>
<list list-type="order">
<list-item><p>representa stdout</p></list-item>
<list-item><p>representa stderr</p></list-item>
</list>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>mkdir /a/b/c 2&#x003E; mi_fichero_errores</code></p>
<p>Redirige stderr al fichero</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>cp fichero_a fichero_b 2&#x003E;/dev/null</code></p>
<p>Redirige stderr al fichero <italic>sumidero</italic> (Lo que se copia en <code>/dev/null</code> desaparece sin mostrarse)</p></list-item>
</list>
<p>Para escribir en 1 o en 2, es necesario anteponer <code>&#x0026;</code> (para que no se confunda con un fichero que se llame &#x201C;<code>1</code>&#x201D; o &#x201C;<code>2</code>&#x0022;)</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>echo &#x201C;ERROR: xxxx ha fallado&#x201D; &#x003E;&#x0026;2</code></p>
<p>Redirige el mensaje a stderr</p></list-item>
</list>
<p><target target-type="page" id="pges_115"/><code>&#x0026;</code> representa stdout y stderr</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>find /var &#x0026;&#x003E;mi_fichero</p></list-item>
</list>
</sec>
<sec id="c9-s9-4">
<title><bold>sudo y redirecciones</bold></title>
<p>La orden <italic>sudo</italic> por omisi&#x00F3;n no incluye las posibles redirecciones</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>sudo echo hola &#x003E; /tmp/aa</code></p>
<p>El proceso <italic>echo</italic> se lanza con la identidad del <code>root</code> (id 0), pero la redirecci&#x00F3;n la ejecuta el usuario ordinario</p></list-item>
<list-item><label>&#x25FE;</label> <p>Para poder usar redirecciones, ejecutamos una subshell con el par&#x00E1;metro -c
<preformat>
sudo bash -c &#x201C;echo hola&#x003E;aa&#x201D;
</preformat></p>
<p><code>sudo bash -c &#x201C;find /root | grep prueba &#x201C;</code></p></list-item>
</list>
</sec></sec>
<sec id="c9-s10">
<label><bold>9.10.</bold></label>
<title><bold>Programaci&#x00F3;n de Scripts</bold></title>
<sec id="c9-s10-1">
<title><bold>Programaci&#x00F3;n de Scripts</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>En esta asignatura generalmente programaremos los scripts en python, que es m&#x00E1;s potente y sencillo que bash</p></list-item>
<list-item><label>&#x25FE;</label> <p>Pero para tareas muy b&#x00E1;sicas (cp, mv, ln -s, etc) puede ser m&#x00E1;s conveniente un script de bash</p></list-item>
</list>
<preformat>
#!/bin/bash
a=&#x0022;hola mundo&#x0022;
echo $a
</preformat>
<p>Para invocarlo:</p>
<preformat>
koji@mazinger:~$ ./holamundo
hola mundo
</preformat>
<p>Es recomendable que un script empiece por <code>#!/bin/bash</code>, pero no es imprescindible</p>
<preformat>
a=&#x0022;hola mundo&#x0022;
echo $a
</preformat>
<p>En este caso podemos ejecutar una shell y pasarle como primer argumento el fichero</p>
<preformat>
<target target-type="page" id="pges_116"/>koji@mazinger:~$ bash holamundo
hola mundo
</preformat>
<p>o bien ejecutar una shell y redirigir el fichero a su entrada est&#x00E1;ndar</p>
<preformat>
koji@mazinger:~$ bash &#x003C;holamundo
hola mundo
</preformat>
<p>Esto tambi&#x00E9;n puede ser &#x00FA;til para ejecutar un script sin permiso de ejecuci&#x00F3;n (basta el de lectura)</p>
</sec></sec>
<sec id="c9-s11">
<label><bold>9.11.</bold></label>
<title><bold>Filtros</bold></title>
<sec id="c9-s11-1">
<title><bold>Filtros</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Los filtros son muy importantes en el scripting Unix: grep, sed, sort, uniq, head, tail, paste...</p></list-item>
<list-item><label>&#x25FE;</label> <p>Un mandato genera una salida, un filtro procesa la salida (selecciona filas o columnas, pega, reemplaza, cuenta, ordena...) y lo pasa al siguiente mandato</p></list-item>
<list-item><label>&#x25FE;</label> <p>Ejemplo
<preformat>
who | cut -c1-8 |sort |uniq | wc -l
ps -ef | grep miguel | grep -v gvim
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>En esta asignatura programaremos en python (de nivel m&#x00E1;s alto y m&#x00E1;s intuitivo), as&#x00ED; que solo usaremos filtros muy b&#x00E1;sicos</p></list-item>
</list>
</sec>
<sec id="c9-s11-2">
<title><bold>grep</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>grep es un filtro que selecciona las filas que contengan (o que no contengan) cierto patr&#x00F3;n</p></list-item>
<list-item><label>&#x25FE;</label> <p>Para definir patrones de texto, emplea expresiones regulares (regexp)</p>
<list list-type="bullet">
<list-item><p>Las regexp de grep, sed y awk son <italic>cl&#x00E1;sicas</italic>.</p></list-item>
<list-item><p>Las regexp de perl, python y ruby son una evoluci&#x00F3;n de las regexp cl&#x00E1;sicas. Son mucho m&#x00E1;s intuitivas</p></list-item>
<list-item><p><target target-type="page" id="pges_117"/>Para tareas muy sencillas, podemos usar grep o sed. Si nuestras necesidades son m&#x00E1;s complejas y podemos elegir qu&#x00E9; herramienta usar, mejor python (o ruby)</p></list-item>
</list></list-item>
</list>
<p>grep con un argumento</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>grep &#x003C;patr&#x00F3;n&#x003E;</code></p>
<p>Lee stdin y escribe en stdout las l&#x00ED;neas que encajen en el patr&#x00F3;n</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>grep -v &#x003C;patr&#x00F3;n&#x003E;</code></p>
<p>Lee stdin y escribe en stdout las l&#x00ED;neas que <bold>no</bold> encajen en el patr&#x00F3;n</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>grep -i &#x003C;patr&#x00F3;n&#x003E;</code></p>
<p>Lee stdin y escribe en stdout las l&#x00ED;neas que encajen en el patr&#x00F3;n, ignorando may&#x00FA;sculas/min&#x00FA;sculas</p></list-item>
</list>
<p>Ejemplos</p>
<preformat>
ps -ef | grep -i ejemplo
ps -ef | grep -v jperez
dmesg | grep eth
</preformat>
<p>grep con dos o m&#x00E1;s argumentos</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>grep &#x003C;patr&#x00F3;n&#x003E; &#x003C;fichero_1&#x003E; ... &#x003C;fichero_n&#x003E;</code></p>
<p>Lee los ficheros indicados y escribe en stdout las l&#x00ED;neas que encajen en el patr&#x00F3;n</p></list-item>
</list>
<p>Ejemplos</p>
<preformat>
grep linux *.txt
grep -i hidalgo quijote.txt
grep -v 193.147 /etc/hosts
</preformat>
<p>Atenci&#x00F3;n: Si el patr&#x00F3;n a buscar incluye espacios, es necesario escribirlo entre comillas.</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>grep &#x201C;la mancha&#x201D; quijote.txt</code></p>
<p>Busca el patr&#x00F3;n <italic>la mancha</italic> en el fichero <italic>quijote.txt</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p><code>grep la mancha quijote.txt</code></p>
<p>Busca el patr&#x00F3;n <italic>la</italic> en el fichero <italic>mancha</italic> y en el fichero <italic>quijote.txt</italic></p></list-item>
</list>
<p><target target-type="page" id="pges_118"/>Atenci&#x00F3;n:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Hablamos de patrones, no de palabras. El patr&#x00F3;n ana encaja en la palabra ana pero tambi&#x00E9;n en rosana</p></list-item>
<list-item><label>&#x25FE;</label> <p>Los metacaracteres de las regexp no son iguales que los metacaracteres (comodines) del bash</p></list-item>
</list>
<p>Algunos metacaracteres:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>grep -i &#x0027;\&#x003C;ana\&#x003E;&#x0027;</code></p>
<p>Principio de palabra, patr&#x00F3;n <italic>ana</italic>, final de palabra. Insensible a may&#x00FA;sculas. (Dicho de otro modo, la palabra <italic>ana</italic>, sin confusi&#x00F3;n con <italic>Mariana</italic>)</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>grep -i &#x0027;\&#x003C;ana p.rez\&#x003E;&#x0027;</code></p>
<p>El punto representa cualquier car&#x00E1;cter (equivalente a la interrogaci&#x00F3;n en las shell de bash)</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>grep -i &#x0027;\&#x003C;ana p[e&#x00E9;]rez\&#x003E;&#x0027;</code></p>
<p>Despu&#x00E9;s de la <italic>p</italic> puede haber una <italic>e</italic> con tilde o sin tilde</p></list-item>
</list>
</sec>
<sec id="c9-s11-3">
<title><bold>xargs</bold></title>
<p>Mediante pipes podemos formar filtros concatenando &#x00F3;rdenes. Pero &#x00BF;qu&#x00E9; sucede cuando la informaci&#x00F3;n la necesitamos como par&#x00E1;metro, no en la entrada est&#x00E1;ndar?</p>
<preformat>
locate -i basura | rm # &#x00A1;Esto NO FUNCIONA!
</preformat>
<p>Podemos usar la orden xargs</p>
<preformat>
locate -i basura | xargs rm
</preformat>
<p>Ejecuta rm tantas veces como l&#x00ED;neas haya en stdin. Y le pasa cada l&#x00ED;nea como argumento</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Cuando necesitamos que la l&#x00ED;nea de entrada vaya en una posici&#x00F3;n distinta, usamos la opci&#x00F3;n -I replstr , donde replstr es la <italic>replace string</italic>, la cadena que reemplazaremos por el argumento</p></list-item>
<list-item><label>&#x25FE;</label> <p>El valor recomendado es {}, porque no es f&#x00E1;cil que aparezca en otro sitio
<preformat>
locate basura | xargs -I {} mv {} /tmp/papelera
find . | grep -i jpg | xargs -I {} mv {} /tmp/fotos
</preformat></p></list-item>
</list>
</sec></sec>
<sec id="c9-s12">
<label><bold>9.12.</bold></label>
<title><target target-type="page" id="pges_119"/><bold>Usos no est&#x00E1;ndar de la barra</bold></title>
<sec id="c9-s12-1">
<title><bold>Usos no est&#x00E1;ndar de la barra</bold></title>
<p>Un principio b&#x00E1;sico para hacer buenos programas es</p>
<p><italic>se laxo con lo que aceptas y estricto con lo que generas</italic></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>/d1//d2///d3/d4</p>
<p>En rigor es un nombre incorrecto. Aunque normalmente se admite, porque la shell y las librer&#x00ED;as lo <italic>limpian</italic> y generan</p>
<p><code>/d1/d2/d3/d4</code></p>
<p>No hay garant&#x00ED;a de que funcione siempre, es mucho mejor evitarlo</p></list-item>
<list-item><label>&#x25FE;</label> <p>/d1/</p>
<p>Algunas &#x00F3;rdenes y algunos documentos muestran una barra al final de un directorio para indicar que se trata de un directorio y no un fichero ordinario (de la misma manera que puede usarse un color distinto)</p>
<p>Algunas &#x00F3;rdenes pueden esperar que un nombre acabado en barra sea un directorio</p>
<p>Pero no es un nombre est&#x00E1;ndar, es preferible evitarlo</p></list-item>
<list-item><label>&#x25FE;</label> <p>Para la orden cp de Mac OS
<preformat>
cp -r d/ .
</preformat></p>
<p>significa</p>
<p><code>cp -r d/* .</code></p></list-item>
<list-item><p>(pero solo para cp -r y solo para Mac OS)</p></list-item>
</list>
</sec></sec>
<sec id="c9-s13">
<label><bold>9.13.</bold></label>
<title><bold>Ordenes internas</bold></title>
<sec id="c9-s13-1">
<title><bold>Ordenes internas</bold></title>
<p>La mayor&#x00ED;a de las &#x00F3;rdenes son externas</p>
<p>Pero todas las shell interpretan ciertas &#x00F3;rdenes por s&#x00ED; mismas: Las &#x00F3;rdenes internas (<italic>builtin commands</italic>)</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Por razones de eficiencia: <code>echo, kill, pwd, test...</code></p>
<p>Son internas aunque tambi&#x00E9;n tienen versi&#x00F3;n externa</p></list-item>
<list-item><label>&#x25FE;</label> <p>Necesariamente internas: <code>cd, export, alias, unset, exit...</code></p>
<p>Realizan funciones que tienen que hacerse forzosamente en el proceso <target target-type="page" id="pges_120"/>de la shell, har&#x00ED;an algo completamente diferente si se implementan como ejecutables externos
<preformat>
koji@mazinger:~$ type echo
echo es una funci&#x00F3;n integrada en la shell
</preformat></p></list-item>
</list>
</sec>
<sec id="c9-s13-2">
<title><bold>alias</bold></title>
<p>Reemplaza una cadena por otra</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>alias c=&#x2019;clear&#x2019;</code></p>
<p>Expande c, se convierte en <italic>clear</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p><code>alias</code></p>
<p>Muestra todos los alias</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>unalias c</code></p>
<p>Deshace el alias</p></list-item>
</list>
<p>alias suele definirse en <code>.bashrc</code></p>
<p>Hay ataques/bromas basados en alias</p>
</sec>
<sec id="c9-s13-3">
<title><bold>Funcionamiento de la shell</bold></title>
<list list-type="order">
<list-item><p>La shell lee texto de cierto fichero (stdin). Frecuentemente el texto lo est&#x00E1; escribiendo el usuario, as&#x00ED; que aporta algunas facilidades (borrar, autocompletar, history)</p></list-item>
<list-item><p>Analiza el texto (expande metacaracteres, variables, alias)</p></list-item>
<list-item><p>Busca la primera palabra, para ver si se trata de un ejecutable</p>
<list list-type="bullet">
<list-item><p>Primero la busca entre las &#x00F3;rdenes internas</p></list-item>
<list-item><p>Si no es interna, busca el ejecutable en ciertos directorios (los indicados en el PATH)</p></list-item>
</list></list-item>
<list-item><p>Aplica las redirecciones que correspondan</p></list-item>
<list-item><p>Ejecuta, pasando el resto de palabras como argumento</p></list-item>
<list-item><p><target target-type="page" id="pges_121"/>Duerme</p>
<list list-type="bullet">
<list-item><p>A menos que lancemos el ejecutable en <italic>background</italic>
<preformat>
acroread file.1 &#x0026;
</preformat></p></list-item>
</list></list-item>
</list>
</sec>
<sec id="c9-s13-4">
<title><bold>History</bold></title>
<p>Facilita la entrada de l&#x00ED;neas</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>(cursor arriba y abajo)</p>
<p>Muestra, una a una, las &#x00F3;rdenes introducidas</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>!&#x003C;cadena&#x003E;</code></p>
<p>Repite la &#x00FA;ltima orden que empiece por &#x003C;cadena&#x003E;</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>history</code></p>
<p>Muestra el historial de &#x00F3;rdenes introducidas</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>!&#x003C;n&#x003E;</code></p>
<p>Repite la &#x00F3;rden <code>&#x003C;n&#x003E;</code></p></list-item>
</list>
</sec></sec>
<sec id="c9-s14">
<label><bold>9.14.</bold></label>
<title><bold>Permisos especiales</bold></title>
<sec id="c9-s14-s1">
<label><bold>9.14.1.</bold></label>
<title><bold>SUID</bold></title>
<sec id="c9-s14-s1-1">
<title><bold>SUID</bold></title>
<p>Sea un fichero perteneciente a un usuario</p>
<preformat>
-rwxr-xr-x 1 koji koji 50 2009-03-24 12:06 holamundo
</preformat>
<p>Si lo ejecuta un usuario distinto</p>
<preformat>
invitado@mazinger:~$ ./holamundo
</preformat>
<p>El proceso pertenece al usuario que lo ejecuta, no al due&#x00F1;o del fichero</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" colspan="8"><p><code>koji@mazinger:~$ ps -ef |grep holamundo</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>invitado</code></p></td>
<td valign="top" align="left"><p>2307</p></td>
<td valign="top" align="left"><p>2260</p></td>
<td valign="top" align="left"><p>22</p></td>
<td valign="top" align="left"><p>12:16</p></td>
<td valign="top" align="left"><p>pts/0</p></td>
<td valign="top" align="left"><p>00:00:00</p></td>
<td valign="top" align="left"><p>holamundo</p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>koji</code></p></td>
<td valign="top" align="left"><p>2309</p></td>
<td valign="top" align="left"><p>2291</p></td>
<td valign="top" align="left"><p>0</p></td>
<td valign="top" align="left"><p>12:16</p></td>
<td valign="top" align="left"><p>pts/1</p></td>
<td valign="top" align="left"><p>00:00:00</p></td>
<td valign="top" align="left"><p>grep holamundo</p></td>
</tr></tbody>
</table></table-wrap>
<p>Este comportamiento es el normal y es lo deseable habitualmente</p>
<p><target target-type="page" id="pges_122"/>Pero en ocasiones deseamos que el proceso se ejecute con los permisos del due&#x00F1;o del ejecutable, no del usuario que lo invoca</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Esto se consigue activando el bit SUID (<italic>set user id</italic>)
<preformat>
chmod u+s fichero
chmod u-s fichero
</preformat></p>
<p>En un listado detallado aparece una <code>s</code> en lugar de la <code>x</code> del due&#x00F1;o (o una <code>S</code> si no hab&#x00ED;a <code>x</code>)</p></list-item>
<list-item><label>&#x25FE;</label> <p>El bit SUID permite que ciertos usuarios modifiquen un fichero, pero no de cualquier manera sino a trav&#x00E9;s de cierto ejecutable
<preformat>
-rwsr-xr-x 1 root root 29104 2008-12-08 10:14 /usr/bin/passwd
-rw-r--r-1 root root 1495 2009-03-23 19:56 /etc/passwd
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>El bit SUID tambi&#x00E9;n puede ser un problema de seguridad</p></list-item>
<list-item><label>&#x25FE;</label> <p>En el caso de los scripts, lo que se ejecuta no es el fichero con el script, sino el int&#x00E9;rprete</p>
<p>Un int&#x00E9;rprete con bit SUID es muy peligroso, normalmente la activaci&#x00F3;n del SUID en un script no tiene efecto</p></list-item>
<list-item><label>&#x25FE;</label> <p>Para buscar ficheros con SUID activo:
<preformat>
find / -perm +4000
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>El bit SGID es an&#x00E1;logo, cambia el GID
<preformat>
chmod g+s fichero
</preformat></p></list-item>
</list>
</sec></sec>
<sec id="c9-s14-s2">
<label><bold>9.14.2.</bold></label>
<title><bold>Sticky bit</bold></title>
<sec id="c9-s14-s2-1">
<title><bold>Sticky bit</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>En ficheros ya no se usa</p></list-item>
<list-item><label>&#x25FE;</label> <p>En un directorio, hace que sus ficheros solo puedan ser borrados o renombrados por el due&#x00F1;o del fichero, del directorio o el <italic>root</italic></p></list-item>
</list>
<p>Se representa con una t, en el listado y en chmod</p>
<preformat>
chmod [+-]t directorio
drwxrwxrwt 15 root root 4096 2007-02-21 13:36 /tmp/
</preformat>
<p>Si el directorio no tuviera permiso de ejecuci&#x00F3;n, aparecer&#x00ED;a <code>T</code></p>
<preformat>
drwxrwx-wT
</preformat>
</sec></sec>
<sec id="c9-s14-s3">
<label><bold>9.14.3.</bold></label>
<title><target target-type="page" id="pges_123"/><bold>Umask</bold></title>
<sec id="c9-s14-s3-1">
<title><bold>Umask</bold></title>
<p>Orden interna que muestra y cambia la variable umask (<italic>user file creation mode mask</italic>)</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>umask</code></p>
<p>Devuelve el valor umask</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>umask nuevo-valor</code></p>
<p>Cambia el valor umask</p></list-item>
</list>
<p>&#x00BF;Qu&#x00E9; permisos tiene por omisi&#x00F3;n un fichero reci&#x00E9;n creado?</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Ficheros: <code>666 and not umask</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>Directorios: <code>777 and not umask</code></p>
<p>Ejemplo. Creaci&#x00F3;n de un fichero</p>
<p>Calculamos el valor de umask negado
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>umask</code></p></td>
<td valign="top" align="left"><p>022</p></td>
<td valign="top" align="left"><p><code>000</code></p></td>
<td valign="top" align="left"><p>010</p></td>
<td valign="top" align="left"><p>010</p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>not umask</code></p></td>
<td valign="top" align="left"><p>755</p></td>
<td valign="top" align="left"><p><code>111</code></p></td>
<td valign="top" align="left"><p>101</p></td>
<td valign="top" align="left"><p>101</p></td>
</tr>
</tbody>
</table>
</table-wrap></p>
<p>Hacemos <italic>and l&#x00F3;gico</italic> entre 666 y el valor de umask negado</p></list-item>
</list>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p></p></td>
<td valign="top" align="left"><p>666</p></td>
<td valign="top" align="left"><p></p></td>
<td valign="top" align="left"><p>110</p></td>
<td valign="top" align="left"><p>110</p></td>
<td valign="top" align="left"><p>110</p></td>
</tr>
<tr>
<td valign="top" align="left"><p></p></td>
<td valign="top" align="left"><p></p></td>
<td valign="top" align="left"><p>and</p></td>
<td valign="top" align="left"><p></p></td>
<td valign="top" align="left"><p></p></td>
<td valign="top" align="left"><p></p></td>
</tr>
<tr>
<td valign="top" align="left"><p>not umask</p></td>
<td valign="top" align="left"><p>755</p></td>
<td valign="top" align="left"><p></p></td>
<td valign="top" align="left"><p>111</p></td>
<td valign="top" align="left"><p>101</p></td>
<td valign="top" align="left"><p>101</p></td>
</tr>
<tr>
<td valign="top" align="left"><p></p></td>
<td valign="top" align="left"><p>644</p></td>
<td valign="top" align="left"><p></p></td>
<td valign="top" align="left"><p>110</p>
<p><code>rw-</code></p></td>
<td valign="top" align="left"><p>100</p>
<p><code>r--</code></p></td>
<td valign="top" align="left"><p>100</p>
<p><code>r--</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
</sec></sec></sec>
<sec id="c9-s15">
<label><bold>9.15.</bold></label>
<title><bold>source</bold></title>
<sec id="c9-s15-1">
<title><bold>source</bold></title>
<p>Ejecuta un fichero en el entorno de la shell actual, que no muere. Las variables usadas en el fichero importado ser&#x00E1;n por tanto variables del proceso actual</p>
<p>El mandato <italic>punto</italic> (.) es equivalente, (aunque puede resultar menos legible)</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>&#x25FE; . ~/.bashrc</code></p></td>
<td valign="top" align="left"><p><code># Ejecuta el c&#x00F3;digo de .bashrc</code></p>
<p><code># en el entorno actual</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>&#x25FE; source ~/.bashrc</code></p></td>
<td valign="top" align="left"><p><code># Forma equivalente</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
</sec></sec>
<sec id="c9-s16">
<label><bold>9.16.</bold></label>
<title><target target-type="page" id="pges_124"/><bold>Invocaci&#x00F3;n de la shell</bold></title>
<sec id="c9-s16-1">
<title><bold>Invocaci&#x00F3;n de la shell</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Es frecuente desear que todas nuestras sesiones ejecuten o configuren algo, sin necesidad de teclearlo a mano cada vez. Para hacer esto necesitamos saber c&#x00F3;mo funciona la <italic>invocaci&#x00F3;n de la shell</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>Cada vez que se invoca una shell, esta ejecuta (con source) cierto fichero</p></list-item>
<list-item><label>&#x25FE;</label> <p>T&#x00ED;picamente esto se emplea para definir y exportar variables de entorno, modificar el prompt, declarar alias...</p></list-item>
<list-item><label>&#x25FE;</label> <p>Cada tipo de shell ejecuta un fichero diferente</p>
<list list-type="bullet">
<list-item><p>Una shell puede ser de login o no de login</p></list-item>
<list-item><p>Una shell puede ser interactiva o no interactiva</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Una shell de login es aquella en la que el usuario ha introducido login y contrase&#x00F1;a</p></list-item>
<list-item><label>&#x25FE;</label> <p>En general, una shell interactiva es aquella que tiene stdin redirigida desde la consola de un usuario, y stdout y stderr redirigidos a la consola de un usuario</p></list-item>
</list>
</sec>
<sec id="c9-s16-2">
<title><bold>Bash interactivo y de login</bold></title>
<p>Ejemplos:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Una sesi&#x00F3;n en una m&#x00E1;quina sin gr&#x00E1;ficos (p.e. un Unix antiguo, un router...)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Una sesi&#x00F3;n sin gr&#x00E1;ficos en una m&#x00E1;quina con gr&#x00E1;ficos, que se inicia pulsando Ctrl+Alt+F1</p></list-item>
<list-item><label>&#x25FE;</label> <p>Entrar por ssh en una m&#x00E1;quina</p></list-item>
</list>
<p>En este caso, la shell</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Lee y ejecuta <code>/etc/profile</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>Despu&#x00E9;s, ejecuta el primero que encuentre de
<preformat>
~/.bash_profile
~/.bash_login
~/.profile
</preformat></p></list-item>
</list>
<p><target target-type="page" id="pges_125"/>No se ejecuta <code>.bashrc</code>, a menos que <code>.bash_profile</code> lo llame.</p>
<p>Al terminar ejecuta</p>
<preformat>
~/.bash_logout
</preformat>
</sec>
<sec id="c9-s16-3">
<title><bold>Bash interactivo, no de login</bold></title>
<p>Ej: Un terminal en Gnome o en Fluxbox</p>
<p>Se ejecuta</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>~/.bashrc</code></p></list-item>
</list>
<p>No se ejecuta <code>~/.bash_profile</code></p>
</sec>
<sec id="c9-s16-4">
<title><bold>Bash no interactivo, no de login</bold></title>
<p>Ej: Un script</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Se ejecuta el fichero <code>$BASH_ENV</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>Antes del <code>.bashrc</code> de cada usuario, se ejecuta <code>/etc/bash.bashrc</code>, com&#x00FA;n para todos los usuarios</p></list-item>
<list-item><label>&#x25FE;</label> <p>Cuando se crea un usuario con <code>adduser</code>, se copia en su <italic>home</italic> todos los fichero que haya en <code>/etc/skel</code> (aqu&#x00ED; se guardan los ficheros de configuraci&#x00F3;n por omisi&#x00F3;n para cada usuario)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Hablamos siempre del inicio de la shell. No debemos confundir todo esto con los niveles de ejecuci&#x00F3;n, que se refienen al inicio de la m&#x00E1;quina (directorios <code>/etc/rc2.d, /etc/rcS.d</code>, etc)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Actualmente la diferencia entre shell de login y shell no de login es algo artificial <xref ref-type="fn" rid="FN7"><sup>7</sup></xref></p></list-item>
<list-item><label>&#x25FE;</label> <p>Hoy no suele resultar conveniente tener un fichero para las de login (<code>~/.bash_profile</code>) y otro distinto para las que son no de login (<code>~/.bashrc</code>)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Por tanto, lo normal es configurar todo lo necesario en <code>~/.bashrc</code> y tener en <code>~/.bash_profile</code> &#x00FA;nicamente una llamada a <code>~/.bashrc</code>, de la siguiente manera:</p></list-item>
</list>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>if [ -f ~/.bashrc ]; then</code></p></td>
<td valign="top" align="left"><p><code># si existe .bashrc</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>&#x00A0;&#x00A0;&#x00A0;. ~/.bashrc</code></p></td>
<td valign="top" align="left"><p><code># ejecuta .bashrc</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>fi</code></p></td>
<td valign="top" align="left"><p></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<p><target target-type="page" id="pges_126"/>O lo que es lo lo mismo</p>
<preformat>
if test -f ~/.bashrc ; then &#x00A0;&#x00A0;# si existe .bashrc
&#x00A0;&#x00A0;source ~/.bashrc &#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;# ejecuta .bashrc
fi
</preformat>
</sec></sec>
<sec id="c9-s17">
<label><bold>9.17.</bold></label>
<title><bold>Manejo b&#x00E1;sico de procesos</bold></title>
<sec id="c9-s17-1">
<title><bold>Manejo b&#x00E1;sico de procesos</bold></title>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" rowspan="3"><p><code>&#x25FE;</code></p></td>
<td valign="top" align="left"><p>ps</p></td>
<td valign="top" align="left"><p></p></td>
<td valign="top" align="left"><p>Informaci&#x00F3;n sobre los procesos</p></td>
</tr>
<tr>
<td valign="top" align="left"><p></p></td>
<td valign="top" align="left"><p>-e</p></td>
<td valign="top" align="left"><p>Informaci&#x00F3;n sobre todos los procesos de la maquina Formato largo</p></td>
</tr>
<tr>
<td valign="top" align="left"><p>ps</p></td>
<td valign="top" align="left"><p>-ef</p></td>
<td valign="top" align="left"><p>Formato largo</p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>&#x25FE;</code></p></td>
<td valign="top" align="left"><p>top</p></td>
<td valign="top" align="left"><p></p></td>
<td valign="top" align="left"><p>Muestra los procesos que consumen m&#x00E1;s cpu</p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>&#x25FE;</code></p></td>
<td valign="top" align="left"><p>kill</p></td>
<td valign="top" align="left"><p></p></td>
<td valign="top" align="left"><p>Envia una se&#x00F1;al a un proceso</p></td>
</tr>
</tbody>
</table>
</table-wrap>
</sec>
<sec id="c9-s17-2">
<title><bold>Se&#x00F1;ales</bold></title>
<p>La orden kill env&#x00ED;a se&#x00F1;ales a procesos</p>
<p><code>kill [se&#x00F1;al] [proceso]</code></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>15 SIGTERM (valor por defecto)</p></list-item>
<list-item><label>&#x25FE;</label> <p>9 SIGKILL</p></list-item>
<list-item><label>&#x25FE;</label> <p>2 SIGINT (Ctrl C) Lo envia tty a todos los programas que se est&#x00E9;n ejecutando en primer plano en el terminal, y a todos los programas lanzados por estos.</p></list-item>
<list-item><label>&#x25FE;</label> <p>19 SIGSTOP (Ctrl Z) Detiene</p></list-item>
<list-item><label>&#x25FE;</label> <p>18 SIGCONT Continua si estaba detenido</p></list-item>
</list>
<p>Las se&#x00F1;ales SIGKILL y SIGSTOP no se pueden ignorar ni bloquear Ejemplos:</p>
<preformat>
kill -9 2341
kill -sigstop 49322
</preformat>
<p>Tabla con las se&#x00F1;ales:</p>
<preformat>
<target target-type="page" id="pges_127"/>man 7 signal
</preformat>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Una manera t&#x00ED;pica de localizar un proceso <italic>a mano</italic> es <code>ps -ef | grep &#x003C;cadena&#x003E;</code></p>
<p>o
<preformat>
ps -ef | grep &#x003C;cadena&#x003E; | less
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p><code>killall</code> env&#x00ED;a se&#x00F1;ales a procesos a partir de su nombre. (El nombre de la se&#x00F1;al se indica de manera ligeramente distinta a como se emplea en kill)</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>pkill</code> env&#x00ED;a se&#x00F1;ales a procesos, identificables mediante nombre u otros atributos</p></list-item>
</list>
</sec></sec>
<sec id="c9-s18">
<label><bold>9.18.</bold></label>
<title><bold>Tareas</bold></title>
<sec id="c9-s18-1">
<title><bold>Control de tareas (jobs)</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Para lanzar varios procesos que se ejecuten en paralelo lo m&#x00E1;s c&#x00F3;modo suele ser abrir varias shells (una nueva terminal o una nueva pesta&#x00F1;a en el terminal o un multiplexor de terminal como tmux)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Pero tambi&#x00E9;n es posible desde una &#x00FA;nica shell manejar varios procesos simult&#x00E1;neamente: mediante el control de tareas (jobs)</p></list-item>
</list>
<p>Un proceso puede ejecutarse en primer o en segundo plano</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>En primer plano (<italic>foreground</italic>) recibe &#x00F3;rdenes desde el teclado, como</p>
<p>Ctrl Z (detener temporalmente) o Ctrl C (finalizar)</p></list-item>
<list-item><p>Cada shell solo puede tener un proceso en primer plano</p></list-item>
<list-item><label>&#x25FE;</label> <p>En segundo plano no tiene vinculada su entrada est&#x00E1;ndar desde el teclado, no recibe las se&#x00F1;ales Ctrl Z o Ctrl D. Es necesario emplear <italic>kill</italic></p>
<p>Puede haber varios procesos en segundo plano</p></list-item>
</list>
<p>De la misma manera, un proceso detenido puede estar tanto en primer como en segundo plano</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>La orden <italic>jobs</italic> indica, en cada l&#x00ED;nea, n&#x00FA;mero de tarea, estado y nombre</p></list-item>
</list>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" colspan="4"><p><code>koji@mazinger:~$ jobs</code></p></td>
<td valign="top" align="left"><p><code>[1]</code></p></td>
<td valign="top" align="left"><p><code>Ejecutando</code></p></td>
<td valign="top" align="left"><p></p></td>
<td valign="top" align="left"><p><code>xcalc &#x0026;</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>[2]-</code></p></td>
<td valign="top" align="left"><p><code>Ejecutando</code></p></td>
<td valign="top" align="left"><p></p></td>
<td valign="top" align="left"><p><code>evince &#x0026;</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>[3]+</code></p></td>
<td valign="top" align="left"><p><code>Detenido</code></p></td>
<td valign="top" align="left"><p></p></td>
<td valign="top" align="left"><p><code>gedit</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>El signo <code>+</code> indica tarea por omisi&#x00F3;n, aquella que se sobreentiende si no <target target-type="page" id="pges_128"/>se indica n&#x00FA;mero de tarea. Si la tarea por omisi&#x00F3;n muere, la siguiente ser&#x00E1; la marcada con el signo <code>-</code></p></list-item>
<list-item><label>&#x25FE;</label> <p><code>kill %n</code> env&#x00ED;a se&#x00F1;al al proceso con el job <code>n</code> (El s&#x00ED;mbolo de porcentaje indica n&#x00BA; de job, su ausencia indica pid)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Algunos programas multiproceso, complejos, aunque los lancemos desde una shell no son hijos de esa shell y no figurar&#x00E1;n en la lista de tareas. Por ejemplo firefox o nautilus</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>fg n</code></p>
<p>pone la tarea n en ejecuci&#x00F3;n en primer plano</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>bg n</code></p>
<p>pone la tarea <code>n</code> en ejecuci&#x00F3;n en segundo plano</p>
<p>El resultado es el mismo que si hubi&#x00E9;ramos lanzado la orden con el s&#x00ED;mbolo <code>&#x0026;</code></p></list-item>
</list>
<p>Las &#x00F3;rdenes <code>bg</code> y <code>fg</code> pueden lanzarse sin indicar n, entonces se sobreentiende la tarea por omisi&#x00F3;n.</p>
<p>La orden <code>kill</code> necesita que se le indique siempre expl&#x00ED;citamente el n&#x00FA;mero de tarea o el numero de pid</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>vmstat 1</code></p></td>
<td valign="top" align="left"><p><code>Lanzo vmstat, indicando que se actualice cada 1 segundo.</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>Ctrl Z</code></p></td>
<td valign="top" align="left"><p><code>Detengo el proceso. La shell me indica su n&#x00FA;mero de trabajo.</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>fg 1</code></p></td>
<td valign="top" align="left"><p><code>El trabajo 1 vuelve a primer plano. No puedo usar la shell.</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>Ctrl Z</code></p></td>
<td valign="top" align="left"><p><code>Vuelvo a detenerlo.</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>jobs</code></p></td>
<td valign="top" align="left"><p><code>Listado de todos los trabajos.</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>bg 1</code></p></td>
<td valign="top" align="left"><p>El trabajo 1 se ejecuta en segundo plano.</p>
<p>Sigue escribiendo en stdout, pero puedo usar la shell.</p>
<p>En este momento no puedo matarlo con ctrl C.</p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>fg</code></p></td>
<td valign="top" align="left"><p><code>El trabajo pasa a primer plano, puedo matarlo.</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
</sec></sec>
<sec id="c9-s19">
<label><bold>9.19.</bold></label>
<title><bold>Tmux</bold></title>
<sec id="c9-s19-1">
<title><bold>nohup</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Normalmente, cuando un usuario cierra una sesi&#x00F3;n, todos sus procesos reciben la se&#x00F1;al SIGHUP y mueren</p></list-item>
<list-item><label>&#x25FE;</label> <p>Si tenemos procesos que queremos que se continu&#x00E9;n ejecutando aunque el usuario cierre la sesi&#x00F3;n, podemos usar <code>nohup</code></p>
<list list-type="bullet">
<list-item><p><target target-type="page" id="pges_129"/><code>nohup &#x003C;orden&#x003E;</code></p>
<p><code>&#x003C;orden&#x003E;</code> ignorar&#x00E1; la se&#x00F1;al SIGHUP. Escribir&#x00E1; stdout en ./nohup.out (o en <code>~/nohup.out</code>)</p></list-item>
<list-item><p>Si necesitamos stdin, es necesario redirigirla desde un fichero</p></list-item>
</list></list-item>
</list>
</sec>
<sec id="c9-s19-2">
<title><bold>tmux</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Los <italic>multiplexores de terminales</italic> son una alternativa a <code>nohup</code> mucho m&#x00E1;s potente: Adem&#x00E1;s de mantener el proceso vivo cuando el usuario se desconecta, posteriormente se puede seguir usando interactivamente stdin y stdout</p></list-item>
<list-item><label>&#x25FE;</label> <p>Otra ventaja:</p>
<list list-type="bullet">
<list-item><p>Normalmente, si deseo tener n sesiones en una m&#x00E1;quina remota, es necesario abrir n conexiones mediante ssh</p></list-item>
<list-item><p>Usando un multiplexor, puedo abrir una &#x00FA;nica conexi&#x00F3;n ssh a una sesi&#x00F3;n del multiplexor, y en ella usar n ventanas</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>El m&#x00E1;s tradicional es GNU Screen (a&#x00F1;o 1987). Aqu&#x00ED; veremos Tmux (a&#x00F1;o 2007), un programa similar con algunas mejoras</p></list-item>
<list-item><label>&#x25FE;</label> <p>Inconvenientes:</p>
<list list-type="bullet">
<list-item><p>Es necesario memorizar algunos atajos de teclado</p></list-item>
</list></list-item>
</list>
<p>tmux maneja <italic>sesiones</italic></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Una sesi&#x00F3;n de tmux permite que un usuario se asocie (attach) y se desasocie de ella (dettach). El usuario puede desconectarse y la sesi&#x00F3;n permanece (todos los procesos se siguen ejecutando). Cuando el usuario vuelva a conectarse (t&#x00ED;picamente por ssh) puede reasociarse (reattach)</p></list-item>
</list>
<p>En cada sesi&#x00F3;n puede haber diferentes <italic>ventanas</italic></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>No son ventanas al estilo Microsoft Windows / X Window ni incluso ncurses, porque cada ventana de tmux ocupa toda la consola disponible</p></list-item>
<list-item><label>&#x25FE;</label> <p>Se parece a tener varias pesta&#x00F1;as en un gnome-terminal, o a diferentes sesiones en alt F1, alt F2</p></list-item>
<list-item><label>&#x25FE;</label> <p>A su vez, cada una de las ventanas se puede dividir en <italic>paneles</italic>, que s&#x00ED; se parecen a las ventanas de Microsoft Windows / X Window</p></list-item>
</list>
<p><target target-type="page" id="pges_130"/>El uso de tmux se basa en comandos, que tendremos que memorizar</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Cada comando est&#x00E1; formado por la pulsaci&#x00F3;n de la tecla <italic>bind</italic>, y a continuaci&#x00F3;n, una letra</p></list-item>
<list-item><label>&#x25FE;</label> <p>La tecla <italic>bind</italic> por omisi&#x00F3;n es <code>Ctrl b</code></p></list-item>
</list>
<p>Tal vez prefieras que la tecla <italic>bind</italic> sea otra. Por ejemplo <code>Ctrl a</code>, puede tener dos ventajas</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Posiblemente es m&#x00E1;s ergon&#x00F3;mico</p></list-item>
<list-item><label>&#x25FE;</label> <p>Es la tecla que usa <italic>screen</italic></p></list-item>
</list>
<p>En ese caso, a&#x00F1;adir&#x00ED;amos lo siguiente en <code>~/.tmux.conf</code></p>
<preformat>
set -g prefix C-a
bind C-a send-prefix
unbind C-b
</preformat>
</sec>
<sec id="c9-s19-3">
<title><bold>Uso t&#x00ED;pico de tmux</bold></title>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>tmux</code></p></td>
<td valign="top" align="left"><p><code>Creamos una sesi&#x00F3;n de tmux y nos asociamos a ella.</code></p>
<p><code>Si ya hab&#x00ED;a sesi&#x00F3;n, tambi&#x00E9;n nos asociamos pero creando una ventana nueva.</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>&#x003C;bind&#x003E; w</code></p></td>
<td valign="top" align="left"><p><code>Vemos las ventanas de la sesi&#x00F3;n. Nos desplazamos entre ellas con los cursores (flechas del teclado) e intro.</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>&#x003C;bind&#x003E; -d</code></p></td>
<td valign="top" align="left"><p><code>Nos desasociamos de la sesi&#x00F3;n actual.</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<p>Desconectamos ssh o cerramos el terminal. Volvemos a conectarnos</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>tmux attach</code></p></td>
<td valign="top" align="left"><p><code>Nos reasociamos a la sesi&#x00F3;n, sin crear m&#x00E1;s ventanas.</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>&#x003C;bind&#x003E; w</code></p></td>
<td valign="top" align="left"><p><code>Vemos las ventanas y nos desplazamos a la deseada.</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<p>Para a&#x00F1;adir ventanas la sesi&#x00F3;n actual pulsamos <code>&#x003C;bind&#x003E;</code> c</p>
<p>Para cerrar definitivamente una ventana, cerramos la shell de la forma habitual (<code>exit</code> o <code>Ctrl d</code>)</p>
<p>Con lo visto hasta ahora, tmux ya resulta de gran utilidad. Pero seguramente querremos dividir cada ventana en <italic>paneles</italic></p>
<p><target target-type="page" id="pges_131"/>Una vez dentro de una ventana de una sesi&#x00F3;n, haremos lo siguiente</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>&#x003C;bind&#x003E;</code></p></td>
<td valign="top" align="left"><p><code>% Divide la ventana en dos paneles</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>&#x003C;bind&#x003E; %</code></p></td>
<td valign="top" align="left"><p><code>Vuelve a realizar otra division (repetir esto las veces deseadas)</code></p>
<p><code>...</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>&#x003C;bind&#x003E; [espacio]</code></p></td>
<td valign="top" align="left"><p><code>Cambia la disposici&#x00F3;n de los paneles</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>&#x003C;bind&#x003E; [espacio]</code></p></td>
<td valign="top" align="left"><p><code>Cambia la disposici&#x00F3;n de los paneles</code></p>
<p><code>(repetir esto las veces deseadas)</code></p>
<p><code>...</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<p>Una vez que hemos ajustado a nuestro gusto el n&#x00FA;mero de paneles y su disposici&#x00F3;n, para mover el foco (esto es, marcar c&#x00FA;al es el panel activo), usamos los cursores del teclado</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>&#x003C;bind&#x003E;</code></p></td>
<td valign="top" align="left"><p><code>Derecha</code></p></td>
<td valign="top" align="left"><p><code>Foco al panel a la derecha del actual</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>&#x003C;bind&#x003E;</code></p></td>
<td valign="top" align="left"><p><code>Izquierda</code></p></td>
<td valign="top" align="left"><p><code>Foco al panel a la izquierda del actual</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>&#x003C;bind&#x003E;</code></p></td>
<td valign="top" align="left"><p><code>Arriba</code></p></td>
<td valign="top" align="left"><p><code>Foco al panel encima del actual</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>&#x003C;bind&#x003E;</code></p></td>
<td valign="top" align="left"><p><code>Abajo</code></p></td>
<td valign="top" align="left"><p><code>Foco al panel debajo del actual</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<p>Aqu&#x00ED; puedes ver una demostraci&#x00F3;n de todo esto: <ext-link ext-link-type="uri" xlink:href="https://youtu.be/Ks-a4YsYciM">https://youtu.be/Ks-a4YsYciM</ext-link></p>
<p>No es necesario conocer m&#x00E1;s atajos para manejar lo fundamental de sesiones, ventanas y paneles. Aunque tmux tiene muchos otros comandos. P.e.</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>&#x003C;bind&#x003E;</code></p></td>
<td valign="top" align="left"><p><code>n Moverse a la siguiente ventana</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>&#x003C;bind&#x003E; p</code></p></td>
<td valign="top" align="left"><p><code>Moverse a la ventana previa</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>&#x003C;bind&#x003E; ,</code></p></td>
<td valign="top" align="left"><p><code>Renombrar ventana actual</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>&#x003C;bind&#x003E; {</code></p></td>
<td valign="top" align="left"><p><code>Mover el panel actual a la izquierda</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>&#x003C;bind&#x003E; }</code></p></td>
<td valign="top" align="left"><p><code>Mover el panel actual a la derecha</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>&#x003C;bind&#x003E; [n&#x00FA;mero]</code></p></td>
<td valign="top" align="left"><p><code>Llevar el foco a la ventana [n&#x00FA;mero]</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<p>Si pulsamos <code>&#x003C;bind&#x003E;</code>, y sin soltarlo, pulsamos uno los cursores del teclado, redimensionamos el panel en la direcci&#x00F3;n del cursor<target target-type="page" id="pges_132"/></p>
</sec>
</sec>
</body>
<back>
<fn-group>
<fn id="FN3"><p><sup>3</sup> M&#x00E1;s detalles en el apartado <italic>invocaci&#x00F3;n de la shell</italic></p></fn>
<fn id="FN4"><p><sup>4</sup> en rigor, octal</p></fn>
<fn id="FN5"><p><sup>5</sup> Cuando hablemos de enlaces veremos una definici&#x00F3;n m&#x00E1;s exacta</p></fn>
<fn id="FN6"><p><sup>6</sup> Siempre que solo haya un punto en el nombre</p></fn>
<fn id="FN7"><p><sup>7</sup> En un linux con gr&#x00E1;ficos, una sesi&#x00F3;n ordinaria no ejecuta ninguna shell de login, mientras que en macOS todas las shell que ejecuta el usuario son de login</p></fn>
</fn-group>
</back>
</book-part>
<book-part id="c10" book-part-type="chapter">
<book-part-meta>
<book-part-id book-part-id-type="publisher-id">URJC</book-part-id>
<title-group>
<label>10.</label>
<title><target target-type="page" id="pges_133"/>COPIAS DE SEGURIDAD</title>
</title-group>
</book-part-meta>
<body>
<sec id="c9-s19-4">
<title><bold>Copias de Seguridad</bold></title>
<p>Los datos almacenados en un disco se pueden perder en cualquier momento, est&#x00E1;n expuestos a</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Fallos hardware</p></list-item>
<list-item><label>&#x25FE;</label> <p>Errores humanos</p></list-item>
<list-item><label>&#x25FE;</label> <p>Malware (virus)</p></list-item>
<list-item><label>&#x25FE;</label> <p>etc</p></list-item>
</list>
<p>En cualquier sistema, las copias de seguridad son de vital importancia</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Para uso personal, cuando tenemos pocos datos y buena conexi&#x00F3;n a internet, el almacenamiento en la nube con servicios <italic>freemium</italic> como Google Cloud o Dropbox es una buena soluci&#x00F3;n</p></list-item>
<list-item><label>&#x25FE;</label> <p>Para entornos m&#x00E1;s exigentes, es preferible usar nuestros propios dispositivos (pendrives o discos externos)</p></list-item>
</list>
<p>&#x00BF;Por qu&#x00E9; necesitamos una aplicaci&#x00F3;n de sincronizaci&#x00F3;n?</p>
<p>Supongamos que queremos respaldar nuestro directorio de trabajo en un disco externo reci&#x00E9;n comprado</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>El primer d&#x00ED;a, lo copiamos todo</p></list-item>
<list-item><label>&#x25FE;</label> <p>El segundo d&#x00ED;a queremos guardar tambi&#x00E9;n las copias recientes.</p>
<p>&#x00BF;Qu&#x00E9; hacemos?</p>
<list list-type="bullet">
<list-item><p>Podemos copiarlo todo de nuevo. Pero estamos desperdiciando recursos (tiempo, acceso a la red, tiempo de vida del disco)</p></list-item>
<list-item><p>Podemos seleccionar a mano las novedades y no copiar nada m&#x00E1;s. Pero es un trabajo tedioso y propenso a errores</p></list-item>
</list></list-item>
</list>
<p>La soluci&#x00F3;n es una herramienta de sincronizaci&#x00F3;n, que se ocupa de copiar s&#x00F3;lo los ficheros y directorios que se hayan a&#x00F1;adido o modificado, autom&#x00E1;ticamente</p>
</sec>
<sec id="c10-s1">
<label><bold>10.1.</bold></label>
<title><target target-type="page" id="pges_134"/><bold>rsync</bold></title>
<sec id="c10-s1-1">
<title><bold>rsync</bold></title>
<p>Permite sincronizaci&#x00F3;n unidireccional de dos directorios</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Herramienta habitual en Unix (incluyendo Linux y OS X). Hay versiones para Windows, pero su uso no es muy c&#x00F3;modo, requiere de componentes adicionales</p></list-item>
<list-item><label>&#x25FE;</label> <p>El esclavo contendr&#x00E1; una r&#x00E9;plica del maestro. El maestro ignora el contenido del esclavo</p></list-item>
<list-item><label>&#x25FE;</label> <p>Alternando las funciones de maestro y esclavo podr&#x00ED;amos conseguir una especia de sincronizaci&#x00F3;n unidireccional, pero no es recomendable, no est&#x00E1; dise&#x00F1;ado para ello</p></list-item>
<list-item><label>&#x25FE;</label> <p>Trabaja con sistemas de ficheros locales o remotos, pero al menos uno de los dos debe ser local</p></list-item>
<list-item><label>&#x25FE;</label> <p>Funciona sobre rcp, ssh o un demonio de rsync</p></list-item>
<list-item><label>&#x25FE;</label> <p>Es muy &#x00FA;til para hacer copias de segurad mediante <italic>mirrors</italic> de disco duro sobre disco duro: automatizarlo es muy sencillo y el disco duro es el medio de almancenamiento m&#x00E1;s barato de la actualidad</p></list-item>
<list-item><label>&#x25FE;</label> <p>Pero tambi&#x00E9;n resulta un sistema de respaldo muy vol&#x00E1;til, deber&#x00ED;amos tener, adicionalmente, otros sistemas de respaldo m&#x00E1;s permanentes
<preformat>
rsync &#x003C;opciones&#x003E; directorio_maestro directorio_esclavo
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>El directorio remoto se especifica anteponiendo usuario@maquina:</p></list-item>
</list>
<p>Ejemplo</p>
<preformat>
rsync -e ssh -va --delete /dir1/dir2 jperez@epsilon03:/dir3/dir4
</preformat>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Crea dentro de dir4 un directorio dir2, r&#x00E9;plica del dir2 del maestro Error frecuente: creer que dir4 ser&#x00E1; una r&#x00E9;plica de dir2</p></list-item>
<list-item><label>&#x25FE;</label> <p>Error frecuente: insertar espacio tras los dos puntos</p></list-item>
</list>
<p>Opciones</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>-e</code> especifica c&#x00F3;mo acceder a los ficheros en la m&#x00E1;quina remota</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_135"/><code>-v</code> verbose (prolijo en detalles)</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>-a</code> modo archive: incluye subdirectorios, copia enlaces como enlaces, conserva permisos, fechas y grupo. Si lo ejecuta el root, tambi&#x00E9;n conserva el due&#x00F1;o</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>--delete</code> indica que si se borra un fichero en el maestro, tambi&#x00E9;n se borrar&#x00E1; en el esclavo</p>
<p>Atenci&#x00F3;n: si montamos un nuevo maestro, vac&#x00ED;o, y aplicamos rsync con esta opci&#x00F3;n, destruiremos el esclavo</p></list-item>
</list>
</sec></sec>
<sec id="c10-s2">
<label><bold>10.2.</bold></label>
<title><bold>FreeFileSync</bold></title>
<sec id="c10-s2-1">
<title><bold>FreeFileSync</bold></title>
<p>FreeFileSync es una aplicaci&#x00F3;n de sincronizaci&#x00F3;n de ficheros</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Permite comparar dos directorios, detectar las diferencias y propagarlas, de forma que ambos directorios acaben teniendo el mismo contenido</p></list-item>
<list-item><label>&#x25FE;</label> <p>Los directorios pueden estar en la m&#x00E1;quina local o en una m&#x00E1;quina remota accesible a trav&#x00E9;s de FTP o SFTP. Tambi&#x00E9;n soporta Google Drive</p></list-item>
<list-item><label>&#x25FE;</label> <p>Libre y gratuita, disponible para Microsoft Windows, Linux y macOS</p></list-item>
<list-item><label>&#x25FE;</label> <p>Software maduro, aparece en 2008 y se actualiza con frecuencia</p></list-item>
</list>
<p>Con FreeFileSync haremos principalmente dos tipos de sincronizaci&#x00F3;n</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Sincronizaci&#x00F3;n bidireccional</p></list-item>
<list-item><label>&#x25FE;</label> <p>Espejo</p></list-item>
</list>
<p>En ambos casos</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Antes de sincronizar tendremos dos directorios que normalmente ser&#x00E1;n parecidos pero con algunas diferencias</p></list-item>
<list-item><label>&#x25FE;</label> <p>Despu&#x00E9;s de sincronizar, ambos directorios ser&#x00E1;n id&#x00E9;nticos</p></list-item>
</list>
<p>Ambos directorios podr&#x00E1;n estar en cualquier lugar</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>En el mismo disco de la misma m&#x00E1;quina</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_136"/>En otro disco de la misma m&#x00E1;quina</p></list-item>
<list-item><label>&#x25FE;</label> <p>En otro disco de otra m&#x00E1;quina</p></list-item>
<list-item><label>&#x25FE;</label> <p>Incluso en el mismo disco pero otra m&#x00E1;quina (como en nuestro laboratorio)</p></list-item>
</list>
</sec>
<sec id="c10-s2-2">
<title><bold>Sincronizaci&#x00F3;n bidireccional</bold></title>
<p>En la sincronizaci&#x00F3;n bidireccional:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Todas las novedades del primer directorio se propagar&#x00E1;n al segundo (ficheros nuevos, modificados o borrados, directorios nuevos, modificados o borrados)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Todas las novedades del segundo directorio se propagar&#x00E1;n al primero</p></list-item>
</list>
<p>Tras la sincronizaci&#x00F3;n, ambos directorios ser&#x00E1;n id&#x00E9;nticos Ejemplo t&#x00ED;pico:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>El primer directorio es mi directorio de trabajo en mi pc de casa</p></list-item>
<list-item><label>&#x25FE;</label> <p>El segundo directorio es mi directorio de trabajo en el laboratorio</p></list-item>
</list>
<fig id="fig5">
<label><bold>Figura 5:</bold></label>
<caption><title>Pantalla principal de FreeFileSync</title></caption>
<graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-17.jpg"/></fig>
<p><target target-type="page" id="pges_137"/>Ventana principal. El panel de la izquierda muestra el directorio local. El de la derecha, el remoto</p>
</sec>
<sec id="c10-s2-3">
<title><bold>Espejo</bold></title>
<p>En la sincronizaci&#x00F3;n <italic>espejo</italic>, tenemos dos directorios</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Uno es el principal</p></list-item>
<list-item><label>&#x25FE;</label> <p>Otro ser&#x00E1; una copia</p></list-item>
</list>
<p>Despu&#x00E9;s de la sincronizaci&#x00F3;n, ambos ser&#x00E1;n id&#x00E9;nticos Para ello:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Todas las novedades del principal se llevar&#x00E1;n a la copia</p></list-item>
<list-item><label>&#x25FE;</label> <p>En el directorio copia no deber&#x00ED;a haber novedades, y si las hay, ser&#x00E1;n ignoradas y destruidas</p></list-item>
</list>
<p>Ejemplo t&#x00ED;pico:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>El primer directorio es mi directorio de trabajo</p></list-item>
<list-item><label>&#x25FE;</label> <p>El segundo es una copia de seguridad en un disco externo</p></list-item>
</list>
</sec>
<sec id="c10-s2-4">
<title><bold>C&#x00F3;mo sincronizar</bold></title>
<p>Para sincronizar los directorios</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Indicamos el primer directorio en el panel de la izquierda</p></list-item>
<list-item><label>&#x25FE;</label> <p>Indicamos el segundo directorio en el panel de la derecha</p></list-item>
<list-item><label>&#x25FE;</label> <p>Pulsamos el icono de la rueda dentada verde para especificar el tipo de sincronizaci&#x00F3;n (bidireccional o espejo)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Pulsamos <italic>comparar</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>Pulsamos <italic>sincronizar</italic></p></list-item>
</list>
<p>Pulsando el icono del filtro, tenemos la opci&#x00F3;n de excluir algunos ficheros. Por ejemplo <code>*.o, *.exe</code></p>
<p>En la ventana de la rueda dentada verde indicamos el tipo de sincronizaci&#x00F3;n</p>
<p><target target-type="page" id="pges_138"/>Desde el icono de la nube accedemos a la ventana de <italic>almacenamiento en l&#x00ED;nea</italic>, donde podemos indicar que el directorio estar&#x00E1; en una m&#x00E1;quina remota. Por ejemplo en el laboratorio de la ETSIT, accesible mediante el protocolo SFTP</p>
<fig id="fig6">
<label><bold>Figura 6:</bold></label>
<caption><title>Opciones de sincronizaci&#x00F3;n</title></caption>
<graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-18.jpg"/></fig>
<fig><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-19.jpg"/></fig>
</sec>
<sec id="c10-s2-5">
<title><target target-type="page" id="pges_139"/><bold>Conflictos</bold></title>
<p>En la sincronizaci&#x00F3;n bidireccional, es importante sincronizar siempre que cambiemos de m&#x00E1;quina, de lo contrario se producir&#x00E1; un <italic>conflicto</italic></p>
<p>Ejemplo:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>El lunes trabajo en casa y sincronizo el directorio contra el laboratorio</p></list-item>
<list-item><label>&#x25FE;</label> <p>El martes trabajo en el laboratorio</p></list-item>
<list-item><label>&#x25FE;</label> <p>El mi&#x00E9;rcoles vuelvo a casa, pero olvido sincronizar. A&#x00F1;ado mis cambios sobre la versi&#x00F3;n del lunes, no sobre la del martes</p></list-item>
</list>
<p>En este caso, tendr&#x00E9; cambios que solo estar&#x00E1;n en casa, y otros que solo estar&#x00E1;n en el laboratorio</p>
<p>FreeFileSync nos avisar&#x00E1; mano del problema, que tendremos que corregir a mano</p>
<fig><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-20.jpg"/></fig>
<p>El conflicto se se&#x00F1;ala con un icono en forma de chispa naranja</p>
<p>El problema tendremos que corregirlo nosotros manualmente: hacemos lo necesario para que uno de los directorios est&#x00E9; <italic>correcto</italic> y luego indicar en qu&#x00E9; sentido debe hacerse la actualizaci&#x00F3;n</p>
</sec>
<sec id="c10-s2-6">
<title><target target-type="page" id="pges_140"/><bold>Guardar la configuraci&#x00F3;n</bold></title>
<p>Finalmente, guardaremos toda la configuraci&#x00F3;n en un fichero de extensi&#x00F3;n <code>.ffs_gui</code>, para poder repetir la sincronizaci&#x00F3;n en la siguiente ocasi&#x00F3;n, sin volver a especificarlo todo.</p>
<p>Aqu&#x00ED; puedes ver una sesi&#x00F3;n de ejemplo: <ext-link ext-link-type="uri" xlink:href="https://youtu.be/gJGp6liznE0">https://youtu.be/gJGp6liznE0</ext-link></p>
<fig><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-21.jpg"/></fig>
<fig><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-22.jpg"/></fig>
</sec></sec>
</body>
</book-part>
<book-part id="c11" book-part-type="chapter">
<book-part-meta>
<book-part-id book-part-id-type="publisher-id">URJC</book-part-id>
<title-group>
<label>11.</label>
<title><target target-type="page" id="pges_141"/>EL LENGUAJE PYTHON</title>
</title-group>
</book-part-meta>
<body>
<sec id="c11-s1">
<label><bold>11.1.</bold></label>
<title><bold>Introducci&#x00F3;n</bold></title>
<sec id="c11-s1-1">
<title><bold>El Lenguaje Python</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Lenguaje <italic>de autor</italic> creado por Guido van Rossum en 1989</p></list-item>
<list-item><label>&#x25FE;</label> <p>Muy relacionado originalmente con el S.O. <italic>Amoeba</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>Disponible en Unix, Linux, macOS, Windows,</p></list-item>
<list-item><label>&#x25FE;</label> <p>Libre</p></list-item>
<list-item><label>&#x25FE;</label> <p>Lenguaje de Script Orientado a Objetos (no muy puro)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Muy alto nivel</p></list-item>
<list-item><label>&#x25FE;</label> <p>Librer&#x00ED;a muy completa</p></list-item>
<list-item><label>&#x25FE;</label> <p>Verdadero lenguaje de prop&#x00F3;sito general</p></list-item>
<list-item><label>&#x25FE;</label> <p>Sencillo, compacto</p></list-item>
<list-item><label>&#x25FE;</label> <p>Sintaxis clara</p></list-item>
<list-item><label>&#x25FE;</label> <p>Interpretado <code>=&#x003E;</code> Lento</p></list-item>
<list-item><label>&#x25FE;</label> <p>Ofrece persistencia</p></list-item>
<list-item><label>&#x25FE;</label> <p>Recolector de basuras</p></list-item>
<list-item><label>&#x25FE;</label> <p>Muy maduro y muy popular</p></list-item>
<list-item><label>&#x25FE;</label> <p>Aplicable para software de uso general</p></list-item>
</list>
<p>Programa python</p>
<preformat>
for x in xrange(1000000):
&#x00A0;&#x00A0;&#x00A0;print x
</preformat>
<p><target target-type="page" id="pges_142"/>Su equivalente Java</p>
<preformat>
public class ConsoleTest {
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;public static void main(String[] args) {
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;for (int i = 0; i &#x003C; 1000000; i++) {
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;System.out.println(i);
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;}
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;}
}
</preformat>
<p>Programa python</p>
<preformat>
for i in xrange(1000):
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;x={}
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;for j in xrange(1000):
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;x[j]=i
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;x[j]
</preformat>
<p>Su equivalente Java</p>
<preformat>
import java.util.Hashtable;
public class HashTest {
&#x00A0;&#x00A0;&#x00A0;public static void main(String[] args) {
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;for (int i = 0; i &#x003C; 1000; i++) {
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;Hashtable x = new Hashtable();
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;for (int j = 0; j &#x003C; 1000; j++) {
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;x.put(new Integer(i), new Integer(j));
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;x.get(new Integer(i));
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;}
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;}
} }
</preformat>
</sec>
<sec id="c11-s1-2">
<title><bold>Librer&#x00ED;as</bold></title>
<p>Python dispone de librer&#x00ED;as <italic>Nativas</italic> y <italic>Normalizadas</italic> para</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Cadenas, listas, tablas hash, pilas, colas</p></list-item>
<list-item><label>&#x25FE;</label> <p>N&#x00FA;meros Complejos</p></list-item>
<list-item><label>&#x25FE;</label> <p>Serializaci&#x00F3;n, Copia profunda y Persistencia de Objetos</p></list-item>
<list-item><label>&#x25FE;</label> <p>Regexp</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_143"/>Unicode, Internacionalizaci&#x00F3;n del Software</p></list-item>
<list-item><label>&#x25FE;</label> <p>Programaci&#x00F3;n Concurrente</p></list-item>
<list-item><label>&#x25FE;</label> <p>Acceso a BD, Ficheros Comprimidos, Control de Cambios... Librer&#x00ED;as relacionadas con Internet:</p></list-item>
<list-item><label>&#x25FE;</label> <p>CGIs, URLs, HTTP, FTP,</p></list-item>
<list-item><label>&#x25FE;</label> <p>pop3, IMAP, telnet</p></list-item>
<list-item><label>&#x25FE;</label> <p>Cookies, Mime, XML, XDR</p></list-item>
<list-item><label>&#x25FE;</label> <p>Diversos formatos multimedia</p></list-item>
<list-item><label>&#x25FE;</label> <p>Criptograf&#x00ED;a</p></list-item>
</list>
<p>La referencia sobre todas las funciones de librer&#x00ED;a podemos encontrarlas en la documentaci&#x00F3;n oficial, disponible en el web en muchos formatos. Basta con localizar en cualquier buscador la <italic>python standard library</italic></p>
</sec>
<sec id="c11-s1-3">
<title><bold>Inconvenientes de Python</bold></title>
<p>Adem&#x00E1;s de su velocidad limitada y necesidad de int&#x00E9;rprete</p>
<p>(Como todo lenguaje interpretado)</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>No siempre compatible hacia atr&#x00E1;s</p></list-item>
<list-item><label>&#x25FE;</label> <p>Uniformidad.</p>
<p>Ej: <code>funci&#x00F3;n len(), m&#x00E9;todo items()</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>Algunos aspectos de la OO</p>
<p><italic>Python is a hybrid language. It has functions for procedural programming and objects for OO programming. Python bridges the two worlds by allowing functions and methods to interconvert using the explicit &#x201C;self&#x201D; parameter of every method def. When a function is inserted into an object, the first argument automagically becomes a reference to the receiver.</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p><code>...</code></p></list-item>
</list>
</sec>
<sec id="c11-s1-4">
<title><target target-type="page" id="pges_144"/><bold>Versiones de python</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Python 1 (a&#x00F1;o 1991)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Python 2 (a&#x00F1;o 2001)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Incompatible con Python 1</p></list-item>
<list-item><label>&#x25FE;</label> <p>Python 3 (a&#x00F1;o 2009)</p>
<p>Incompatible con Python 2. Versi&#x00F3;n problem&#x00E1;tica, durante la d&#x00E9;cada de 2010 fue muy habitual seguir usando Python 2</p></list-item>
</list>
<p>El soporte para Python 2 finaliz&#x00F3; oficialmente el 1 de enero de 2020. En la actualidad:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Sigue habiendo mucho c&#x00F3;digo antiguo en Python 2</p></list-item>
<list-item><label>&#x25FE;</label> <p>Sigue siendo recomendable que en cualquier sistema <italic>python a secas</italic> sea Python 2</p></list-item>
<list-item><label>&#x25FE;</label> <p>Deber&#x00ED;amos programar siempre en Python 3, indicando expl&#x00ED;citamente que el int&#x00E9;rprete es <italic>python3</italic></p></list-item>
</list>
</sec></sec>
<sec id="c11-s2">
<label><bold>11.2.</bold></label>
<title><bold>El int&#x00E9;rprete de python</bold></title>
<sec id="c11-s2-1">
<title><bold>El int&#x00E9;rprete de python se puede usar</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>En modo interactivo
<preformat>
koji@mazinger:<sup>~</sup>$ python3
Python 3.9.0 (default, Oct 27 2020, 14:13:35)
[Clang 11.0.0 (clang-1100.0.33.17)] on darwin
Type &#x0022;help&#x0022;, &#x0022;copyright&#x0022;, &#x0022;credits&#x0022; or &#x0022;license&#x0022; for more information.
&#x003E;&#x003E;&#x003E; print(&#x0022;Hola, mundo&#x0022;)
Hola, mundo
&#x003E;&#x003E;&#x003E; 3/2
1.5
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Mediante scripts</p></list-item>
</list>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># holamundo.py</italic>
print &#x201C;hola mundo&#x201D; <italic># esto es un comentario</italic>
euros=415
pesetas=euros*166.386
print(&#x0022;{} euros son {} pesetas&#x0022;.format(euros, pesetas))</preformat></p>
</td>
</tr>
</tbody>
</table>
</table-wrap>
<p><target target-type="page" id="pges_145"/>La l&#x00ED;nea <code>#!/usr/bin/env python3</code> indica al S.O. d&#x00F3;nde est&#x00E1; la utilidad <italic>env</italic>, que buscar&#x00E1; en el <italic>path</italic> el ejecutable <italic>python3</italic> y le pasar&#x00E9; el fuente para que lo ejecute</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Debe ser exactamente la primera l&#x00ED;nea</p></list-item>
<list-item><label>&#x25FE;</label> <p>No puede haber espacios entre la admiraci&#x00F3;n y la barra</p></list-item>
</list>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#Este ejemplo es doblemente incorrecto</italic>
<italic>#! /usr/bin/env python3</italic>
<italic># &#x00A1;MAL!</italic></preformat></p>
</td>
</tr></tbody>
</table></table-wrap>
<p>En linux se puede especificar directamente la direcci&#x00F3;n del int&#x00E9;rprete</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/python3</italic>
print(&#x201C;Hola mundo&#x201D;)
</preformat></p></td>
</tr></tbody>
</table></table-wrap>
<p>Pero env es m&#x00E1;s general, permite que tengamos varios int&#x00E9;rpretes en el sistema (algo muy normal en macOS )</p>
</sec></sec>
<sec id="c11-s3">
<label><bold>11.3.</bold></label>
<title><bold>Operadores</bold></title>
<sec id="c11-s3-1">
<title><bold>Operadores</bold></title>
<p>En orden de precedencia decreciente:</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
+x, <sup>-</sup>x, <sup>~</sup>x &#x00A0;&#x00A0;&#x00A0;Unary operators
x ** y Power
x * y, x / y, x % y &#x00A0;&#x00A0;&#x00A0;Multiplication, division, modulo
x + y, x - y &#x00A0;&#x00A0;&#x00A0;Addition, subtraction
x &#x003C;&#x003C; y, x &#x003E;&#x003E; y &#x00A0;&#x00A0;&#x00A0;Bit shifting
x &#x0026; y &#x00A0;&#x00A0;&#x00A0;Bitwise and
x | y Bitwise or
x &#x003C; y, x &#x003C;= y, x &#x003E; y, x &#x003E;= y, x == y, x != y,
x &#x003C;&#x003E; y,
x is y, x is not y, x in s, x not in s
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;Comparison, identity,
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;sequence membership tests
not x&#x00A0;&#x00A0;&#x00A0;Logical negation
x and y&#x00A0;&#x00A0;&#x00A0;Logical and
lambda args: expr&#x00A0;&#x00A0;&#x00A0;Anonymous function
</preformat></p></td>
</tr>
</tbody>
</table></table-wrap>
</sec></sec>
<sec id="c11-s4">
<label><bold>11.4.</bold></label>
<title><target target-type="page" id="pges_146"/><bold>Identificadores</bold></title>
<p>Identificadores (nombre de objetos, de funciones...):</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Letras inglesas de &#x2019;a&#x2019; a &#x2019;z&#x2019;, en may&#x00FA;sculas o min&#x00FA;sculas. Barra baja &#x2019;_&#x2019; y n&#x00FA;meros</p></list-item>
<list-item><label>&#x25FE;</label> <p>Sensible a may&#x00FA;sculas/min&#x00FA;sculas</p></list-item>
</list>
<p>Naturalmente, en las cadenas de texto y en los comentarios podemos escribir cualquier car&#x00E1;cter en la codificaci&#x00F3;n habitual en la actualidad (utf-8)</p>
</sec>
<sec id="c11-s5">
<label><bold>11.5.</bold></label>
<title><bold>Tipado</bold></title>
<p>Python es</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Din&#x00E1;micamente tipado, (dynamically typed ). No es est&#x00E1;ticamente tipado (statically typed )</p>
<p>Una variable puede cambiar su tipo, din&#x00E1;micamente</p></list-item>
<list-item><label>&#x25FE;</label> <p>Fuertemente tipado, (strongly typed ). No es d&#x00E9;bilmente tipado (weakly typed )</p>
<p>Este concepto no es absoluto, decimos que ciertos lenguajes tienen tipado m&#x00E1;s fuerte o m&#x00E1;s d&#x00E9;bil que otros</p>
<p>Si alg&#x00FA;n objeto, variable, m&#x00E9;todo, funci&#x00F3;n... espera cierto tipo de objeto/de dato:</p>
<list list-type="bullet">
<list-item><p>Un lenguaje fuertemente tipado ha de recibir o bien exactamente ese tipo o bien uno muy parecido, de forma que pueda hacerse una conversi&#x00F3;n autom&#x00E1;tica sin p&#x00E9;rdida de informaci&#x00F3;n</p>
<p>Obliga al programador a conversiones expl&#x00ED;citas. Esto resulta r&#x00ED;gido, tal vez farragoso, pero facilita la seguridad</p></list-item>
<list-item><p>Un lenguaje d&#x00E9;bilemente tipado, admite casi cualquier cosa. Esto resulta c&#x00F3;modo, flexible, potencialmente peligroso</p></list-item>
</list></list-item>
</list>
</sec>
<sec id="c11-s6">
<label><bold>11.6.</bold></label>
<title><target target-type="page" id="pges_147"/><bold>Declaraci&#x00F3;n de objetos</bold></title>
<p>En Python la declaraci&#x00F3;n de objetos (variables) es impl&#x00ED;cita (no hay declaraci&#x00F3;n expl&#x00ED;cita)</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Las variables &#x201C;nacen&#x201D; cuando se les asigna un valor</p></list-item>
<list-item><label>&#x25FE;</label> <p>Las variables &#x201C;desaparecen&#x201D; cuando se sale de su &#x00E1;mbito</p></list-item>
<list-item><label>&#x25FE;</label> <p>La declaraci&#x00F3;n impl&#x00ED;cita de variables como en perl puede provocar resultados desastrosos</p></list-item>
</list>
<preformat>
#!/usr/bin/perl
$sum_elementos= 3 + 4 + 17;
$media=suma_elementos / 3;&#x00A0;&#x00A0;&#x00A0;# deletreamos mal la variable
print $media;&#x00A0;&#x00A0;&#x00A0;# y provocamos resultado incorrecto
</preformat>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Pero Python no permite referenciar variables a las que nunca se ha asignado un valor.</p></list-item>
</list>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
sum_elementos= 3 + 4 + 17
media=suma_elementos / 3 <italic># deletreamos mal la variable</italic>
print(media) <italic># y el int&#x00E9;prete nos avisa con un error</italic>
</preformat></p></td>
</tr>
</tbody></table>
</table-wrap>
</sec>
<sec id="c11-s7">
<label><bold>11.7.</bold></label>
<title><bold>Funciones predefinidas</bold></title>
<sec id="c11-s7-1">
<title><bold>Funciones predefinidas</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>abs()</code> valor absoluto</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>float()</code> convierte a float</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>int()</code> convierte a int</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>str()</code> convierte a string</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>round()</code> redondea</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>input()</code> acepta un valor desde teclado</p></list-item>
</list>
</sec></sec>
<sec id="c11-s8">
<label><bold>11.8.</bold></label>
<title><target target-type="page" id="pges_148"/><bold>Tabulaci&#x00F3;n</bold></title>
<sec id="c11-s8-1">
<title><bold>Sangrado y separadores de sentencias</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>&#x00A1;En Python NO hay llaves ni <code>begin-end</code> para encerrar bloques de c&#x00F3;digo! Un mayor nivel de sangrado indica que comienza un bloque, y un menor nivel indica que termina un bloque.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Las sentencias se terminan al acabarse la l&#x00ED;nea (salvo casos especiales donde la sentencia queda &#x201C;abierta&#x201D;: en mitad de expresiones entre par&#x00E9;ntesis, corchetes o llaves).</p></list-item>
<list-item><label>&#x25FE;</label> <p>El car&#x00E1;cter \ se utiliza para extender una sentencia m&#x00E1;s all&#x00E1; de una linea, en los casos en que no queda &#x201C;abierta&#x201D;.</p></list-item>
<list-item><label>&#x25FE;</label> <p>El car&#x00E1;cter : se utiliza como separador en sentencias compuestas. Ej.: para separar la definici&#x00F3;n de una funci&#x00F3;n de su c&#x00F3;digo.</p></list-item>
<list-item><label>&#x25FE;</label> <p>El car&#x00E1;cter ; se utiliza como separador de sentencias escritas en la misma l&#x00ED;nea.</p></list-item>
<list-item><label>&#x25FE;</label> <p>La recomendaci&#x00F3;n oficial es emplear 4 espacios para cada nivel de sangrado</p>
<list list-type="bullet">
<list-item><p>PEP-8 Style Guide for Python Code</p></list-item>
<list-item><p>David Goodger, <italic>Code Like a Pythonista: Idiomatic Python</italic> Traducci&#x00F3;n al espa&#x00F1;ol:</p>
<p><italic>Programa como un Pythonista: Python Idiom&#x00E1;tico</italic></p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Emplear 8 espacios o emplear tabuladores es legal, con tal de que no lo mezclemos <xref ref-type="fn" rid="FN8"><sup>8</sup></xref></p></list-item>
</list>
</sec></sec>
<sec id="c11-s9">
<label><bold>11.9.</bold></label>
<title><bold>Tipos de objeto</bold></title>
<sec id="c11-s9-1">
<title><bold>Tipos de objeto</bold></title>
<p>En python todo son objetos: cadenas, listas, diccionarios, funciones, m&#x00F3;dulos. . .</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_149"/>En los lenguajes de scripting m&#x00E1;s antiguos como bash o tcl, el &#x00FA;nico tipo de datos es la cadena</p></list-item>
<list-item><label>&#x25FE;</label> <p>Los lenguajes imperativos m&#x00E1;s habituales (C, C++, pascal. . . ) suelen tener (con variantes) los tipos: booleano, car&#x00E1;cter, cadena, entero, real y matriz</p></list-item>
<list-item><label>&#x25FE;</label> <p>Python tiene booleanos, enteros, reales y cadenas. Y adem&#x00E1;s, cadenas unicode, listas, tuplas, n&#x00FA;meros complejos, diccionarios, conjuntos...</p>
<list list-type="bullet">
<list-item><p>En terminolog&#x00ED;a python se denominan <italic>tipos de objeto</italic></p></list-item>
<list-item><p>Estos tipos de objeto de alto nivel facilitan mucho el trabajo del programador</p></list-item>
</list></list-item>
</list>
<p>En python es muy importante distinguir entre</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Objetos inmutables: N&#x00FA;meros, cadenas y tuplas</p>
<list list-type="bullet">
<list-item><p>Se pasan a las funciones por valor</p></list-item>
<list-item><p>Si est&#x00E1;n declarados fuera de una funci&#x00F3;n son globales y para modificarlos dentro de la funci&#x00F3;n, es necesaria la sentencia <italic>global</italic></p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Objetos mutables: Todos los dem&#x00E1;s</p>
<list list-type="bullet">
<list-item><p>Se pasan a las funciones por referencia</p></list-item>
<list-item><p>Si est&#x00E1;n declarados fuera de una funci&#x00F3;n son globales, pero no hace falta la sentencia <italic>global</italic> para modificarlos dentro de la funci&#x00F3;n, puesto que pueden ser modificados a trav&#x00E9;s de sus m&#x00E9;todos</p></list-item>
</list></list-item>
</list>
</sec>
<sec id="c11-s9-2">
<title><bold>Comprobaci&#x00F3;n de tipos</bold></title>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3 #</italic>
<italic>comprueba_tipos.py</italic>
if isinstance(&#x201C;Bla bl&#x00E1;&#x201D;, str):

&#x00A0;&#x00A0;&#x00A0;print(&#x201C;ok, es una cadena&#x201D;)
else:
&#x00A0;&#x00A0;&#x00A0;print(&#x201C;no es una cadena&#x201D;)
</preformat></p></td>
</tr>
</tbody></table></table-wrap>
<p>Tipos de objeto habituales:<target target-type="page" id="pges_150"/></p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
bool
int
float
list
str
dict
tuple
</preformat></p></td></tr>
</tbody></table></table-wrap>
</sec>
<sec id="c11-s9-s1">
<label><bold>11.9.1.</bold></label>
<title><bold>Cadenas</bold></title>
<sec id="c11-s9-s1-1">
<title><bold>Cadenas</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>No existe tipo <code>char</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>Podemos emplear indistintamente la comilla simple o la doble (con tal de que el cierre coincida con la apertura)
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
print(&#x201C;hola&#x201D;)
print(&#x0027;hola&#x0027;)
print(&#x0027;me dijo &#x201C;hola&#x0022;&#x0027;)
</preformat></p></td></tr></tbody>
</table></table-wrap></p>
<p>Lo que resulta m&#x00E1;s legible que escapar los caracteres especiales
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
print(&#x0027;me dijo \&#x0027;hola\&#x0027;&#x0027;)
</preformat></p></td></tr></tbody></table></table-wrap></p></list-item>
<list-item><label>&#x25FE;</label> <p>Se permiten caracteres especiales, p.e. nueva l&#x00ED;nea
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
print(&#x0022;hola\nque tal&#x0022;)
</preformat></p></td></tr></tbody></table></table-wrap></p></list-item>
<list-item><label>&#x25FE;</label> <p>Cadenas crudas: esto imprime la barra y la n, literalmente
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
print(r&#x0022;&#x0022;&#x0022;hola\nque tal&#x0022;&#x0022;&#x0022;)
</preformat></p></td></tr></tbody></table></table-wrap></p></list-item>
<list-item><label>&#x25FE;</label> <p>El operador <code>+</code> concatena cadenas, y el <code>*</code> las repite un n&#x00FA;mero entero de veces</p></list-item>
<list-item><label>&#x25FE;</label> <p>Para concatenar una cadena con un objeto de tipo diferente, podemos convertir el objeto en cadena mediante la funci&#x00F3;n <code>str()</code>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
&#x003E;&#x003E;&#x003E; gamma=0.12
&#x003E;&#x003E;&#x003E; print &#x201C;gamma vale &#x201C;+str(gamma)
gamma vale 0.12
</preformat></p></td></tr></tbody></table></table-wrap></p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_151"/>Se puede acceder a los caracteres de cadenas mediante &#x00ED;ndices y rodajas como en las listas</p></list-item>
<list-item><label>&#x25FE;</label> <p>Las cadenas son inmutables. Ser&#x00ED;a err&#x00F3;neo <code>a[1]=...</code></p></list-item>
</list>
</sec></sec>
<sec id="c11-s9-s2">
<label><bold>11.9.2.</bold></label>
<title><bold>Listas</bold></title>
<sec id="c11-s9-s2-1">
<title><bold>Listas</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Tipo de datos predefinido en Python, va mucho m&#x00E1;s all&#x00E1; de los arrays</p></list-item>
<list-item><label>&#x25FE;</label> <p>Es un conjunto <italic>indexado</italic> de elementos, no necesariamente homog&#x00E9;neos</p></list-item>
<list-item><label>&#x25FE;</label> <p>Sintaxis:Identificador de lista, mas &#x00ED;ndice entre corchetes</p></list-item>
<list-item><label>&#x25FE;</label> <p>Cada elemento se separa del anterior por un car&#x00E1;cter ,
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># listas01.py</italic>
a=[&#x0027;rojo&#x0027;,&#x0027;amarillo&#x0027;]
a.append(&#x0027;verde&#x0027;)
print(a) &#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;<italic># [&#x0027;rojo&#x0027;, &#x0027;amarillo&#x0027;, &#x0027;verde&#x0027;]</italic>
print(a[2]) &#x00A0;&#x00A0;&#x00A0;<italic># verde</italic>
print(len(a)) &#x00A0;<italic># 3</italic>

b=[&#x0027;uno&#x0027;,2, 3.0]&#x00A0;&#x00A0;&#x00A0;<italic># Lista heterog&#x00E9;nea</italic>
</preformat></p></td></tr></tbody></table></table-wrap></p></list-item>
<list-item><label>&#x25FE;</label> <p>El primer elemento tiene &#x00ED;ndice 0.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Un &#x00ED;ndice negativo accede a los elementos empezando por el final de la lista. El &#x00FA;ltimo elemento tiene &#x00ED;ndice -1.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Pueden referirse rodajas (slices) de listas escribiendo dos &#x00ED;ndices entre el car&#x00E1;cter :</p></list-item>
<list-item><label>&#x25FE;</label> <p>La rodaja va desde el primero, incluido, al &#x00FA;ltimo, excluido.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Si no aparece el primero, se entiende que empieza en el primer elemento (0)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Si no aparece el segundo, se entiende que termina en el &#x00FA;ltimo elemento (incluido).<target target-type="page" id="pges_152"/>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># listas02.py</italic>
a=[0,1,2,3,4]
print(a)&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;<italic>#&#x00A0;&#x00A0;&#x00A0;[0, 1, 2, 3, 4]</italic>
print(a[1])&#x00A0;&#x00A0;&#x00A0;<italic>#&#x00A0;&#x00A0;&#x00A0;1</italic>
print(a[0:2]) <italic>#&#x00A0;&#x00A0;&#x00A0;[0,1]</italic>
print(a[3:]) &#x00A0;<italic>#&#x00A0;&#x00A0;&#x00A0;[3,4]</italic>
print(a[-1]) &#x00A0;<italic>#&#x00A0;&#x00A0;&#x00A0;4</italic>
print(a[:-1]) <italic>#&#x00A0;&#x00A0;&#x00A0;[0, 1, 2, 3]</italic>
print(a[:-2])&#x00A0;<italic>#&#x00A0;&#x00A0;&#x00A0;[0, 1, 2]</italic>
</preformat></p></td></tr></tbody></table></table-wrap></p></list-item>
</list>
<p>La misma sintaxis se aplica a las cadenas</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
a=&#x201C;ni&#x00F1;o&#x201D;
print(a[-1]) <italic># o</italic>
</preformat></p></td></tr></tbody></table></table-wrap>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>append()</code> a&#x00F1;ade un elemento al final de la lista</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>insert()</code> inserta un elemento en la posici&#x00F3;n indicada
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># listas03.py</italic>
a=[&#x0027;cero&#x0027;, &#x0027;uno&#x0027;]
a.append(&#x0027;dos&#x0027;)
print(a) &#x00A0;&#x00A0;<italic>#[&#x0027;cero&#x0027;, &#x0027;uno&#x0027;, &#x0027;dos&#x0027;]</italic>
a.insert(1, &#x0027;cero cinco&#x0027;)
print(a) &#x00A0;&#x00A0;<italic>#[&#x0027;cero&#x0027;, &#x0027;cero cinco&#x0027;, &#x0027;uno&#x0027;, &#x0027;dos&#x0027;]</italic>
</preformat></p></td></tr></tbody></table></table-wrap></p></list-item>
<list-item><label>&#x25FE;</label> <p><code>index()</code> busca en la lista un elemento y devuelve el &#x00ED;ndice de la primera aparici&#x00F3;n del elemento en la lista. Si no aparece se eleva una excepci&#x00F3;n.</p>
<p>El operador <code>in</code> devuelve <italic>true</italic> si un elemento aparece en la lista, y <italic>false</italic> en caso contrario.
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
&#x003E;&#x003E;&#x003E; lista=[&#x0027;cero&#x0027;, &#x0027;uno&#x0027;, &#x0027;dos&#x0027;]
&#x003E;&#x003E;&#x003E; print(lista)
[&#x0027;cero&#x0027;, &#x0027;uno&#x0027;, &#x0027;dos&#x0027;]
&#x003E;&#x003E;&#x003E; lista.index(&#x0027;uno&#x0027;)
1
&#x003E;&#x003E;&#x003E; &#x0027;doce&#x0027; in lista
False
&#x003E;&#x003E;&#x003E; print(lista.index(&#x0027;doce&#x0027;))
Traceback (most recent call last):
File &#x201C;&#x003C;stdin&#x003E;&#x201D;, line 1, in &#x003C;module&#x003E;
ValueError: &#x0027;doce&#x0027; is not in list
</preformat></p></td></tr></tbody></table></table-wrap></p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_153"/><code>remove()</code> elimina la primera aparici&#x00F3;n de un elemento en la lista. Si no aparece, eleva una excepci&#x00F3;n.</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>pop()</code> devuelve el &#x00FA;ltimo elemento de la lista, y lo elimina. (Pila)</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>pop(0)</code> devuelve el primer elemento de la lista, y lo elimina. (Cola)
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># listas04.py</italic>
a = [&#x0027;rojo&#x0027;, &#x0027;verde&#x0027;, &#x0027;azul&#x0027;, &#x0027;negro&#x0027;]
a.remove(&#x0027;negro&#x0027;)
print(a) <italic># [&#x0027;rojo&#x0027;, &#x0027;verde&#x0027;, &#x0027;azul&#x0027;]</italic>
print(a.pop()) <italic># azul</italic>
print(a) <italic># [&#x0027;rojo&#x0027;, &#x0027;verde&#x0027;]</italic>
print(a.pop(0)) <italic># rojo</italic>
print(a) <italic># [&#x0027;verde&#x0027;]</italic>
</preformat></p></td></tr></tbody></table></table-wrap></p></list-item>
<list-item><label>&#x25FE;</label> <p>El operador <code>+</code> concatena dos listas, devolviendo una nueva lista</p></list-item>
<list-item><label>&#x25FE;</label> <p>El operador <code>*</code> concatena repetitivamente una lista a s&#x00ED; misma
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># listas05.py</italic>
a = [&#x0027;rojo&#x0027;, &#x0027;verde&#x0027; ]
a = a + [&#x0027;azul&#x0027;]
print(a) <italic># [&#x0027;rojo&#x0027;, &#x0027;verde&#x0027;, &#x0027;azul&#x0027;]</italic>
a += [&#x0027;negro&#x0027;]
print(a) <italic># [&#x0027;rojo&#x0027;, &#x0027;verde&#x0027;, &#x0027;azul&#x0027;, &#x0027;negro&#x0027;]</italic>
a = [&#x0027;x&#x0027;, &#x0027;y&#x0027;]
a = a * 3
print(a) <italic># [&#x0027;x&#x0027;, &#x0027;y&#x0027;, &#x0027;x&#x0027;, &#x0027;y&#x0027;, &#x0027;x&#x0027;, &#x0027;y&#x0027;]</italic>
</preformat></p></td></tr></tbody></table></table-wrap></p></list-item>
</list>
</sec>
<sec id="c11-s9-s2-2">
<title><bold>Funciones, m&#x00E9;todos y operadores</bold></title>
<p>El lenguaje python:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_154"/>Emplea el modelo de programaci&#x00F3;n imperativa convencional Por tanto usa funciones, cuya sintaxis es
<preformat>
funcion(objeto)
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Emplea el modelo de programaci&#x00F3;n orientada a objetos Por tanto usa m&#x00E9;todos, cuya sintaxis es
<preformat>
objeto.metodo()
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Es de muy alto nivel, cuenta con operadores con funcionalidad avanzada La sintaxis de un operador es
<preformat>
elemento1 operador elemento2
</preformat></p></list-item>
</list>
<p>Esto puede provocar confusi&#x00F3;n, es f&#x00E1;cil equivocarse e intentar usar una funci&#x00F3;n como un m&#x00E9;todo o viceversa</p>
<p>Este script emplea la funci&#x00F3;n <code>len()</code>, el m&#x00E9;todo <code>pop()</code> y el operador in</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># funcion_operador_metodo.py</italic>
lista=[&#x201C;rojo&#x201D;,&#x201C;amarillo&#x201D;,&#x201C;verde&#x201D;]
print(len(lista)) &#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;<italic>#3</italic>
print(&#x201C;blanco&#x201D; in lista)&#x00A0;&#x00A0;&#x00A0;<italic>#False</italic>
print(lista.pop()) &#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;<italic>#verde</italic>
print(lista) &#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;<italic>#[&#x0027;rojo&#x0027;, &#x0027;amarillo&#x0027;]</italic>
</preformat></p></td>
</tr></tbody></table></table-wrap>
</sec>
<sec id="c11-s9-s2-3">
<title><bold>Inversi&#x00F3;n de una lista</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>El m&#x00E9;todo reverse() invierte las posiciones de los elementos en una lista.</p></list-item>
</list>
<p>No devuelve nada, simplemente altera la lista sobre la que se aplican.</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
&#x003E;&#x003E;&#x003E; a=[&#x0027;sota&#x0027;, &#x0027;caballo&#x0027;, &#x0027;rey&#x0027;]
&#x003E;&#x003E;&#x003E; a.reverse()
&#x003E;&#x003E;&#x003E; print(a)
[&#x0027;rey&#x0027;, &#x0027;caballo&#x0027;, &#x0027;sota&#x0027;]
</preformat></p></td></tr>
</tbody></table></table-wrap>
<p>Errores t&#x00ED;picos:</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
a = reverse(a) &#x00A0;&#x00A0;&#x00A0;<italic># &#x00A1;Mal! Esta funci&#x00F3;n no existe</italic>
a = a.reverse() &#x00A0;&#x00A0;<italic># Ahora valdr&#x00ED;a None</italic>
</preformat></p></td></tr>
</tbody></table></table-wrap>
<p>El primer error lo indica el int&#x00E9;rprete. El segundo es m&#x00E1;s peligroso</p>
</sec>
<sec id="c11-s9-s2-4">
<title><target target-type="page" id="pges_155"/><bold>Ordenar una lista</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>La funci&#x00F3;n <code>sorted()</code> devuelve una lista ordenada (no la modifica)</p></list-item>
<list-item><label>&#x25FE;</label> <p>El m&#x00E9;todo <code>sort()</code> ordena una lista (Modifica la lista, devuelve <italic>None</italic>)</p></list-item>
</list>
<p>Ambas admiten personalizar la ordenaci&#x00F3;n de elementos complejos, pasando como argumento una funci&#x00F3;n que devuelva la parte del elemento a usar como clave. Esta funci&#x00F3;n argumento debe llamarse <italic>key</italic></p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># ordenar01.py</italic>
mi_lista=[ &#x201C;gamma&#x201D;, &#x201C;alfa&#x201D;, &#x201C;beta&#x201D;]

print( sorted(mi_lista) ) &#x00A0;<italic>#alfa, beta, gamma</italic>
print( mi_lista ) &#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;<italic>#gamma, alfa, beta. No ha cambiado.</italic>

print( mi_lista.sort() ) &#x00A0;&#x00A0;<italic>#Devuelve &#x0027;None&#x0027;</italic>
print( mi_lista ) &#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;<italic>#alfa, beta, gamma. La ha ordenado</italic>
</preformat></p></td></tr></tbody></table></table-wrap>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># ordenar02.py</italic>
mi_lista=[ [&#x0027;IV&#x0027;,4] , [&#x0027;XX&#x0027;,20], [&#x0027;III&#x0027;,3] ]

def clave_lista(lista):
&#x00A0;&#x00A0;return lista[1]

mi_lista.sort(key = clave_lista)
print( mi_lista )
</preformat></p></td></tr></tbody>
</table></table-wrap>
</sec>
<sec id="c11-s9-s2-5">
<title><bold>Split, join</bold></title>
<p>Es muy frecuente trocear una cadena para formar en un lista (split) y concatenar los elementos de una lista para formar una cadena (join)</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># split_join.py</italic>
mi_cadena=&#x201C;esto es una prueba&#x201D;
print(mi_cadena.split()) &#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;<italic># [&#x0027;esto&#x0027;, &#x0027;es&#x0027;, &#x0027;una&#x0027;, &#x0027;prueba&#x0027;]</italic>
print(&#x201C;esto-tambien&#x201D;.split(&#x201C;-&#x201D;)) &#x00A0;<italic># [&#x0027;esto&#x0027;, &#x0027;tambien&#x0027;]</italic>
mi_lista=[&#x201C;as&#x201D;,&#x201C;dos&#x201D;,&#x201C;tres&#x201D;]
<italic>#print(mi_lista.join()) &#x00A0;&#x00A0;#&#x00A1;ERROR! Parecer&#x00ED;a l&#x00F3;gico que join()</italic>
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;<italic># fuera un m&#x00E9;todo del tipo lista. Pero no</italic>
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;<italic># lo es</italic>.
print(&#x201C;&#x201D;.join(mi_lista)) &#x00A0;<italic># Es un m&#x00E9;todo del tipo string, hay</italic>
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;<italic># que invocarlo desde una cadena cualquiera,</italic>
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;<italic># que ser&#x00E1; el separador.</italic>
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;<italic># Devuelve &#x201C;asdostres&#x201D;</italic>
print(&#x201D;,&#x201D;.join(mi_lista)) <italic># Devuelve &#x201C;as,dos,tres&#x201D;</italic>
</preformat></p></td></tr>
</tbody></table></table-wrap>
</sec>
<sec id="c11-s9-s2-6">
<title><target target-type="page" id="pges_156"/><bold>Otros m&#x00E9;todos de los objetos string</bold></title>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># cadenas.py</italic>
print(&#x201C;hola mundo&#x201D;.upper()) <italic># HOLA MUNDO</italic>
print(&#x201C;HOLA MUNDO&#x201D;.lower()) <italic># hola mundo</italic>

<italic># Estos m&#x00E9;todos devuelven una cadena,</italic>
<italic># sin modificar la cadena original</italic>
a=&#x201C;prueba&#x201D;
print(a.upper()) &#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;<italic># PRUEBA</italic>
print(a) &#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;<italic># prueba</italic>

<italic># find() indica la posici&#x00F3;n de una subcadena</italic>
print(&#x201C;buscando una subcadena&#x201D;.find(&#x201C;una&#x201D;)) &#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;<italic># 9</italic>
print(&#x0022;buscando una subcadena&#x0022;.find(&#x0022;nohay&#x0022;)) &#x00A0;&#x00A0;&#x00A0;<italic># -1</italic>

<italic># strip() devuelve una copia de la cadena quitando</italic>
<italic># espacios a derecha e izda, retornos de carro, etc</italic>
print(&#x201D; hola \n&#x201C;.strip()) &#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;<italic># &#x0027;hola&#x0027;</italic>

print(&#x0022;te digo que no&#x0022;.replace(&#x0022;digo&#x0022;,&#x0022;diego&#x0022;))
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;# imprime &#x201C;te diego que no&#x201D;
</preformat></p></td></tr>
</tbody></table></table-wrap>
</sec>
<sec id="c11-s9-s2-7">
<title><bold>Nombres de objeto</bold></title>
<p>Con frecuencia hablamos de <italic>variables</italic>, porque es el t&#x00E9;rmino tradicional en programaci&#x00F3;n. Pero Python no tiene <italic>variables</italic>, sino <italic>nombres</italic>. Son referencias a objetos<target target-type="page" id="pges_157"/></p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># nombres.py</italic>
x=[&#x0027;uno&#x0027;]
y=x &#x00A0;&#x00A0;&#x00A0;<italic># y apunta al mismo objeto</italic>
print(x) &#x00A0;&#x00A0;&#x00A0;<italic># [&#x0027;uno&#x0027;]</italic>
print(y) &#x00A0;&#x00A0;&#x00A0;<italic># [&#x0027;uno&#x0027;]</italic>
x=[&#x0027;dos&#x0027;] &#x00A0;&#x00A0;&#x00A0;<italic># x apunta a un nuevo objeto</italic>

print(x) <italic># [&#x0027;dos&#x0027;] # El objeto nuevo</italic>
print(y) <italic># [&#x0027;uno&#x0027;] # El objeto antiguo</italic>

x=[&#x0027;uno&#x0027;]
y=x &#x00A0;&#x00A0;&#x00A0;<italic># y apunta al mismo objeto</italic>
x.append(&#x0027;dos&#x0027;) &#x00A0;&#x00A0;&#x00A0;<italic># modificamos el objeto</italic>
print(x) <italic># [&#x0027;uno&#x0027;,&#x0027;dos&#x0027;] &#x00A0;&#x00A0;&#x00A0;# el objeto modificado</italic>
print(y) <italic># [&#x0027;uno&#x0027;,&#x0027;dos&#x0027;] &#x00A0;&#x00A0;&#x00A0;# el mismo objeto, modificado</italic>
</preformat></p></td></tr>
</tbody></table></table-wrap>
</sec></sec>
<sec id="c11-s9-s3">
<label><bold>11.9.3.</bold></label>
<title><bold>Diccionarios</bold></title>
<sec id="c11-s9-s3-1">
<title><bold>Diccionarios</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Es un conjunto <italic>desordenado</italic> de elementos</p></list-item>
<list-item><label>&#x25FE;</label> <p>Cada elemento del diccionario es un par clave-valor.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Se pueden obtener valores a partir de la clave, pero no al rev&#x00E9;s.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Longitud variable</p></list-item>
<list-item><label>&#x25FE;</label> <p>Hace las veces de los <italic>registros</italic> en otros lenguajes</p></list-item>
<list-item><label>&#x25FE;</label> <p>Atenci&#x00F3;n: Se declaran con {}, se refieren con []</p></list-item>
<list-item><label>&#x25FE;</label> <p>Asignar valor a una clave existente reemplaza el antiguo</p></list-item>
<list-item><label>&#x25FE;</label> <p>Una clave de tipo cadena es sensible a may&#x00FA;sculas/min&#x00FA;sculas</p></list-item>
<list-item><label>&#x25FE;</label> <p>Pueden a&#x00F1;adirse entradas nuevas al diccionario</p></list-item>
<list-item><label>&#x25FE;</label> <p>Los diccionarios se mantienen desordenados</p></list-item>
<list-item><label>&#x25FE;</label> <p>Los valores de un diccionario pueden ser de cualquier tipo</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_158"/>Las claves pueden ser enteros, cadenas y alg&#x00FA;n otro tipo</p></list-item>
<list-item><label>&#x25FE;</label> <p>Pueden borrarse un elemento del diccionario con del</p></list-item>
<list-item><label>&#x25FE;</label> <p>Pueden borrarse todos los elementos del diccionario con clear()</p></list-item>
</list>
<p>Otras operaciones con diccionarios:</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p>&#x25FE;</p></td>
<td valign="top" align="left"><p><code>len(d)</code></p></td>
<td valign="top" align="left"><p>devuelve el n&#x00FA;mero de elementos de <code>d</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p>&#x25FE;</p></td>
<td valign="top" align="left"><p><code>d.has_key(k)</code></p></td>
<td valign="top" align="left"><p>devuelve 1 si existe la clave <code>k</code> en <code>d</code>, 0 en caso contrario</p></td>
</tr>
<tr>
<td valign="top" align="left"><p>&#x25FE;</p></td>
<td valign="top" align="left"><p><code>k in d</code></p></td>
<td valign="top" align="left"><p>equivale a: <code>d.has_key(k)</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p>&#x25FE;</p></td>
<td valign="top" align="left"><p><code>d.items()</code></p></td>
<td valign="top" align="left"><p>devuelve la lista de elementos de <code>d</code> (pares clave:valor)</p></td>
</tr>
<tr>
<td valign="top" align="left"><p>&#x25FE;</p></td>
<td valign="top" align="left"><p><code>d.keys()</code></p></td>
<td valign="top" align="left"><p>devuelve la lista de claves de <code>d</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># diccionarios.py</italic>
pais={&#x0027;de&#x0027;: &#x0027;Alemania&#x0027;, &#x0027;fr&#x0027;: &#x0027;Francia&#x0027;, &#x0027;es&#x0027;: &#x0027;Espa&#x00F1;a&#x0027;}
print(pais[&#x201C;fr&#x201D;])

extension={}
extension[&#x0027;py&#x0027;]=&#x0027;python&#x0027;
extension[&#x0027;txt&#x0027;]=&#x0027;texto plano&#x0027;
extension[&#x0027;mp3&#x0027;]=&#x0027;MPEG layer 3&#x0027;

for x in pais.keys():
&#x00A0;&#x00A0;&#x00A0;print(x, pais[x])

del pais[&#x0027;fr&#x0027;] &#x00A0;&#x00A0;&#x00A0;<italic># Borramos francia</italic>
print(len(pais)) &#x00A0;&#x00A0;&#x00A0;<italic># Quedan 2 paises</italic>
print(&#x0027;es&#x0027; in pais) &#x00A0;&#x00A0;&#x00A0;<italic># True</italic>
pais[&#x0027;es&#x0027;]=&#x201C;Spain&#x201D; &#x00A0;&#x00A0;&#x00A0;<italic># modificamos un elemento</italic>
pais.clear() &#x00A0;&#x00A0;&#x00A0;<italic># Borramos todas las claves</italic>
</preformat></p></td></tr>
</tbody></table></table-wrap>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># diccionarios02.py</italic>
diccionario={&#x201C;juan&#x201D;: [&#x201C;empanada&#x201D;] ,
&#x00A0;&#x00A0;&#x00A0;&#x201C;maria&#x0022;: [&#x0022;refrescos&#x201D;,&#x0022;vino&#x0022;]}
diccionario[&#x201C;luis&#x201D; ] = [&#x201C;patatas fritas&#x201D;,&#x201C;platos plastico&#x201D;]
diccionario[&#x201C;luis&#x201D;].append(&#x201C;vasos plastico&#x201D;)
claves = diccionario.keys() <italic># Devuelve un tipo dict_keys</italic>
claves = list(claves) <italic># Lo convertimos en lista para ordenarlo</italic>
claves.sort()
for clave in claves:
&#x00A0;&#x00A0;&#x00A0;print(clave, diccionario[clave])
</preformat></p></td></tr>
</tbody></table></table-wrap>
<p><target target-type="page" id="pges_159"/>Resultado de la ejecuci&#x00F3;n:</p>
<preformat>
juan [&#x0027;empanada&#x0027;]
luis [&#x0027;patatas fritas&#x0027;, &#x0027;platos plastico&#x0027;, &#x0027;vasos plastico&#x0027;]
maria [&#x0027;refrescos&#x0027;, &#x0027;vino&#x0027;]
</preformat>
</sec>
<sec id="c11-s9-s3-2">
<title><bold>Acceso a las claves mediante el operador in</bold></title>
<p>Una forma alternativa de obtener las claves de un diccionario:</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
for clave in d:
&#x00A0;&#x00A0;&#x00A0;print(clave)
</preformat></p></td></tr>
</tbody></table></table-wrap>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Esto es m&#x00E1;s eficiente que emplear el m&#x00E9;todo keys()</p></list-item>
<list-item><label>&#x25FE;</label> <p>Es aplicable a listas y tuplas</p></list-item>
<list-item><label>&#x25FE;</label> <p>Aunque en ocasiones seguiremos necesitando el m&#x00E9;todo keys()
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
claves=list(diccionario.keys())
claves.sort()
</preformat></p></td></tr></tbody></table></table-wrap></p></list-item>
</list>
</sec></sec>
<sec id="c11-s9-s4">
<label><bold>11.9.4.</bold></label>
<title><bold>Tuplas</bold></title>
<sec id="c11-s9-s4-1">
<title><bold>Tuplas</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Tipo predefinido de Python para una lista inmutable</p></list-item>
<list-item><label>&#x25FE;</label> <p>Se define de la misma manera que las listas, pero con los elementos entre par&#x00E9;ntesis</p></list-item>
<list-item><label>&#x25FE;</label> <p>Las tuplas no tienen m&#x00E9;todos: no se pueden a&#x00F1;adir elementos, ni cambiarlos, ni buscar con <code>index()</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>S&#x00ED; puede comprobarse la existencia con el operador <code>in</code>.<target target-type="page" id="pges_160"/></p></list-item>
</list>
<preformat>
&#x003E;&#x003E;&#x003E; t = (&#x0022;a&#x0022;, &#x0022;b&#x0022;, &#x0022;blablabla&#x0022;, &#x0022;z&#x0022;, &#x0022;example&#x0022;)
&#x003E;&#x003E;&#x003E; t[0]
&#x0027;a&#x0027;
&#x003E;&#x003E;&#x003E; &#x0027;a&#x0027; in t
True
&#x003E;&#x003E;&#x003E; t[0] = &#x0022;b&#x0022;
Traceback (most recent call last):
File &#x0022;&#x003C;stdin&#x003E;&#x0022;, line 1, in ?
TypeError: object doesn&#x0027;t support item assignment
</preformat>
<p>Utilidad de las tuplas:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Son m&#x00E1;s r&#x00E1;pidas que las listas</p></list-item>
<list-item><label>&#x25FE;</label> <p>Pueden ser una clave de un diccionario (no as&#x00ED; las listas)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Se usan en el formateo de cadenas</p></list-item>
</list>
<p><code>tuple(li)</code> devuelve una tupla con los elementos de la lista <code>li</code></p>
<p><code>list(t)</code> devuelve una lista con los elementos de la tupla <code>t</code></p>
</sec>
<sec id="c11-s9-s4-2">
<title><bold>Asignaciones m&#x00FA;ltiples</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Pueden hacerse tambi&#x00E9;n tuplas de nombres de objetos
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
&#x003E;&#x003E;&#x003E; v = (&#x0027;a&#x0027;, &#x0027;b&#x0027;, &#x0027;e&#x0027;)
&#x003E;&#x003E;&#x003E; (x, y, z) = v
&#x003E;&#x003E;&#x003E; x
&#x0027;a&#x0027;
</preformat></p></td></tr></tbody></table></table-wrap></p></list-item>
</list>
</sec></sec></sec>
<sec id="c11-s10">
<label><bold>11.10.</bold></label>
<title><bold>Sentencias de control</bold></title>
<sec id="c11-s10-1">
<title><bold>If</bold></title>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># if01.py</italic>
x = True
if x :
&#x00A0;&#x00A0;&#x00A0;print(&#x0027;verdadero&#x0027;)
else:
&#x00A0;&#x00A0;&#x00A0;print(&#x0027;falso&#x0027;)
</preformat></p></td></tr></tbody>
</table></table-wrap>
<p>N&#x00F3;tese como el car&#x00E1;cter : introduce cada bloque de sentencias.</p>
<p>Si en una rama queremos poner la sentencia nula: <code>pass</code></p>
<p>No confundir con el valor nulo: <code>None</code><target target-type="page" id="pges_161"/></p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># if02.py</italic>

x = int(input(&#x201C;Escribe un entero: &#x201C;))
if x &#x003C; 0:
&#x00A0;&#x00A0;&#x00A0;x = 0
&#x00A0;&#x00A0;&#x00A0;print(&#x0027;Valor negativo, modificado a cero&#x0027;)
elif x == 0:
&#x00A0;&#x00A0;&#x00A0;print(&#x0027;Cero&#x0027;)
elif x == 1:
&#x00A0;&#x00A0;&#x00A0;print(&#x0027;Uno&#x0027;)
else:
&#x00A0;&#x00A0;&#x00A0;print(&#x0027;M&#x00E1;s de uno&#x0027;)
</preformat></p></td></tr></tbody>
</table></table-wrap>
<p>La sentencia case aparece en la versi&#x00F3;n 3.10 (Octubre 2021)</p>
</sec>
<sec id="c11-s10-2">
<title><bold>For</bold></title>
<p>En los lenguajes <italic>convencionales</italic>, la cl&#x00E1;usula <italic>for</italic> sirve para que un entero recorra una serie de valores.</p>
<p>En python es diferente: recorre un objeto iterable, como una lista o una tupla. Por cada elemento del iterable, ejecuta el bloque de c&#x00F3;digo</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
lista = [&#x201C;sota&#x201D;,&#x201C;caballo&#x201D;,&#x201C;rey&#x201D;]
for x in lista:
&#x00A0;&#x00A0;&#x00A0;print(x) <italic># Imprime el elemento y nueva l&#x00ED;nea</italic>
</preformat></p></td></tr></tbody></table></table-wrap>
<p>Resultado:</p>
<preformat>
sota
caballo
rey
</preformat>
<p>Si necesitamos un bucle <italic>convencional</italic> podemos emplear la funci&#x00F3;n <italic>range()</italic></p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># rangos01.py</italic>
rango = range(3)
for x in rango:
&#x00A0;&#x00A0;&#x00A0;print(x, end=&#x0027;&#x0027;)
</preformat></p></td></tr></tbody></table></table-wrap>
<p><target target-type="page" id="pges_162"/>Resultado:</p>
<preformat>
012
</preformat>
<p>Observa que la funci&#x00F3;n <italic>print</italic></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Por omisi&#x00F3;n a&#x00F1;ade un car&#x00E1;cter <italic>nueva l&#x00ED;nea</italic> tras cada argumento</p></list-item>
<list-item><label>&#x25FE;</label> <p>Para evitarlo, podemos a&#x00F1;adir el par&#x00E1;metro <italic>end</italic> indicando qu&#x00E9; a&#x00F1;adir. Por ejemplo una cadena vac&#x00ED;a, como en este caso</p></list-item>
</list>
<p>A range() le podemos pasar</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Un elemento: el final del rango</p></list-item>
<list-item><label>&#x25FE;</label> <p>Dos elementos: principio y final</p></list-item>
<list-item><label>&#x25FE;</label> <p>Tres elementos: principio, final e incremento</p></list-item>
</list>
<p>Por omisi&#x00F3;n, el principio es 0 y el incremento es +1</p>
<p>Esta funci&#x00F3;n devuelve un objeto de tipo <italic>range</italic>. Si es necesario, podemos convertirlo en lista con la funci&#x00F3;n <italic>list</italic></p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># rangos02.py</italic>
print(range(2,5)) &#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;<italic># range(2,5)</italic>
print(list(range(2,5))) &#x00A0;&#x00A0;&#x00A0;&#x00A0;<italic># 2, 3, 4</italic>
print(list(range(2,-2,-1))) <italic># 2, 1, 0, -1</italic>
</preformat></p></td></tr></tbody></table></table-wrap>
<p>No deber&#x00ED;amos usar range para los bucles a menos que sea imprescindible. No es idiom&#x00E1;tico en python, a&#x00F1;ade complejidad innecesaria.</p>
<p>No hagas bucles <italic>al estilo Pascal</italic></p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># for.py</italic>

lista=[&#x201C;sota&#x201D;,&#x201C;caballo&#x201D;,&#x201C;rey&#x201D;]
<italic># &#x00A1;&#x00A1;NO HAGAS ESTO!!</italic>
for i in range(len(lista)):
&#x00A0;&#x00A0;&#x00A0;print(lista[i])

<italic># Lo idiom&#x00E1;tico en python es</italic>
for x in lista:
&#x00A0;&#x00A0;&#x00A0;print(x)
</preformat></p></td></tr></tbody></table></table-wrap>
</sec>
<sec id="c11-s10-3">
<title><target target-type="page" id="pges_163"/><bold>While</bold></title>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
&#x003E;&#x003E;&#x003E; a=0
&#x003E;&#x003E;&#x003E; while a&#x003C;10:
... &#x00A0;&#x00A0;&#x00A0;print(a, end=&#x0027;&#x0027;)
... &#x00A0;&#x00A0;&#x00A0;a+=1
...
0 1 2 3 4 5 6 7 8 9
</preformat></p></td></tr>
</tbody></table></table-wrap>
<p>break sale de un bucle. (Aunque seg&#x00FA;n los principios de la <italic>programaci&#x00F3;n estructurada</italic>, <code>break</code> no deber&#x00ED;a usarse nunca. Empl&#x00E9;alo solo si est&#x00E1;s muy seguro de lo que haces)</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># while01.py</italic>
a=10
while a &#x003E; 0:
&#x00A0;&#x00A0;&#x00A0;print(a, end=&#x0027;&#x0027;)
&#x00A0;&#x00A0;&#x00A0;a-=1
</preformat></p></td></tr>
</tbody></table></table-wrap>
<p>equivale a</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># while02.py</italic>
a=10
while True:
&#x00A0;&#x00A0;&#x00A0;print(a, end=&#x0027;&#x0027;)
&#x00A0;&#x00A0;&#x00A0;if a==1:
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;break
&#x00A0;&#x00A0;&#x00A0;a-=1
</preformat></p></td></tr></tbody></table></table-wrap>
</sec></sec>
<sec id="c11-s11">
<label><bold>11.11.</bold></label>
<title><bold>format</bold></title>
<sec id="c11-s11-1">
<title><bold>format</bold></title>
<p>Las cadenas cuentan con el m&#x00E9;todo <code>format()</code></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Dentro de una cadena, podemos indicar, entre llaves, qu&#x00E9; campos se mostrar&#x00E1;n y con qu&#x00E9; formato.</p>
<p>Format tiene un microlenguaje para esto</p></list-item>
<list-item><label>&#x25FE;</label> <p>Los argumentos de <code>format()</code> ser&#x00E1;n los campos</p></list-item>
</list>
<p><target target-type="page" id="pges_164"/>Ejemplo: Indicar qu&#x00E9; campo mostrar, a partir del ordinal</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>

name=&#x201C;Juan&#x201D;
surname=&#x201C;Garc&#x00ED;a&#x201D;
print(&#x0022;Se llama {0} y se apellida {1}&#x201C;.format(name,surname))
print(&#x0022;Se llama {} y se apellida {}&#x201C;.format(name,surname))

persona=[&#x201C;Juan&#x201D;,&#x201C;Garc&#x00ED;a&#x201D;]
print(&#x0022;Se llama {0[0]} y se apellida {0[1]}&#x201C;.format(persona))

persona={&#x201C;name&#x201D;:&#x201C;Juan&#x201D;, &#x201C;surname&#x201D;:&#x201C;Garc&#x0131;&#x00E1;&#x201D;}
print(&#x0022;Se llama {0[name]} y se apellida {0[surname]}&#x201C;.format(persona))
</preformat></p></td></tr>
</tbody></table></table-wrap>
<p>Resultado:</p>
<preformat>
Se llama Juan y se apellida Garc&#x00ED;a
Se llama Juan y se apellida Garc&#x00ED;a
Se llama Juan y se apellida Garc&#x00ED;a
Se llama Juan y se apellida Garc&#x00ED;a
</preformat>
<p>Despu&#x00E9;s de indicar qu&#x00E9; campo mostrar, separado por el car&#x00E1;cter dos puntos, podemos especificar cu&#x00E1;ntos caracteres debe ocupar la salida, y si estar&#x00E1; alineada a la derecha (signo de mayor), a la izquierda (signo de menor o ning&#x00FA;n signo) o al centro (acento circunflejo)</p>
<p>Ejemplo: mostrar una palabra, ocupando siempre 12 caracteres</p>
<preformat>
#!/usr/bin/env python3

print(&#x0022;{0:&#x003E;12}{1:&#x003E;12}&#x0022;.format(&#x0022;sota&#x0022;,&#x0022;caballo&#x0022;))
print(&#x0022;{0:&#x003C;12}{1:&#x003C;12}&#x0022;.format(&#x0022;sota&#x0022;,&#x0022;caballo&#x0022;))
print(&#x0022;{0:12}{1:12}&#x0022;.format(&#x0022;sota&#x0022;,&#x0022;caballo&#x0022;))
print(&#x0022;{0:^12}{1:^12}&#x0022;.format(&#x0022;sota&#x0022;,&#x0022;caballo&#x0022;))
</preformat>
<p>Resultado:</p>
<preformat>
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;sota&#x00A0;&#x00A0;&#x00A0;caballo
sota &#x00A0;&#x00A0;&#x00A0;caballo
sota &#x00A0;&#x00A0;&#x00A0;caballo
&#x00A0;&#x00A0;&#x00A0;sota&#x00A0;&#x00A0;&#x00A0;caballo
</preformat>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Si solo hay un campo, podemos omitir el 0 a la izquierda del car&#x00E1;cter dos puntos</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_165"/>Con el car&#x00E1;cter <code>d</code> podemos indicar que el campo contiene un n&#x00FA;mero entero. En este caso, la alineaci&#x00F3;n por omisi&#x00F3;n es a la derecha</p></list-item>
<list-item><label>&#x25FE;</label> <p>Con el car&#x00E1;cter f indicamos que el campo es un n&#x00FA;mero real</p>
<p>Podemos especificar cu&#x00E1;ntos decimales representar. Por ejemplo 4: .4f</p></list-item>
</list>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
print(&#x201D;{:&#x003C;6d} metros&#x201D;.format(592))
print(&#x201D;{:&#x003E;6d} metros&#x201D;.format(592))
print(&#x201D;{0:6d} metros&#x201D;.format(592))
print(&#x201C;Pi vale {:.4f}&#x201D;.format(3.14159265358979))
</preformat></p></td></tr>
</tbody></table></table-wrap>
<p>Resultado:</p>
<preformat>
592 metros
592 metros
592 metros
Pi vale 3.1416
</preformat>
<p>Naturalmente, la cadena no tiene por qu&#x00E9; ser una constante, puede ser una variable</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>

x=12.3
y=0.345
z=34000
template=&#x201C;{:8},{:8},{:8}&#x201D;
msg=template.format(x,y,z)
print(msg) <italic># 12.3, 0.345, 34000</italic>
</preformat></p></td></tr></tbody>
</table></table-wrap>
</sec></sec>
<sec id="c11-s12">
<label><bold>11.12.</bold></label>
<title><bold>Funciones</bold></title>
<sec id="c11-s12-1">
<title><bold>Funciones</bold></title>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># grados.py</italic>
def a_centigrado(x):
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x201C;&#x0022;&#x0022;Convierte grados farenheit en grados cent&#x00ED;grados.&#x0022;&#x0022;&#x0022;
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;return (x-32)*(5/9.0)
def a_farenheit(x):
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x201C;&#x0022;&#x0022;Convierte grados cent&#x00ED;grados en grados farenheit.&#x0022;&#x0022;&#x0022;
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;return (x*1.8)+32
</preformat></p></td></tr></tbody></table></table-wrap>
<p><target target-type="page" id="pges_166"/>Los nombres de objeto declarados fuera de una funci&#x00F3;n son globales, y los declarados dentro, locales</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># ambito01.py</italic>
a=3
def f():
&#x00A0;&#x00A0;&#x00A0;b=4
&#x00A0;&#x00A0;&#x00A0;print(a) <italic># 3</italic>
&#x00A0;&#x00A0;&#x00A0;print(b) <italic># 4</italic>
&#x00A0;&#x00A0;&#x00A0;return
f()
print(a) &#x00A0;&#x00A0;&#x00A0;<italic># 3</italic>
print(b) &#x00A0;&#x00A0;&#x00A0;<italic># &#x00A1;Error! B es un objeto local</italic>
</preformat></p></td></tr></tbody></table></table-wrap>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Algunas metodolog&#x00ED;as establecen que los objetos globales deben usarse lo m&#x00ED;nimo posible. Otras los prohiben por completo</p></list-item>
<list-item><label>&#x25FE;</label> <p>Los objetos globales pueden leerse dentro (y fuera) de la funci&#x00F3;n.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Los objetos locales, declarados dentro de una funci&#x00F3;n, son invisibles fuera de ella</p></list-item>
</list>
<p>Supongamos que intentamos modificar el objeto global de esta forma</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># ambito02.py</italic>
a=3
def f():
&#x00A0;&#x00A0;&#x00A0;a=0
&#x00A0;&#x00A0;&#x00A0;print(a) <italic># 0</italic>
&#x00A0;&#x00A0;&#x00A0;return
f()
print(a) &#x00A0;&#x00A0;&#x00A0;<italic># 3 . No se ha modificado</italic>
</preformat></p></td></tr></tbody></table></table-wrap>
<p>No podemos modificar el objeto global sin m&#x00E1;s, lo que sucede es que python crea un nuevo objeto local, con el mismo nombre que el global. El objeto local hace que el objeto global sea invisible, el local <italic>tapa</italic> al global</p>
<p>Las modificaciones similares a esta siempre generar&#x00E1;n un error<target target-type="page" id="pges_167"/></p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># ambito03.py</italic>
c=3
def f():
&#x00A0;&#x00A0;&#x00A0;c=c-1 &#x00A0;&#x00A0;&#x00A0;<italic># ERROR: la variable global ya no es visible y la</italic>
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;<italic># local &#x00E1;un no est&#x00E1; definida</italic>
&#x00A0;&#x00A0;&#x00A0;return
f()
</preformat></p></td></tr></tbody></table></table-wrap>
<p>En cuanto el int&#x00E9;rprete procesa el nombre del objeto a la izquierda de un signo igual, crea un objeto local que a&#x00FA;n no est&#x00E1; definido, pero que hace invisible al objeto global</p>
<p>Para poder modificar un objeto global, es necesario declararlo con la sentencia <code>global</code></p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># ambito04.py</italic>
c=3
def f():
&#x00A0;&#x00A0;&#x00A0;global c
&#x00A0;&#x00A0;&#x00A0;c=0 <italic># Esto modifica el objeto global</italic>
&#x00A0;&#x00A0;&#x00A0;print(c) <italic># 0</italic>
&#x00A0;&#x00A0;&#x00A0;return
f()
print(c) &#x00A0;&#x00A0;&#x00A0;<italic>#0</italic>
</preformat></p></td></tr></tbody></table></table-wrap>
<p>La sentencia global evita que al declarar un objeto en una funci&#x00F3;n, se cree un nuevo objeto con el mismo nombre pero de &#x00E1;mbito local. Por tanto permite modificar el objeto global</p>
<p>En muchos lenguajes, para hacer que una variable sea global, la declarar&#x00ED;amos <code>global</code> en la la <italic>zona global</italic> del c&#x00F3;digo, har&#x00ED;amos un c&#x00F3;digo similar a este, pero que en python es incorrecto</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># ambito05</italic>
global c &#x00A0;&#x00A0;&#x00A0;<italic>#ERROR, esto no sirve de nada</italic>
c=3
def f():
&#x00A0;&#x00A0;&#x00A0;c=0 &#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;<italic># Esto es un objeto local</italic>
&#x00A0;&#x00A0;&#x00A0;print(c) <italic># 0</italic>
&#x00A0;&#x00A0;&#x00A0;return
f()
print(c) &#x00A0;&#x00A0;&#x00A0;<italic>#3 el global no ha cambiado</italic>
</preformat></p></td></tr></tbody></table></table-wrap>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_168"/>Observa que en python se usa la sentencia global en la funci&#x00F3;n local que vaya a modificar el objeto</p></list-item>
<list-item><label>&#x25FE;</label> <p>Dicho de otro modo: la sentencia global no significa <italic>haz que este objeto sea global</italic>, sino <italic>haz que este objeto global pueda ser modificado aq&#x00FA;&#x0131;</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>Seguramente resultar&#x00ED;a m&#x00E1;s intutivo si la sentencia global tuviera un nombre distinto. Tal vez global-write o GlobalModify</p></list-item>
</list>
<p>Los objetos mutables (listas, diccionarios...) declarados dentro de una funci&#x00F3;n tambi&#x00E9;n son locales, en este aspecto se comportan igual que los objetos inmutables</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># ambito06.py</italic>
l= [&#x201C;uno&#x201D;,&#x201C;dos&#x201D;]
def f():
&#x00A0;&#x00A0;&#x00A0;l=[&#x201C;cuatro&#x201D;] <italic># nuevo objeto mutable, local</italic>
&#x00A0;&#x00A0;&#x00A0;print(l) <italic># [&#x0022;uno&#x201D;,&#x0022;dos&#x0022;]</italic>
&#x00A0;&#x00A0;&#x00A0;f()
print(l) &#x00A0;&#x00A0;&#x00A0;<italic># [&#x0022;uno&#x201D;,&#x0022;dos&#x0022;]</italic>
</preformat></p></td></tr></tbody></table></table-wrap>
<p>Hay una diferencia entre los objetos mutables y los inmutables. Como hemos visto</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Los objetos inmutables globales se pueden leer localmente</p></list-item>
<list-item><label>&#x25FE;</label> <p>Para poder modificar un objeto inmutable global, es necesario usar la sentencia <code>global</code></p>
<p>Por tanto, un objeto global sin la sentencia global queda <italic>protegido contra escritura</italic></p></list-item>
</list>
<p>Los objetos mutables globales no se pueden <italic>proteger contra escritura</italic> de esta manera</p>
<p>Un objeto mutable s&#x00ED; puede ser modificado en una funci&#x00F3;n local, a pesar de no estar declarado <code>global</code></p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># ambito07.py</italic>
l= [&#x201C;uno&#x201D;,&#x201C;dos&#x201D;]
<target target-type="page" id="pges_169"/>
def f():
&#x00A0;&#x00A0;&#x00A0;l.pop()
print(l) <italic># [&#x0022;uno&#x201D;,&#x0022;dos&#x0022;]</italic>
f()
print(l) <italic># [&#x0022;uno&#x0022;] . El objeto mutable fue modificado por la funci&#x00F3;n</italic>
</preformat></p></td></tr></tbody></table></table-wrap>
<p>El objeto mutable puede ser modificado a trav&#x00E9;s de sus m&#x00E9;todos. (No debo pensar que la ausencia de la sentencia global hace que el objeto est&#x00E9; en modo <italic>solo lectura</italic>)</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># ambito08.py</italic>
l= [&#x201C;uno&#x201D;,&#x201C;dos&#x201D;]
def f():
&#x00A0;&#x00A0;&#x00A0;l=[&#x201C;uno&#x201D;]
print(l) <italic># [&#x0022;uno&#x201D;,&#x0022;dos&#x0022;]</italic>
f()
print(l) <italic># [&#x0022;uno&#x201D;,&#x0022;dos&#x0022;] No cambia .</italic>
</preformat></p></td></tr></tbody></table></table-wrap>
<p>En el caso de que la modificaci&#x00F3;n se haga redefiniendo el objeto (no mediante m&#x00E9;todos), como ya sabemos, implica la declaraci&#x00F3;n impl&#x00ED;cita de un objeto nuevo, local, que oculta al objeto global. Por tanto, el objeto global no es modificado</p>
<p>Si al ejemplo anterior le a&#x00F1;adimos <code>global</code> de esta manera, como cabr&#x00ED;a esperar, permite modificar el objeto global</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># ambito09.py</italic>
l= [&#x201C;uno&#x201D;,&#x201C;dos&#x201D;]
def f():
&#x00A0;&#x00A0;&#x00A0;global l
&#x00A0;&#x00A0;&#x00A0;l=[&#x201C;uno&#x201D;]
print(l) <italic># [&#x0022;uno&#x201D;,&#x0022;dos&#x0022;]</italic>
f()
print(l) <italic># [&#x0022;uno&#x0022;] Ha cambiado.</italic>
</preformat></p></td></tr></tbody></table></table-wrap>
<p>Resumen:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Los objetos declarados fuera de una funci&#x00F3;n son globales</p></list-item>
<list-item><label>&#x25FE;</label> <p>Los objetos declarados dentro de una funci&#x00F3;n son locales</p></list-item>
<list-item><label>&#x25FE;</label> <p>Los objetos globales siempre se pueden leer dentro de una funci&#x00F3;n</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_170"/>Para modificar un objeto global dentro de una funci&#x00F3;n</p>
<list list-type="bullet">
<list-item><p>Si es inmutable, hay que usar global dentro de la funci&#x00F3;n</p></list-item>
<list-item><p>Si es mutable</p>
<list list-type="simple">
<list-item><label>&#x25E6;</label> <p>Para modificarlo mediante una asignaci&#x00F3;n, hay que usar <code>global</code></p></list-item>
<list-item><label>&#x25E6;</label> <p>Para modificarlo mediante sus m&#x00E9;todos, no es necesario usar <code>global</code></p></list-item>
</list></list-item>
</list></list-item>
</list>
<p>En las llamadas a funciones</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Los objetos inmutables se pasan por valor. La funci&#x00F3;n recibe una copia del valor, por lo que una posible modificaci&#x00F3;n de la copia no altera el original</p></list-item>
<list-item><label>&#x25FE;</label> <p>Los objetos mutables se pasan por referencia. La funci&#x00F3;n recibe una referencia al objeto original, una modificaci&#x00F3;n del objeto en la funci&#x00F3;n modifica el objeto original</p></list-item>
</list>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># valor_referencia.py</italic>
<italic>def f(x,y):</italic>
&#x00A0;&#x00A0;&#x00A0;x=x-1
&#x00A0;&#x00A0;&#x00A0;y.pop()
v=3

l=[&#x201C;uno&#x201D;,&#x201C;dos&#x201D;]
f(v,l)
print(v) <italic># 3 . La funci&#x00F3;n cre&#x00F3; copia local, no toc&#x00F3; el global</italic>
print(l) <italic># [&#x0027;uno&#x0027;] . La funci&#x00F3;n recibi&#x00F3; referencia al global</italic>
</preformat></p></td></tr></tbody></table></table-wrap>
</sec></sec>
<sec id="c11-s13">
<label><bold>11.13.</bold></label>
<title><bold>Cadenas de documentaci&#x00F3;n</bold></title>
<sec id="c11-s13-1">
<title><bold>Cadenas de documentaci&#x00F3;n</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>No son obligatorias pero s&#x00ED; muy recomendables (varias herramientas hacen uso de ellas).</p></list-item>
<list-item><label>&#x25FE;</label> <p>La cadena de documentaci&#x00F3;n de un objeto es su atributo <code>__doc__</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>En una sola l&#x00ED;nea para objetos sencillos, en varias para el resto de los casos.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Entre triples comillas-dobles (incluso si ocupan una l&#x00ED;nea).</p></list-item>
<list-item><label>&#x25FE;</label> <p>Si hay varias l&#x00ED;neas:</p>
<list list-type="bullet">
<list-item><p><target target-type="page" id="pges_171"/>La primera l&#x00ED;nea debe ser una resumen breve del prop&#x00F3;sito del objeto. Debe empezar con may&#x00FA;scula y acabar con un punto</p></list-item>
<list-item><p>Una l&#x00ED;nea en blanco debe separar la primera l&#x00ED;nea del resto</p></list-item>
<list-item><p>Las siguientes l&#x00ED;neas deber&#x00ED;an empezar justo debajo de la primera comilla doble de la primera l&#x00ED;nea</p></list-item>
</list></list-item>
</list>
<p>De una sola l&#x00ED;nea:</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
def kos_root():
&#x00A0;&#x00A0;&#x00A0;&#x201C;&#x0022;&#x0022;Return the pathname of the KOS root directory.&#x0022;&#x0022;&#x0022;
&#x00A0;&#x00A0;&#x00A0;global _kos_root
&#x00A0;&#x00A0;&#x00A0;...
</preformat></p></td></tr></tbody></table></table-wrap>
<p>De varias:</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
def complex(real=0.0, imag=0.0):
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x201C;&#x0022;&#x0022;Form a complex number.
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;Keyword arguments:
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;real -- the real part (default 0.0)
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;imag -- the imaginary part (default 0.0)
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x0022;&#x0022;&#x0022;
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;if imag == 0.0 and real == 0.0: return complex_zero
</preformat></p></td></tr></tbody></table></table-wrap>
</sec>
<sec id="c11-s13-2">
<title><bold>Documentando el c&#x00F3;digo (tipo Javadoc)</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Permite documentar el c&#x00F3;digo -generalmente las funcionesdentro del propio c&#x00F3;digo</p></list-item>
<list-item><label>&#x25FE;</label> <p>Genera la documentaci&#x00F3;n del c&#x00F3;digo en formatos legibles y navegables (HTML, PDF...)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Se basa en un lenguaje de marcado simple</p></list-item>
<list-item><label>&#x25FE;</label> <p>PERO... hay que mantener la documentaci&#x00F3;n al d&#x00ED;a cuando se cambia el c&#x00F3;digo</p></list-item>
</list>
<p>Ejemplo</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
def interseccion(m, b):
&#x0022;&#x0022;&#x0022;
&#x00A0;&#x00A0;&#x00A0;<italic>Devuelve la interseccion de la curva M{y=m*x+b} con el eje X.</italic>
&#x00A0;&#x00A0;&#x00A0;<italic>Se trata del punto en el que la curva cruza el eje X (M{y=0}).</italic>
<target target-type="page" id="pges_172"/>
&#x00A0;&#x00A0;&#x00A0;<italic>@type m: n&#x00FA;mero</italic>
&#x00A0;&#x00A0;&#x00A0;<italic>@param m: La pendiente de la curva</italic>
&#x00A0;&#x00A0;&#x00A0;<italic>@type b: n&#x00FA;mero</italic>
&#x00A0;&#x00A0;&#x00A0;<italic>@param b: La intersecci&#x00F3;n con el eje Y</italic>

&#x00A0;&#x00A0;&#x00A0;<italic>@rtype: n&#x00FA;mero</italic>
&#x00A0;&#x00A0;&#x00A0;<italic>@return: la intersecc&#x00ED;oin con el eje X de la curva M{y=m*x+b}.</italic>
&#x00A0;&#x00A0;&#x00A0;<italic>&#x0022;&#x0022;&#x0022;</italic>
&#x00A0;&#x00A0;&#x00A0;return -b/m
</preformat></p></td></tr></tbody></table></table-wrap>
</sec></sec>
<sec id="c11-s14">
<label><bold>11.14.</bold></label>
<title><bold>Ficheros</bold></title>
<sec id="c11-s14-1">
<title><bold>Ficheros</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>open(<code>nombre_fichero,modo</code>) devuelve un objeto fichero. modo:</p>
<list list-type="bullet">
<list-item><p><code>w:</code> Escritura. Destruye contenido anterior</p></list-item>
<list-item><p><code>r:</code> Lectura. Modo por defecto</p></list-item>
<list-item><p><code>r+:</code> Lectura y escritura</p></list-item>
<list-item><p><code>a:</code> Append</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p><code>write(cadena)</code> escribe la cadena en el fichero. Solo escribe cadenas, para otros tipos, es necesario pasar a texto o usar librer&#x00ED;as como <italic>json</italic> o <italic>pickle</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p><code>read()</code> devuelve una cadena con todo el contenido del fichero</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>readlines()</code> devuelve una lista donde cada elemento es una l&#x00ED;nea del fichero</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>close()</code> cierra el fichero</p></list-item>
</list>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># ficheros01.py</italic>
lista=[&#x0027;sota&#x0027;,&#x0027;caballo&#x0027;,&#x0027;rey&#x0027;]
fichero=open(&#x0027;prueba.txt&#x0027;,&#x0027;w&#x0027;)
for x in lista:
&#x00A0;&#x00A0;&#x00A0;fichero.write(x+&#x201D;\n&#x201D;)
fichero.close()

fichero=open(&#x0027;prueba.txt&#x0027;,&#x0027;r&#x0027;)
mi_cadena=fichero.read() &#x00A0;&#x00A0;&#x00A0;<italic># Una sola cadena de 3 l&#x00ED;neas</italic>
fichero.seek(0) &#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;<italic># vuelvo al princio del fichero</italic>
<target target-type="page" id="pges_173"/>
lista_de_cadenas=fichero.readlines() <italic># ahora cada elemento incluye \n</italic>
fichero.seek(0)

for linea in fichero.readlines():
&#x00A0;&#x00A0;&#x00A0;print(linea, end=&#x0027;&#x0027;)

fichero.close()
</preformat></p></td></tr></tbody></table></table-wrap>
<p>Los m&#x00E9;todos <italic>read()</italic> y <italic>readlines()</italic> crean una copia completa del fichero en memoria.</p>
<p>Para ficheros muy grandes es m&#x00E1;s eficiente trabajar l&#x00ED;nea a l&#x00ED;nea</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
fichero=open(&#x0027;prueba.txt&#x0027;,&#x0027;r&#x0027;)
for linea in fichero:
&#x00A0;&#x00A0;&#x00A0;print(linea, end=&#x0027;&#x0027;)
fichero.close()
</preformat></p></td></tr></tbody></table></table-wrap>
<p>No se deben mezclar estas dos maneras de acceder a un fichero</p>
</sec></sec>
<sec id="c11-s15">
<label><bold>11.15.</bold></label>
<title><bold>Excepciones</bold></title>
<sec id="c11-s15-1">
<title><bold>Excepciones</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Un programa sint&#x00E1;cticamente correcto puede dar errores de ejecuci&#x00F3;n</p></list-item>
</list>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># excepcion01.py</italic>
while 1:
<target target-type="page" id="pges_174"/>
&#x00A0;&#x00A0;&#x00A0;x=int(input(&#x201C;Introduce un n-&#x201D; ))
&#x00A0;&#x00A0;&#x00A0;print(x)
</preformat></p></td></tr></tbody></table></table-wrap>
<p>Si el usuario no escribe un n&#x00FA;mero sino por ejemplo <italic>a</italic></p>
<preformat>
File &#x0022;./excepcion01.py&#x0022;, line 4, in &#x003C;module&#x003E;
&#x00A0;&#x00A0;&#x00A0;x=int(input(&#x0022;Introduce un n&#x00B0; &#x0022;))
ValueError: invalid literal for int() with base 10: &#x0027;a&#x0027;
</preformat>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Definimos una acci&#x00F3;n para determinada excepci&#x00F3;n
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># excepcion02.py</italic>
while 1:
&#x00A0;&#x00A0;&#x00A0;try:
&#x00A0;&#x00A0;&#x00A0;x=int(input(&#x201C;Introduce un n&#x00B0;:&#x201D;))
&#x00A0;&#x00A0;&#x00A0;print(x)
except ValueError:
&#x00A0;&#x00A0;&#x00A0;print(&#x201C;N&#x00FA;mero incorrecto&#x201D;)
</preformat></p></td></tr></tbody></table></table-wrap></p></list-item>
<list-item><label>&#x25FE;</label> <p>Se puede indicar una acci&#x00F3;n que se ejecute sea cual sea la excepci&#x00F3;n pero es <italic>muy</italic> desaconsejable (enmascara otros errores)</p></list-item>
<list-item><label>&#x25FE;</label> <p>El programador puede levantar excepciones
<preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># excepcion03.py</italic>
try:
&#x00A0;&#x00A0;&#x00A0;x=int(input(&#x201C;Introduce un n&#x00B0;:&#x201D;))
&#x00A0;&#x00A0;&#x00A0;print(x)
except : <italic># para cualquier excepci&#x00F3;n</italic>
&#x00A0;&#x00A0;&#x00A0;raise Exception(&#x201C;N&#x00FA;mero incorrecto&#x201D;)
</preformat></p></list-item>
</list>
</sec></sec>
<sec id="c11-s16">
<label><bold>11.16.</bold></label>
<title><bold>Fechas</bold></title>
<sec id="c11-s16-1">
<title><bold>Fechas</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>En Unix, y por tanto en internet, es habitual expresar la fecha y la hora como el n&#x00FA;mero de segundos transcurridos desde el <italic>epoch</italic> (1 de enero de 1970 a las 00:00)</p></list-item>
<list-item><label>&#x25FE;</label> <p>En python, la librer&#x00ED;a time ofrece el m&#x00E9;todo time que acepta una fecha en segundos desde el epoch, y la devuelve en un struct_time</p>
<list list-type="bullet">
<list-item><p>Si invocamos a este m&#x00E9;todo sin argumentos, nos devuelve la fecha actual</p></list-item></list>
</list-item>
<list-item><label>&#x25FE;</label> <p>Para conocer la hora Unix de modificaci&#x00F3;n de un fichero
<preformat>
os.path.getmtime(&#x0022;/nombre/de/fichero&#x0022;)
</preformat></p></list-item>
</list>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># fechas.py</italic>
import time,os

t_unix = time.time() &#x00A0;&#x00A0;&#x00A0;&#x00A0;<italic># Segundos desde el epoch</italic>
t = time.gmtime(t_unix) &#x00A0;<italic># Fecha en un struct_time</italic>
<target target-type="page" id="pges_175"/>
print(t)
print(t.tm_year) print(t.tm_mon)
print(t.tm_mday)

print(os.path.getmtime(&#x201C;/etc/hosts&#x201D;))
</preformat></p></td></tr></tbody></table></table-wrap>
<p>Resultado:</p>
<preformat>
time.struct_time(tm_year=2021, tm_mon=11, tm_mday=24, tm_hour=16, tm_min=48, tm_sec=3, tm_wday=2, tm_yday=328, tm_isdst=0)
2021
11
24
1637418845.3172204
</preformat>
</sec></sec>
<sec id="c11-s17">
<label><bold>11.17.</bold></label>
<title><bold>Cadenas binarias</bold></title>
<sec id="c11-s17-1">
<title><bold>Cadenas binarias</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Hasta la d&#x00E9;cada de 1990, en pr&#x00E1;cticamente cualquier &#x00E1;mbito de la inform&#x00E1;tica, un car&#x00E1;cter equival&#x00ED;a a un byte. La codificaci&#x00F3;n de caracteres no ingleses era compleja, con distintas normas incompatibles</p></list-item>
<list-item><label>&#x25FE;</label> <p>En las d&#x00E9;cadas de 1990, 2000 se emplea la norma ISO 8859. Para los lenguajes de Europa Occidental, ISO 8859-1, tambi&#x00E9;n llamada <italic>latin 1</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>En la d&#x00E9;cada de 2000 se empieza a usar la norma Unicode, concretamente con la codificaci&#x00F3;n UTF-8</p>
<list list-type="bullet">
<list-item><p>Es la codificaci&#x00F3;n pr&#x00E1;cticamente universal en la actualidad, aunque no es raro encontrarse sistemas antiguos con normas anteriores</p></list-item>
<list-item><p>Permite representar cualquier lenguaje humano, incluyendo lenguajes extintos y lenguajes artificiales</p></list-item>
<list-item><p>UTF-8 es un superconjunto de ASCII, para cada caracter emplea entre 1 y 4 bytes</p></list-item>
</list></list-item>
</list>
<p>En python 3, todas las cadenas son unicode UTF-8 por omisi&#x00F3;n</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Si es necesario, tambi&#x00E9;n se pueden usar cadenas <italic>al estilo antiguo</italic>, llamadas tipo bytes<xref ref-type="fn" rid="FN9"><sup>9</sup></xref></p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_176"/>Podemos declarar una cadena de tipo bytes anteponiendo b
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
b&#x0022;Esto es una cadena de tipo byte&#x201D;
</preformat></p></td></tr></tbody></table></table-wrap></p></list-item>
<list-item><label>&#x25FE;</label> <p>Para detectar el tipo de una cadena usamos la funci&#x00F3;n <italic>type</italic>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
print(type(&#x0027;holamundo&#x0027;)) <italic>&#x00A0;#&#x00A0; &#x003C;class &#x0027;str&#x0027;&#x003E;</italic>
print(type(b&#x0027;holamundo&#x0027;)) <italic>&#x00A0;#&#x00A0; &#x003C;class &#x0027;bytes&#x0027;&#x003E;</italic></preformat></p></td></tr></tbody></table></table-wrap></p></list-item>
</list>
<p>Conversi&#x00F3;n entre cadenas de 8 bits y cadenas unicode en python 3</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>De string (unicode) a byte (8 bits)
<preformat>
&#x003E;&#x003E;&#x003E; &#x0022;espa&#x00F1;a&#x0022;.encode(&#x0027;utf-8&#x0027;)
b&#x0027;espa\xc3\xb1a&#x0027;
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>De bytes (8 bits) a string (unicode)
<preformat>
&#x003E;&#x003E;&#x003E; b=b&#x0027;123&#x0027;
&#x003E;&#x003E;&#x003E; print(b)
b&#x0027;123&#x0027;
&#x003E;&#x003E;&#x003E; s=b.decode()
&#x003E;&#x003E;&#x003E; print(s)
123
</preformat></p></list-item>
</list>
</sec></sec>
</body>
<back>
<fn-group>
<fn id="FN8"><p><sup>8</sup> En python 2 era necesario a&#x00F1;adir la opci&#x00F3;n -tt para que el int&#x00E9;rprete detectase este problema</p></fn>
<fn id="FN9"><p><sup>9</sup> Al contrario que en python 2, donde por omisi&#x00F3;n era de tipo byte aunque tambi&#x00E9;n exist&#x00ED;a el tipo unicode</p></fn>
</fn-group>
</back>
</book-part>
<book-part id="c12" book-part-type="chapter">
<book-part-meta>
<book-part-id book-part-id-type="publisher-id">URJC</book-part-id>
<title-group>
<label>12.</label>
<title><target target-type="page" id="pges_177"/>LIBRER&#x00CD;AS DE PYTHON</title>
</title-group>
</book-part-meta>
<body>
<sec id="c12-s1">
<label><bold>12.1.</bold></label>
<title><bold>Librer&#x00ED;a sys</bold></title>
<sec id="c12-s1-1">
<title><bold>libreria sys</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Argumentos de linea de &#x00F3;rdenes</p></list-item>
</list>
<p><code>sys.argv</code> devuelve una lista con los argumentos pasados al script python desde la shell</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># argumentos.py</italic>
import sys
print(sys.argv[:])
</preformat></p></td></tr></tbody></table></table-wrap>
<preformat>
koji@mazinger:~$ ./argumentos.py un_argumento otro_argumento
[&#x0027;./argumentos.py&#x0027;, &#x0027;un_argumento&#x0027;, &#x0027;otro_argumento&#x0027;]
</preformat>
<p>(El argumento cero es el nombre del programa)</p>
<p>Escribir en stderr</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># error_estandar.py</italic>
import sys
sys.stderr.write(&#x0027;Error: \n&#x0027;)
</preformat></p></td></tr></tbody></table></table-wrap>
<p>Leer desde stdin, escribir en stdout</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># cat.py</italic>
import sys
for linea in sys.stdin.readlines():
&#x00A0;&#x00A0;&#x00A0;sys.stdout.write(linea)
</preformat></p></td></tr></tbody></table></table-wrap>
</sec></sec>
<sec id="c12-s2">
<label><bold>12.2.</bold></label>
<title><bold>Librer&#x00ED;a subprocess</bold></title>
<sec id="c12-s2-1">
<title><bold>subprocess</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>subprocess.check_output()</code> permite ejecutar una orden de la shell en un subproceso externo</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_178"/>Aunque puede ser muy &#x00FA;til, el script deja de ser portable entre sistemas operativos diferentes</p></list-item>
<list-item><label>&#x25FE;</label> <p>Su primer argumento es una lista con los argumentos de la orden a ejecutar</p></list-item>
<list-item><label>&#x25FE;</label> <p>Devuelve la salida est&#x00E1;ndar del subproceso pero como bytes que habr&#x00E1; que convertir a cadena (unicode) con decode</p>
<list list-type="bullet">
<list-item><p>En caso contrario obtendremos un error
<preformat>
TypeError: a bytes-like object is required, not &#x0027;str&#x0027;
</preformat></p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Si se produce alg&#x00FA;n error, eleva la excepci&#x00F3;n <code>subprocess.CalledProcessError</code></p></list-item>
</list>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># subproceso01.py</italic>
import subprocess,sys
<target target-type="page" id="pges_179"/>
mandato=&#x201C;ps -ef&#x201D;
mandato_troceado=mandato.split()
try:
&#x00A0;&#x00A0;&#x00A0;salida=subprocess.check_output(mandato_troceado)
except subprocess.CalledProcessError:
&#x00A0;&#x00A0;&#x00A0;sys.stderr.write(&#x201C;La orden ha producido un error\n&#x201D;)
&#x00A0;&#x00A0;&#x00A0;raise SystemExit
salida=salida.decode(&#x201C;utf-8&#x201D;) &#x00A0;&#x00A0;&#x00A0;<italic># De bytes a string</italic>
lineas=salida.split(&#x201D;\n&#x201D;) &#x00A0;&#x00A0;&#x00A0;<italic># troceamos la salida l&#x00ED;nea a l&#x00ED;nea</italic>
lineas.pop(0) &#x00A0;&#x00A0;&#x00A0;<italic># quitamos la primera l&#x00ED;nea, la cabecera del ps</italic>
for linea in lineas:
&#x00A0;&#x00A0;&#x00A0;if len(linea) != 0:
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;campos_linea=linea.split()
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;usuario = campos_linea[0]
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;proceso = campos_linea[7]
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;print(&#x0022;Usuario:{} Proceso:{}&#x201C;.format(usuario,proceso))
</preformat></p></td></tr></tbody></table></table-wrap>
<p>Si necesitamos m&#x00E1;s control sobre los posibles errores</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Para redirigir la salida de error del subproceso a la salida est&#x00E1;ndar, pasamos el par&#x00E1;metro <code>stderr=subprocess.STDOUT</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>El atributo <code>returncode</code> de <code>CalledProcessError</code> contiene el c&#x00F3;digo del error</p></list-item>
</list>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># subproceso02.py</italic>
import subprocess,sys
mandato=&#x201C;ls inexistente&#x201D;
mandato_troceado=mandato.split()
try:
&#x00A0;&#x00A0;&#x00A0;salida=subprocess.check_output(mandato_troceado,
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
&#x00A0;&#x00A0;&#x00A0;plantilla = &#x201D;{}C&#x00F3;digo:{}\n&#x201D;
&#x00A0;&#x00A0;&#x00A0;msj_subproceso = e.output
&#x00A0;&#x00A0;&#x00A0;msj_subproceso = msj_subproceso.decode() <italic># De bytes a string</italic>
&#x00A0;&#x00A0;&#x00A0;codigo_subproceso = e.returncode
&#x00A0;&#x00A0;&#x00A0;msj = plantilla.format(msj_subproceso, codigo_subproceso)
&#x00A0;&#x00A0;&#x00A0;sys.stderr.write(msj)
</preformat></p></td></tr></tbody></table></table-wrap>
</sec></sec>
<sec id="c12-s3">
<label><bold>12.3.</bold></label>
<title><bold>Librer&#x00ED;as os, shutil</bold></title>
<sec id="c12-s3-1">
<title><bold>os.path</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Las funciones os.path.join() y os.path.split() unen y separan nombres de fichero con directorios</p>
<list list-type="bullet">
<list-item><p>Son compatibles con cualquier S.O.</p></list-item>
<list-item><p>No importa si el path acaba en barra o no</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p><code>os.path.exists()</code> devuelve un boolean indicando si un fichero existe</p></list-item>
</list>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3 # path01.py</italic>
<italic># path01.py</italic>
import os
ejemplo=os.path.join(&#x201C;/etc/apt&#x201D;,&#x201C;sources.list&#x201D;)
print(ejemplo) &#x00A0;&#x00A0;&#x00A0;<italic># /etc/apt/sources.list</italic>
print(os.path.split(ejemplo)) &#x00A0;&#x00A0;&#x00A0;<italic># (&#x0027;/etc/apt&#x0027;, &#x0027;sources.list&#x0027;)</italic>

print(os.path.exists(ejemplo)) &#x00A0;&#x00A0;&#x00A0;<italic># True</italic>
print(os.path.exists(&#x201C;/usr/local/noexiste&#x201D;)) &#x00A0;&#x00A0;&#x00A0;<italic># False</italic>
</preformat></p></td></tr></tbody></table></table-wrap>
</sec>
<sec id="c12-s3-2">
<title><bold>Enlazar, borrar</bold></title>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python</italic>
<italic># enlazar_borrar.py</italic>
import os
if not os.path.exists(&#x0022;/tmp/test&#x0022;):
&#x00A0;&#x00A0;&#x00A0;os.mkdir(&#x0022;/tmp/test&#x0022;)
<target target-type="page" id="pges_180"/>
os.chdir(&#x0022;/tmp/test&#x0022;) &#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;<italic># cd /tmp/aa</italic>
os.symlink(&#x0022;/etc/hosts&#x0022;,&#x0022;enlace_hosts&#x0022;) <italic># crea enlace simb&#x00F3;lico</italic>
os.remove(&#x0022;enlace_hosts&#x0022;) &#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;<italic># borra el fichero</italic>
os.rmdir(&#x0022;/tmp/test&#x0022;) &#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;<italic># borra directorio (vacio)</italic>
</preformat></p></td></tr></tbody></table></table-wrap>
</sec>
<sec id="c12-s3-3">
<title><bold>copiar, copiar y borrar recursivamente</bold></title>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># recursivo.py</italic>
import shutil,os
shutil.copytree(&#x201C;/home/koji/.gnome&#x201D;,&#x201C;/tmp/probando&#x201D;)
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;# copia recursivamente. El destino no debe existir
shutil.copy(&#x201C;/etc/hosts&#x201D;,&#x201C;/tmp/probando&#x201D;)
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;# copia 1 fichero (como el cp de bash)
shutil.move(&#x201C;/tmp/probando/hosts&#x201D;,&#x201C;/tmp/probando/mi_hosts&#x201D;)
shutil.rmtree(&#x201C;/tmp/probando&#x201D;) <italic># borra directorio lleno</italic>
</preformat></p></td></tr></tbody></table></table-wrap>
</sec>
<sec id="c12-s3-4">
<title><bold>os.walk</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Recorre recursivamente un directorio</p></list-item>
<list-item><label>&#x25FE;</label> <p>Por cada directorio devuelve una 3-tupla</p>
<list list-type="bullet">
<list-item><p>Directorio</p></list-item>
<list-item><p>Subdirectorios</p></list-item>
<list-item><p>Ficheros</p></list-item>
</list></list-item>
</list>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># walk.py</italic>
import os
directorio_inicial=os.getcwd() <italic># current working directory</italic>
os.chdir(&#x201C;/tmp/musica&#x201D;) &#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;<italic># cd</italic>

for x in os.walk(&#x201D;.&#x201D;):
print(x)

os.chdir(directorio_inicial)
</preformat><target target-type="page" id="pges_181"/>
</p></td></tr></tbody></table></table-wrap>
<preformat>
/tmp/musica
|-- listado.txt
|-- jazz
&#x0060;-- pop
&#x00A0;&#x00A0;&#x00A0;&#x00A0;|-- sabina
&#x00A0;&#x00A0;&#x00A0;&#x00A0;| |-- pirata_cojo.mp3
&#x00A0;&#x00A0;&#x00A0;&#x00A0;| &#x0060;-- princesa.mp3
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x0060;-- serrat
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;|-- curro_el_palmo.mp3
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x0060;-- penelope.mp3
(&#x0027;.&#x0027;, [&#x0027;jazz&#x0027;, &#x0027;pop&#x0027;], [&#x0027;listado.txt&#x0027;])
(&#x0027;./jazz&#x0027;, [], [])
(&#x0027;./pop&#x0027;, [&#x0027;serrat&#x0027;, &#x0027;sabina&#x0027;], [])
(&#x0027;./pop/serrat&#x0027;, [], [&#x0027;curro_el_palmo.mp3&#x0027;, &#x0027;penelope.mp3&#x0027;])
(&#x0027;./pop/sabina&#x0027;, [], [&#x0027;princesa.mp3&#x0027;, &#x0027;pirata_cojo.mp3&#x0027;])
</preformat>
</sec></sec>
<sec id="c12-s4">
<label><bold>12.4.</bold></label>
<title><bold>Librer&#x00ED;as pickle: Persistencia</bold></title>
<sec id="c12-s4-1">
<title><bold>Persistencia</bold></title>
<p>Persistencia en Python:</p>
<p>Una librer&#x00ED;a de persistencia permite <italic>serializar objetos</italic>, esto es, convertirlos en una secuencia binaria o de texto que se pueda transmitir fuera de python, almacenar en disco, base de datos, etc</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Librer&#x00ED;a <italic>Pickle</italic></p>
<list list-type="bullet">
<list-item><p>Formato propio de Python, binario, muy eficiente</p></list-item>
<list-item><p>Soporta cualquire clase, predefinida en Python o por el usuario</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Librer&#x00ED;a <italic>json</italic></p>
<list list-type="bullet">
<list-item><p>Formato est&#x00E1;ndar de internet, modo texto, legible</p></list-item>
<list-item><p>Solo soporta cadenas, n&#x00FA;meros, booleanos, diccionarios y listas</p></list-item>
</list></list-item>
</list>
</sec>
<sec id="c12-s4-2">
<title><target target-type="page" id="pges_182"/><bold>Persistencia con pickle</bold></title>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># conserva.py</italic>
import pickle

cp={28:&#x0027;madrid&#x0027;,8:&#x0027;barcelona&#x0027;,33:&#x0027;asturias&#x0027;}
fich=open(&#x0027;prueba.pick&#x0027;,&#x0027;wb&#x0027;) &#x00A0;&#x00A0;&#x00A0;<italic># Apertura modo binario</italic>
pickle.dump(cp,fich)
fich.close()

fich=open(&#x0027;prueba.pick&#x0027;,&#x0027;rb&#x0027;) &#x00A0;&#x00A0;&#x00A0;<italic># Lectura modo binario</italic>
codigos_postales=pickle.load(fich)
fich.close()

for x in codigos_postales.keys():
&#x00A0;&#x00A0;&#x00A0;print(x,codigos_postales[x])
</preformat></p></td></tr></tbody></table></table-wrap>
</sec>
<sec id="c12-s4-3">
<title><bold>Persistencia con json</bold></title>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># ejemplo_json.py</italic>
import json

<italic># Objeto python ordinario</italic>
diccionario = { &#x0027;MAD&#x0027;:&#x0027;Madrid Barajas&#x0027;, &#x0027;MCV&#x0027;:&#x0027;Madrid Cuatro Vientos&#x0027;}
print(type(diccionario)) &#x00A0;&#x00A0;&#x00A0;<italic># &#x003C;class &#x0027;dict&#x0027;&#x003E;</italic>

<italic># Lo convertimos en cadena json</italic>
cadena_json = json.dumps(diccionario)
print(type(cadena_json)) &#x00A0;&#x00A0;&#x00A0;<italic># &#x003C;class &#x0027;str&#x0027;&#x003E;</italic>
print(cadena_json)
<italic># {&#x0022;MAD&#x0022;: &#x201C;Madrid Barajas&#x201D;, &#x201C;MCV&#x0022;: &#x201C;Madrid Cuatro Vientos&#x0022;}</italic>

<italic># Volvemos a convertir la cadena en objeto python</italic>
diccionario = json.loads(cadena_json)
print(type(diccionario)) &#x00A0;&#x00A0;&#x00A0;<italic># &#x003C;class &#x0027;dict&#x0027;&#x003E;</italic>
</preformat></p></td></tr></tbody></table></table-wrap>
</sec></sec>
<sec id="c12-s5">
<label><bold>12.5.</bold></label>
<title><target target-type="page" id="pges_183"/><bold>Acceso a las variables de entorno</bold></title>
<sec id="c12-s5-1">
<title><bold>Variables de entorno</bold></title>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># entorno.py</italic>
import os, sys
mi_variable=os.getenv(&#x201C;MI_VARIABLE&#x201D;)
if mi_variable==None:
&#x00A0;&#x00A0;&#x00A0;msg=&#x201C;ERROR: variable de entorno MI_VARIABLE no definida&#x201D;
&#x00A0;&#x00A0;&#x00A0;sys.stderr.write(msg+&#x0027;\n&#x0027;)
&#x00A0;&#x00A0;&#x00A0;raise SystemExit
</preformat></p></td></tr></tbody></table></table-wrap>
<p>Atenci&#x00F3;n: Cuando la shell crea un proceso (p.e. el int&#x00E9;rprete de python), puede no pasarle todas las variables de entorno. Por tanto, las variables visibles desde la shell ser&#x00E1;n distintas a las visibles desde python</p>
</sec></sec>
<sec id="c12-s6">
<label><bold>12.6.</bold></label>
<title><bold>M&#x00F3;dulos</bold></title>
<sec id="c12-s6-1">
<title><bold>M&#x00F3;dulos</bold></title>
<p>Un m&#x00F3;dulo es un fichero que contiene definiciones y sentencias, que pueden ser usados desde otro fichero</p>
<preformat>
mi_modulo.py
</preformat>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
a=3
def f(x):
&#x00A0;&#x00A0;&#x00A0;return x+1
</preformat></p></td></tr></tbody></table></table-wrap>
<preformat>
test.py
</preformat>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
import mi_modulo

print(mi_modulo.a) &#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;<italic># 3</italic>
print(mi_modulo.f(0)) &#x00A0;&#x00A0;<italic># 1</italic>
</preformat></p></td></tr></tbody></table></table-wrap>
<p>Tambi&#x00E9;n se pueden importar los objetos por separado, de forma que luego se puede usar sin indicar expl&#x00ED;citamente el m&#x00F3;dulo<target target-type="page" id="pges_184"/></p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
from mi_modulo
import f from mi_modulo import a

print(f(0)) &#x00A0;&#x00A0;&#x00A0;<italic># 1</italic>
print(a) &#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;<italic># 3</italic>
</preformat></p></td></tr></tbody></table></table-wrap>
<p>Es posible importar todos los objetos de un m&#x00F3;dulo</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
from mi_modulo import *
print(f(0)) &#x00A0;&#x00A0;&#x00A0;<italic># 1</italic>
print(a) &#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;<italic># 3</italic>
</preformat></p></td></tr></tbody></table></table-wrap>
<p>Pero esto es una mala pr&#x00E1;ctica, porque cuando el n&#x00FA;mero de m&#x00F3;dulos aumenta, es dif&#x00ED;cil saber en qu&#x00E9; m&#x00F3;dulo est&#x00E1; cada objeto</p>
</sec>
<sec id="c12-s6-2">
<title><bold>B&#x00FA;squeda de los m&#x00F3;dulos</bold></title>
<p>El int&#x00E9;rprete busca los m&#x00F3;dulos en el siguiente orden</p>
<list list-type="order">
<list-item><p>En el directorio del script</p></list-item>
<list-item><p>En cada directorio indicado en la variable de entorno PYTHONPATH</p></list-item>
<list-item><p>En el directorio por omisi&#x00F3;n</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>En Unix y Linux suele estar en /usr/lib</p>
<p>Por ejemplo</p>
<p><code>/usr/lib/python3.9</code></p></list-item>
</list></list-item>
</list>
</sec>
<sec id="c12-s6-3">
<title><bold>Ficheros .pyc</bold></title>
<p>Cuando se importa un m&#x00F3;dulo, si el int&#x00E9;rprete tiene permisos, guarda en el mismo directorio un fichero con extensi&#x00F3;n .pyc que contiene el script compilado en bytecodes</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Este fichero ahorra tiempo la segunda vez que se ejecuta el m&#x00F3;dulo</p></list-item>
<list-item><label>&#x25FE;</label> <p>No es dependiente de la arquitectura pero s&#x00ED; de la versi&#x00F3;n exacta del int&#x00E9;rprete. Si no existe o no es adecuado, se genera uno nuevo autom&#x00E1;ticamente</p></list-item>
<list-item><label>&#x25FE;</label> <p>Permite borrar el fuente .py si no queremos distribuirlo</p></list-item>
</list>
</sec>
<sec id="c12-s6-4">
<title><target target-type="page" id="pges_185"/><bold>Objetos en m&#x00F3;dulos</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Usar objetos globales es peligroso, muchas metodolog&#x00ED;as lo proh&#x00ED;ben</p></list-item>
<list-item><label>&#x25FE;</label> <p>Pero usar alg&#x00FA;n objeto global, en un m&#x00F3;dulo compartido por otros m&#x00F3;dulos, en algunas ocasiones puede ser una pr&#x00E1;ctica aceptable y conveniente</p></list-item>
</list>
<preformat>
mis_globales.py
</preformat>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
a=3
</preformat></p></td></tr></tbody></table></table-wrap>
<preformat>
modulo1.py
</preformat>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
import mis_globales
def f():
&#x00A0;&#x00A0;&#x00A0;return mis_globales.a
</preformat></p></td></tr></tbody></table></table-wrap>
<preformat>
test.py
</preformat>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
import mis_globales, modulo1
print(modulo1.f()) <italic>#3</italic>
mis_globales.a=5
print(modulo1.f()) <italic>#5</italic>
</preformat></p></td></tr></tbody></table></table-wrap>
<p>Un fichero puede ser un script y un m&#x00F3;dulo simult&#x00E1;neamente, si a&#x00F1;adimos una funci&#x00F3;n main() y la sentencia</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
if name == &#x201C; main &#x201C;:
&#x00A0;&#x00A0;&#x00A0;main()
</preformat></p></td></tr></tbody></table></table-wrap>
<p>De esta manera,</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Si el fichero se ejecuta como un script, el int&#x00E9;rprete ejecutar&#x00E1; la funci&#x00F3;n main()</p></list-item>
<list-item><label>&#x25FE;</label> <p>Si el fichero se usa como m&#x00F3;dulo, importando sus funciones desde otro script, la funci&#x00F3;n main() no ser&#x00E1; ejecutada<target target-type="page" id="pges_186"/></p></list-item>
</list>
<preformat>
modulo1.py
</preformat>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
def f(x):
&#x00A0;&#x00A0;&#x00A0;return x+1

def main():
&#x00A0;&#x00A0;&#x00A0;print(&#x201C;probando f&#x201D;, f(2))

if name == &#x201C; main &#x201C;:
&#x00A0;&#x00A0;&#x00A0;main()
</preformat></p></td></tr></tbody></table></table-wrap>
<preformat>
test.py
</preformat>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
import modulo1
print(modulo1.f(0)) <italic>#1 No se ejecuta main()</italic>
</preformat></p></td></tr></tbody></table></table-wrap>
</sec></sec>
<sec id="c12-s7">
<label><bold>12.7.</bold></label>
<title><bold>optparse</bold></title>
<sec id="c12-s7-1">
<title><bold>optparse</bold></title>
<p>optparse es una librer&#x00ED;a de python para procesar las opciones y argumentos con los que se llama a un script</p>
<preformat>
orden&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;opciones&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;argumentos
-------------------------------------------------
cp&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;-r -v&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;directorio1&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;directorio2
</preformat>
<p>En un buen interfaz</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Las opciones deben ser opcionales. (El programa debe hacer algo &#x00FA;til sin ninguna opci&#x00F3;n)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Las opciones proporcionan flexibilidad, pero demasiadas introducen complejidad</p></list-item>
<list-item><label>&#x25FE;</label> <p>Los par&#x00E1;metros fundamentales e imprescindibles deben ser argumentos</p></list-item>
<list-item><label>&#x25FE;</label> <p>Creamos una instancia de la clase <code>OptionParser</code>, pasando como argumento la cadena <code>usage</code> (que se mostrar&#x00E1; al usuario cuando use mal el script, o cuando lo llame con <code>-h</code> o <code>--help</code><target target-type="page" id="pges_187"/>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
usage = &#x201C;Uso: %prog [opciones] origen destino&#x201D;
parser = OptionParser(usage)
</preformat></p></td></tr></tbody></table></table-wrap></p></list-item>
<list-item><label>&#x25FE;</label> <p>Para a&#x00F1;adir opciones invocamos al m&#x00E9;todo <code>add_option</code>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
parser.add_option(&#x201C;-v&#x201D;, &#x201C;--verbose&#x201D;,
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;action=&#x201C;store_true&#x201D;, dest=&#x201C;verbose&#x201D;,
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;help=&#x201C;Informe detallado&#x201D;)
</preformat></p></td></tr></tbody></table></table-wrap></p></list-item>
<list-item><label>&#x25FE;</label> <p>Invocamos a <code>parse_args()</code>, que devuelve las opciones ya procesadas y los argumentos
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
opciones, argumentos = parser.parse_args()
</preformat></p></td></tr></tbody></table></table-wrap></p></list-item>
</list>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># opciones01.py</italic>
import sys
from optparse import OptionParser
def main():
&#x00A0;&#x00A0;&#x00A0;usage = &#x201C;%prog [opciones] origen destino&#x201D;
&#x00A0;&#x00A0;&#x00A0;parser = OptionParser(usage)
&#x00A0;&#x00A0;&#x00A0;parser.add_option(&#x201C;-e&#x201D;, &#x201C;--energy&#x201D;,
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;action=&#x201C;store&#x201D;, dest=&#x201C;energy&#x201D;,
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;help=&#x201C;Tipo de energia a usar en la copia &#x201C;,
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;default=&#x0027;eolic&#x0027;)
&#x00A0;&#x00A0;&#x00A0;parser.add_option(&#x201C;-v&#x201D;, &#x201C;--verbose&#x201D;,
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;action=&#x201C;store_true&#x201D;, dest=&#x201C;verbose&#x201D;,
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;help=&#x201C;Informe detallado&#x201D;)
&#x00A0;&#x00A0;&#x00A0;parser.add_option(&#x201C;-q&#x201D;, &#x201C;--quiet&#x201D;,
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;action=&#x201C;store_false&#x201D;, dest=&#x201C;verbose&#x201D;,
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;help=&#x201C;Informe silencioso&#x201D;)
&#x00A0;&#x00A0;&#x00A0;opciones, argumentos = parser.parse_args()
</preformat></p></td></tr></tbody></table></table-wrap>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
&#x00A0;&#x00A0;&#x00A0;if len(argumentos) != 2:
&#x00A0;&#x00A0;&#x00A0;&#x00A0;parser.error(&#x201C;N&#x00FA;mero incorrecto de argumentos&#x201D;)
&#x00A0;&#x00A0;&#x00A0;print(&#x201C;Tipo de energia:&#x201D;+opciones.energy)
&#x00A0;&#x00A0;&#x00A0;print(&#x201C;Origen:&#x201D;,argumentos[0])
&#x00A0;&#x00A0;&#x00A0;print(&#x201C;Destino:&#x201D;,argumentos[1])
&#x00A0;&#x00A0;&#x00A0;if opciones.verbose:
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;print(&#x201C;mucho blablabla &#x201C;)
&#x00A0;&#x00A0;&#x00A0;if __name__ == &#x201C; __main__ &#x201C;:
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;main()
</preformat></p></td></tr></tbody></table></table-wrap>
</sec>
<sec id="c12-s7-2">
<title><target target-type="page" id="pges_188"/><bold>add_option</bold></title>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
parser.add_option(&#x201C;-e&#x201D;, &#x201C;--energy&#x201D;,
&#x00A0;&#x00A0;&#x00A0;action=&#x201C;store&#x201D;, dest=&#x201C;energy&#x201D;,
&#x00A0;&#x00A0;&#x00A0;help=&#x201C;Tipo de energia a usar en la copia &#x201C;, default=&#x0027;eolic&#x0027;)
</preformat></p></td></tr></tbody></table></table-wrap>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Cada opci&#x00F3;n puede invocarse con una &#x00FA;nica letra (p.e. <code>-v</code>) o con una palabra (p.e. <code>--verbose</code>)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Con el atributo <italic>help</italic> se construye el mensaje que se mostrar&#x00E1; al usuario cuando invoque el programa con <code>-h</code> o <code>--help</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>La opci&#x00F3;n puede</p>
<list list-type="bullet">
<list-item><p>Limitarse a activar o desactivar un flag.
<preformat>
action=&#x201C;store_true&#x201D; action=&#x201C;store_false&#x201D;
</preformat></p></list-item>
</list></list-item>
<list-item><p>Indicar un valor
<preformat>
action=&#x201C;store&#x201D;
</preformat></p>
<p>En ambos casos, la informaci&#x00F3;n se almacena en un atributo que se llama como indique el par&#x00E1;metro dest
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
parser.add_option(&#x201C;-d&#x201D;, &#x201C;--discount&#x201D;,
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;action=&#x201C;store&#x201D;, dest=&#x201C;discount&#x201D;, type=&#x201C;float&#x201D;,
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;help=&#x201C;Coeficiente de descuento&#x201D;)
</preformat></p></td></tr></tbody></table></table-wrap></p></list-item>
<list-item><label>&#x25FE;</label> <p>Por omisi&#x00F3;n el tipo de la opci&#x00F3;n es un string, pero tambi&#x00E9;n acepta
<preformat>
string, int, long, choice, float y complex
</preformat></p></list-item>
</list>
<preformat>
koji@mazinger:~/python$ ./cp_ecologico.py
Usage: cp_ecologico.py [opciones] origen destino cp_ecologico.py: error: N&#x00FA;mero incorrecto de argumentos
koji@mazinger:<sup>~</sup>/python$ ./cp_ecologico.py -h
Usage: cp_ecologico.py [opciones] origen destino
Options:
-h, --help show this help message and exit
-e ENERGY, --energy=ENERGY
Tipo de energia a usar en la copia
-v, --verbose Informe detallado
-q, --quiet Informe silencioso
<target target-type="page" id="pges_189"/>-d DISCOUNT, --discount=DISCOUNT
Coeficiente de descuento
koji@mazinger:<sup>~</sup>/python$ ./cp_ecologico.py -v -d 0.15 mi_origen mi_destino
Tipo de energia:eolic
Coeficiente de descuento:0.15
Origen: mi_origen
Destino: mi_destino
mucho blablabla
</preformat>
</sec></sec>
<sec id="c12-s8">
<label><bold>12.8.</bold></label>
<title><bold>Bots de telegram</bold></title>
<sec id="c12-s8-1">
<title><bold>Bots de telegram</bold></title>
<p>Telegram es una aplicaci&#x00F3;n de mensajer&#x00ED;a instant&#x00E1;nea muy similar a WhatsApp, con la que tiene las siguientes diferencias:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Telegram es muy popular pero no tiene tantos usuarios como WhatsApp</p></list-item>
<list-item><label>&#x25FE;</label> <p>Telegram no solo tiene clientes para iOS y Android (como WhatsApp) , tambi&#x00E9;n tiene clientes independientes del telefono para Windows, Linux y macOS</p></list-item>
<list-item><label>&#x25FE;</label> <p>El cliente es software libre (el servidor,no)</p></list-item>
<list-item><label>&#x25FE;</label> <p>F&#x00E1;cilmente accesible mediante API</p></list-item>
</list>
<p>En python hay muchas librer&#x00ED;as para manejar telegram, aq&#x00FA;&#x0131; veremos <italic>telepot</italic></p>
</sec>
<sec id="c12-s8-s1">
<label><bold>12.8.1.</bold></label>
<title><bold>Creaci&#x00F3;n de un bot de telegram</bold></title>
<p>Para poder enviar y recibir mensajes, hay que crear un tipo de usuario especial llamado <italic>bot</italic></p>
<p>Para crear un bot, tenemos que usar otro bot, llamado <italic>BotFather</italic></p>
<list list-type="order">
<list-item><p>Desde nuestro cliente de telegram, enviamos un mensaje con el texto <code>/newbot</code> al usuario <code>@BotFather</code></p></list-item>
<list-item><p>Un di&#x00E1;logo nos ir&#x00E1; preguntando todo lo necesario</p></list-item>
<list-item><p>Recibiremos un <italic>token</italic> que nos permitir&#x00E1; maneja el bot via API</p></list-item>
</list>
<p><target target-type="page" id="pges_190"/>De la misma forma, @BotFather nos permite cambiar el nombre de nuestro bot (comando /newbot), su foto (/setuserpic), eliminarlo (deletebot), etc</p>
<p>Para que un bot de telegram pueda enviar un mensaje a un usuario, hace falta:</p>
<list list-type="order">
<list-item><p>Que el usuario le env&#x00ED;e al bot un mensaje cualquiera (un mensaje en la vida es suficiente)</p></list-item>
<list-item><p>Que el usuario facilite al programador del bot su propio <code>id</code> de usuario</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Es un n&#x00FA;mero de unos 9 d&#x00ED;gitos, no confundir con el nombre de usuario (p.e. @JuanPerez)</p></list-item>
<list-item><label>&#x25FE;</label> <p>El usuario puede averiguar su propio id enviando un mensaje cualquiera al bot @userinfobot</p></list-item>
</list></list-item>
</list>
</sec>
<sec id="c12-s8-s2">
<label><bold>12.8.2.</bold></label>
<title><bold>Uso de la librer&#x00ED;a telepot</bold></title>
<sec id="c12-s8-s2-1">
<title><bold>Uso de la librer&#x00ED;a telepot</bold></title>
<p>Para instalar telepot</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Si tenemos privilegios de root y queremos instalarlo para todos los usuarios del sistema, ejecutamos desde la shell
<preformat>
pip3 install telepot
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>En otro caso, podemos instalarlo solo para nuestro usuario:
<preformat>
pip3 install --user telepot
</preformat></p></list-item>
</list>
</sec>
<sec id="c12-s8-s2-2">
<title><bold>Enviar un mensaje a un usuario</bold></title>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
import telepot
TOKEN = &#x201C;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&#x201D;
bot = telepot.Bot(TOKEN)
def main():
&#x00A0;&#x00A0;&#x00A0;id_usuario = &#x201C;999999999&#x201D;
&#x00A0;&#x00A0;&#x00A0;bot.sendMessage(id_usuario, &#x201C;Hola&#x201D;)

&#x00A0;&#x00A0;&#x00A0;return
if __name__ == &#x201C; __main__ &#x201C;:
&#x00A0;&#x00A0;&#x00A0;main()
</preformat></p></td></tr></tbody></table></table-wrap>
</sec>
<sec id="c12-s8-s2-3">
<title><target target-type="page" id="pges_191"/><bold>Contestar a los mensajes de un usuario</bold></title>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
import telepot
import telepot.namedtuple
import time
from telepot.loop import MessageLoop
TOKEN = &#x201C;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&#x201D;
bot = telepot.Bot(TOKEN)
def handle(msg):

&#x00A0;&#x00A0;&#x00A0;chat_id = msg[&#x201C;chat&#x201D;][&#x201C;id&#x201D;]
&#x00A0;&#x00A0;&#x00A0;texto = msg[&#x201C;text&#x201D;]

&#x00A0;&#x00A0;&#x00A0;print(&#x201C;Recibiendo mensaje:&#x201D;)
&#x00A0;&#x00A0;&#x00A0;print(msg)

&#x00A0;&#x00A0;&#x00A0;respuesta = &#x201C;Me has dicho &#x201C; + texto
&#x00A0;&#x00A0;&#x00A0;bot.sendMessage(chat_id, respuesta)
&#x00A0;&#x00A0;&#x00A0;return
</preformat></p></td></tr></tbody></table></table-wrap>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
def main():
&#x00A0;&#x00A0;&#x00A0;MessageLoop(bot, handle).run_as_thread()
&#x00A0;&#x00A0;&#x00A0;print &#x201C;Escuchando...&#x201D;

&#x00A0;&#x00A0;&#x00A0;while 1:
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;time.sleep(10)
&#x00A0;&#x00A0;&#x00A0;return

if __name__ == &#x201C; __main__ &#x201C;:
&#x00A0;&#x00A0;&#x00A0;main()
</preformat></p></td></tr></tbody></table></table-wrap>
<p>Ejemplo:</p>
<preformat>
Escuchando...
Recibiendo mensaje:
{u&#x0027;date&#x0027;: 1542706429, u&#x0027;text&#x0027;: u&#x0027;Hola&#x0027;, u&#x0027;from&#x0027;: {u&#x0027;username&#x0027;: u&#x0027;Juan_perez&#x0027;, u&#x0027;first_name&#x0027;: u&#x0027;Juan&#x0027;, u&#x0027;last_name&#x0027;: u&#x0027;Perez&#x0027;, u&#x0027;is_bot&#x0027;: False, u&#x0027;language_code&#x0027;: u&#x0027;es&#x0027;, u&#x0027;id&#x0027;: 154195197}, u&#x0027;message_id&#x0027;: 2704, u&#x0027;chat&#x0027;:
{u&#x0027;username&#x0027;: u&#x0027;Juan_perez&#x0027;, u&#x0027;first_name&#x0027;: u&#x0027;Juan&#x0027;, u&#x0027;last_name&#x0027;: u&#x0027;Perez&#x0027;, u&#x0027;type&#x0027;: u&#x0027;private&#x0027;, u&#x0027;id&#x0027;: 154196127}}
</preformat>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_192"/>En los ejemplos anteriores, para preparar un ejemplo m&#x00ED;nimo hemos escrito el token en el c&#x00F3;digo fuente</p></list-item>
<list-item><label>&#x25FE;</label> <p>Por motivos de seguridad, en cualquier programa que no sea una prueba de concepto, el token (o cualquier otra contrase&#x00F1;a similar) deber&#x00ED;a ir en un fichero aparte</p>
<list list-type="bullet">
<list-item><p>Al leer el token desde un fichero, ser&#x00E1; necesario que suprimas el salto de l&#x00ED;nea final</p></list-item>
<list-item><p>Puedes usar el m&#x00E9;todo <code>strip()</code>, que devuelve una copia de la cadena eliminando tabuladores, espacios y saltos de l&#x00ED;nea al princio y al final de una cadena</p></list-item>
</list></list-item>
</list>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
&#x003E;&#x003E;&#x003E; a = &#x0027; hola &#x0027;
&#x003E;&#x003E;&#x003E; a.strip()
&#x0027;hola&#x0027;
</preformat></p></td></tr></tbody></table></table-wrap>
</sec>
</sec></sec>
<sec id="c12-s9">
<label><bold>12.9.</bold></label>
<title><bold>Expresiones Regulares en python</bold></title>
<sec id="c12-s9-s1">
<label><bold>12.9.1.</bold></label>
<title><bold>Introducci&#x00F3;n</bold></title>
<sec id="c12-s9-s1-1">
<title><bold>Expresiones regulares. Introducci&#x00F3;n</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Las <italic>expresione regulares</italic> son expresiones que definen un conjunto de cadenas de texto</p></list-item>
<list-item><label>&#x25FE;</label> <p>Pertenecen a la disciplinas de teor&#x00ED;a de aut&#x00F3;matas y lenguajes formales. Las bases las sienta Stephen Cole Kleene en la d&#x00E9;cada de 1950. Se desarrollan en los a&#x00F1;os 60 y se popularizan en los a&#x00F1;os 80</p></list-item>
<list-item><label>&#x25FE;</label> <p>Se denominan abreviadamente re, regex o regexp</p>
<p>Tambi&#x00E9;n <italic>patr&#x00F3;n</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>Las regex son una herramienta muy potente para procesar texto autom&#x00E1;ticamente. Especialmente texto plano, no son muy apropiadas para HTML o XML</p></list-item>
<list-item><label>&#x25FE;</label> <p>Las regex pueden manejarse desde</p>
<list list-type="bullet">
<list-item><p>Herramientas cl&#x00E1;sicas como grep, sed, awk</p></list-item>
<list-item><p>Editores de texto</p></list-item>
<list-item><p>Librer&#x00ED;as para lenguajes de programaci&#x00F3;n cl&#x00E1;sicos como C o Pascal</p></list-item>
<list-item><p>Librer&#x00ED;as nativas en cualquier lenguaje moderno: perl, python, java, ruby, c<code>#</code>, etc</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_193"/>Entre las distintinas versiones hay similitudes y diferencias</p>
<list list-type="bullet">
<list-item><p>Las regex <italic>tradicionales</italic> (grep, sed, awk) se parecen bastante entre s&#x00ED;.</p></list-item>
<list-item><p>Las regex <italic>modernas</italic> se parecen entre s&#x00ED;. Son una derivaci&#x00F3;n de las tradicionales. Su uso resulta m&#x00E1;s sencillo</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Es una materia que puede llegar a resultar bastante compleja, conocerlas a fondo es dif&#x00ED;cil. Pero manejar sus fundamentos resulta de gran utilidad para pr&#x00E1;cticamente cualquier programador en cualquier entorno</p></list-item>
</list>
</sec>
<sec id="c12-s9-s1-2">
<title><bold>Algunas definiciones</bold></title>
<p>Decimos que una regex y una cadena de texto <italic>encajan</italic> o <italic>no encajan</italic>. <xref ref-type="fn" rid="FN10"><sup>10</sup></xref></p>
<p>Ejemplo. Patr&#x00F3;n/regex</p>
<preformat>
[Aa]na [Pp].rez
</preformat>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>La cadena <code>Ana P&#x00E9;rez</code> encaja</p></list-item>
<list-item><label>&#x25FE;</label> <p>Tambi&#x00E9;n encajan las cadenas ana perez, <code>ana p&#x00E9;rez, ana porez, Ana p&#x00D1;rez</code>, etc</p></list-item>
<list-item><label>&#x25FE;</label> <p>La cadena <code>ANA PEREZ</code> no encaja</p></list-item>
<list-item><label>&#x25FE;</label> <p>Decimos que un car&#x00E1;cter<xref ref-type="fn" rid="FN11"><sup>11</sup></xref></p>
<list list-type="bullet">
<list-item><p>Se usa como literal si representa a ese car&#x00E1;cter.</p></list-item>
<list-item><p>Se usa como metacar&#x00E1;cter (o <italic>comod&#x00ED;n</italic>) si tiene un significado especial, si representa algo distinto al propio car&#x00E1;cter</p></list-item>
</list></list-item>
</list>
<p>Ejemplo: el punto usado como literal, representa un punto. Usado como metacar&#x00E1;cter, representa cualquier car&#x00E1;cter</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Normalmente, cuando un car&#x00E1;cter puede tomarse como metacar&#x00E1;cter o como literal</p>
<list list-type="bullet">
<list-item><p>Por omisi&#x00F3;n se toma como metacar&#x00E1;cter</p></list-item>
<list-item><p>Para interpretarlo como literal, hay que escaparlo. T&#x00ED;picamente anteponiendo una barra invertida o incluyendolo entre comillas, rectas o dobles. Ejemplo: \.</p></list-item>
</list></list-item>
</list>
</sec></sec>
<sec id="c12-s9-s2">
<label><bold>12.9.2.</bold></label>
<title><target target-type="page" id="pges_194"/><bold>Metacaracteres</bold></title>
<sec id="c12-s9-s2-1">
<title><bold>Metacaracteres cl&#x00E1;sicos</bold></title>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>^</code></p></td>
<td valign="top" align="left"><p><code>Principio de cadena (principio de l&#x00ED;nea)</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>$</code></p></td>
<td valign="top" align="left"><p><code>Fin de cadena (fin de l&#x00ED;nea)</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>.</code></p></td>
<td valign="top" align="left"><p><code>Cualquier car&#x00E1;cter</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>*</code></p></td>
<td valign="top" align="left"><p><code>La regex precedente puede aparecer 0 o m&#x00E1;s veces</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>?</code></p></td>
<td valign="top" align="left"><p><code>La regex precedente puede aparecer o no aparecer</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>[]</code></p></td>
<td valign="top" align="left"><p><code>Clase de caracteres: uno cualquiera de los caracteres entre corchetes</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>[^]</code></p></td>
<td valign="top" align="left"><p><code>Complementario de la clase de caracteres: cualquiera menos los incluidos entre corchetes</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>[a-f]</code></p></td>
<td valign="top" align="left"><p><code>Caracteres de la &#x0027;a&#x0027; hasta la &#x0027;f&#x0027;</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>{2,3}</code></p></td>
<td valign="top" align="left"><p><code>La regex precedente se puede repetir entre 2 y 3 veces</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>{2,}</code></p></td>
<td valign="top" align="left"><p><code>La regex precedente se repite 2 o m&#x00E1;s veces</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>{,3}</code></p></td>
<td valign="top" align="left"><p><code>La regex precedente se repite entre 0 y 3 veces</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>{4}</code></p></td>
<td valign="top" align="left"><p><code>La regex precedente se repite 4 veces</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>()</code></p></td>
<td valign="top" align="left"><p><code>Permite agrupar una regex</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>\2</code></p></td>
<td valign="top" align="left"><p><code>El segundo grupo de regex</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>r1|r2</code></p></td>
<td valign="top" align="left"><p><code>Una regex u otra</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>\&#x003C;</code></p></td>
<td valign="top" align="left"><p><code>Inicio de palabra</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>\&#x003E;</code></p></td>
<td valign="top" align="left"><p><code>Fin de palabra</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<p>Ejemplos</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>[a-z][a-z0-9_]*</code></p></td>
<td valign="top" align="left"><p><code>letra min&#x00FA;scula seguida de cero o</code></p>
<p><code>m&#x00E1;s letras min&#x00FA;sculas, n&#x00FA;meros o barras bajas</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>Se&#x00F1;ora?</code></p></td>
<td valign="top" align="left"><p><code>Se&#x00F1;or o Se&#x00F1;ora</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>Serg[e&#x00E9;][iy]? Ra(j|ch|h|kh)m[a&#x00E1;]n[ij]no(v|ff|w)</code></p></td>
<td valign="top" align="left"><p><code>Serg&#x00E9;i / Sergei / Sergey / Serge</code></p>
<p><code>Rajm&#x00E1;ninov / Rachmaninoff / Rahmanjnov ...</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<p>Dentro una clase de caracteres, cada car&#x00E1;cter siempre se toma literalmente, no se escapa ning&#x00FA;n posible metacar&#x00E1;cter (excepto el cierre de corchetes)</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>[0-9.]</code></p></td>
<td valign="top" align="left"><p><code># Un d&#x00ED;gito o un punto. (Aq&#x00FA;&#x0131; el punto representa un punto, no &#x201C;cualquier car&#x00E1;cter&#x0022;)</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<p>Atenci&#x00F3;n: algunos metacaracteres de bash coinciden, otros tienen un significado distinto</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>?</code></p></td>
<td valign="top" align="left"><p><code>En bash, cualquier car&#x00E1;cter</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>*</code></p></td>
<td valign="top" align="left"><p><code>En bash, cualquier car&#x00E1;cter 0 o m&#x00E1;s veces</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
</sec>
<sec id="c12-s9-s2-2">
<title><target target-type="page" id="pges_195"/><bold>Fin de l&#x00ED;nea</bold></title>
<p>El fin de l&#x00ED;nea se representa de diferentas maneras</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>En MS-DOS/Windows y otros, el fin de l&#x00ED;nea se representa con <code>CRLF</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>En Unix, se representa con <code>LF</code></p></list-item>
</list>
<p>Esto es una fuente tradicional de problemas</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>En Windows, un fichero para Unix se ver&#x00E1; como una &#x00FA;nica l&#x00ED;nea</p></list-item>
<list-item><label>&#x25FE;</label> <p>En Unix, un fichero para Windows tendr&#x00E1; un <code>^M</code> al final de cada l&#x00ED;nea</p></list-item>
</list>
<p>Algunos editores son lo bastante <italic>listos</italic> como para mostrar correctamente un fichero con un formato distinto</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Pero ocultar el problema a veces es contraproducente: puede suceder que la apariencia sea correcta, pero el compilador no lo acepte y muestre un error muy confuso
<table-wrap>
<table frame="hsides" rules="groups">
<thead>
<tr>
<th valign="top" align="center"><p>Nombre ASCII</p></th>
<th valign="top" align="center"><p>Abreviatura</p></th>
<th valign="top" align="center"><p>Decimal</p></th>
<th valign="top" align="center"><p>Hexa</p></th>
<th valign="top" align="center"><p>Caret Notation</p></th>
<th valign="top" align="center"><p>Notaci&#x00F3;n C</p></th>
</tr>
</thead>
<tbody>
<tr>
<td valign="top" align="center"><p>Carriage Return</p></td>
<td valign="top" align="center"><p>CR</p></td>
<td valign="top" align="center"><p>13</p></td>
<td valign="top" align="center"><p>OD</p></td>
<td valign="top" align="center"><p><code>^M</code></p></td>
<td valign="top" align="center"><p><code>\r</code></p></td>
</tr>
<tr>
<td valign="top" align="center"><p>Line Feed</p></td>
<td valign="top" align="center"><p>LF</p></td>
<td valign="top" align="center"><p>10</p></td>
<td valign="top" align="center"><p>0A</p></td>
<td valign="top" align="center"><p><code>^J</code></p></td>
<td valign="top" align="center"><p><code>\n</code></p></td>
</tr>
</tbody>
</table>
</table-wrap></p></list-item>
<list-item><label>&#x25FE;</label> <p><italic>Caret notation</italic> es una m&#x00E9;todo empleado en ASCII para representar caracteres no imprimibles. (Caret: acento circunflejo). Normalmente, se puede usar la tecla <code>control</code> para generar estos caracteres</p></list-item>
<list-item><label>&#x25FE;</label> <p><italic>Notaci&#x00F3;n C</italic> : Notaci&#x00F3;n del lenguaje C, que despu&#x00E9;s han seguido muchos otros como python</p></list-item>
</list>
<p>Obs&#x00E9;rvese que nada de esto se refiere directamente a las expresiones regulares: Cuando en una cadena escribimos \n, se entiende que es un avance de l&#x00ED;nea (excepto si lo escapamos con otra barra adicional, o con una cadena cruda de python)</p>
<preformat>
\n suele representar LF, excepto en macOS, donde suele representar CR.
En java o en .net sobre cualquier SO, siempre representa LF
</preformat>
<p>Python emplea <italic>universal newlines</italic>:</p>
<p>En la E/S de ficheros, por omision:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_196"/>Sea cual sea el criterio de la entrada, lo convierte a <code>\n</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>A la salida, escribe el formato propio de la plataforma</p></list-item>
</list>
<p>Este comportamieto puede cambiarse si es necesario (consultar PEP 278 y PEP 3116)</p>
<p>Para cadenas que no provengan de un fichero, se puede emplear el m&#x00E9;todo <italic>splitlines()</italic> de las cadenas, que:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Trocea una cadena con el mismo enfoque (soporta todos los criterios), y elimina el fin de linea (sea el que sea)</p></list-item>
<list-item><label>&#x25FE;</label> <p>A menos que se invoque <italic>splitlines(true)</italic>, entonces conserve el fin de linea, inalterado</p></list-item>
</list>
<p>Otra fuente t&#x00ED;pica de problemas: &#x00BF;El fin de l&#x00ED;nea es un terminador o un separador?</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Algunas herramientas/aplicaciones/sistemas operativos entienden que es un separador, y por tanto la &#x00FA;ltima l&#x00ED;nea no acaba en <code>\n</code> sino en fin de fichero</p></list-item>
<list-item><label>&#x25FE;</label> <p>Otras consideran que es un terminador, por tanto la &#x00FA;ltima l&#x00ED;nea s&#x00ED; acaba en <code>\n</code> (P.e. Unix)</p></list-item>
</list>
<p>Todo esto son cuestiones que puede ser necesario considerar procesando texto. Pero si lo &#x00FA;nico que queremos es convertir ficheros entre Windows y Unix, no hace falta usar regex</p>
<preformat>
sed -e &#x0027;s/$/\r/&#x0027; inputfile &#x003E; outputfile # Unix a Windows
sed -e &#x0027;s/\r$//&#x0027; inputfile &#x003E; outputfile # Windows a Unix
</preformat>
<p>El metacar&#x00E1;cter $ de las regex no se corresponde exactamente con CR ni con LF. Su significado exacto depende de la plataforma. Normalmente encaja tanto con el fin de cadena como con la posici&#x00F3;n inmediatamente antes de LF/CR/CRLF</p>
</sec>
<sec id="c12-s9-s2-3">
<title><bold>Metacaracteres modernos</bold></title>
<p>El lenguaje perl es el <italic>padre</italic> de las regex modernas. Incluye los metacaracteres cl&#x00E1;sicos y a&#x00F1;ade otros nuevos. Lenguajes como python copian las regex de perl<target target-type="page" id="pges_197"/></p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>Metac.</code></p></td>
<td valign="top" align="left"><p></p></td>
<td valign="top" align="left"><p><code>Clase equivalente</code></p></td>
</tr>
<tr>
<td valign="top" align="left" colspan="3"><p><code>------------------------------------------------------------------</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>\d</code></p></td>
<td valign="top" align="left"><p><code>D&#x00ED;gito</code></p></td>
<td valign="top" align="right"><p><code>[0-9]</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>\s</code></p></td>
<td valign="top" align="left"><p><code>Espacio en blanco, tab...</code></p></td>
<td valign="top" align="right"><p><code>[\ \t\r\n\f] (*)</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>\w</code></p></td>
<td valign="top" align="left"><p><code>Car&#x00E1;cter de palabra (alfanum&#x00E9;tico o barra baja)</code></p></td>
<td valign="top" align="right"><p><code>[0-9a-zA-Z_]</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>\D</code></p></td>
<td valign="top" align="left"><p><code>Cualquiera menos \d</code></p></td>
<td valign="top" align="right"><p><code>[^0-9]</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>\S</code></p></td>
<td valign="top" align="left"><p><code>Cualquiera menos \s</code></p></td>
<td valign="top" align="right"><p><code>[^\s]</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>\W</code></p></td>
<td valign="top" align="left"><p><code>Cualquiera menos \w;</code></p></td>
<td valign="top" align="right"><p><code>[^\w]</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>\b</code></p></td>
<td valign="top" align="left" colspan="2"><p><code>Limite de palabra. (Secuencia de alfanum&#x00E9;ricos o barra baja)</code></p></td>
</tr>
</tbody>
</table>
<table-wrap-foot>
<attrib>(*) \t: Tab</attrib>
<attrib>\f: Form Feed, salto de p&#x00E1;gina</attrib>
</table-wrap-foot>
</table-wrap>
<p>Observaciones</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>El &#x00FA;nico metacar&#x00E1;cter que cambia entre regex cl&#x00E1;sicas y modernas es el l&#x00ED;mite de palabra, se usa <code>\b</code> y no <code>\&#x003C; \&#x003E;</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>Las locales no siembre est&#x00E1;n bien definidas, en tal caso para definir una palabra tal vez haya que incluir explicitamente las letras espa&#x00F1;olas (si procede)</p></list-item>
</list>
</sec></sec>
<sec id="c12-s9-s3">
<label><bold>12.9.3.</bold></label>
<title><target target-type="page" id="pges_198"/><bold>Regexp en python</bold></title>
<sec id="c12-s9-s3-1">
<title><bold>Regexp en python</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Para operaciones sencillas con cadenas, como b&#x00FA;squedas y sustituciones sin metacaracteres, es m&#x00E1;s eficiente emplear los m&#x00E9;todos de las cadenas, como <code>find</code> y <code>replace</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>El m&#x00F3;dulo re tiene funciones a la que se puede pasar directamente una cadena regexp
<preformat>
&#x003E;&#x003E;&#x003E; import re
&#x003E;&#x003E;&#x003E; m=re.search(&#x0027;[0-9]+&#x0027; , &#x0027;abc98521zzz&#x0027;)
&#x003E;&#x003E;&#x003E; m.group(0)
&#x0027;98521&#x0027;
</preformat></p>
<p>Pero aq&#x00FA;&#x0131; usaremos objetos regex, m&#x00E1;s potentes</p></list-item>
</list>
</sec>
<sec id="c12-s9-s3-2">
<title><bold>Regexp en python</bold></title>
<p>Para usar regexp, importamos el m&#x00F3;dulo <code>re</code></p>
<p><code>import re</code></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Una regex es un objeto que construimos con la funci&#x00F3;n <code>compile</code>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
regex=re.compile(&#x201C;a+&#x201D;)
</preformat></p></td></tr></tbody></table></table-wrap></p></list-item>
<list-item><label>&#x25FE;</label> <p>Para buscar el patr&#x00F3;n en una cadena tenemos los m&#x00E9;todos</p>
<list list-type="bullet">
<list-item><p><code>match()</code>, que comprueba si el principio de la cadena encaja en la regex</p></list-item>
<list-item><p><code>search()</code>, que comprueba si alguna parte de la cadena encaja en la regex</p></list-item>
</list>
<p>Ambos m&#x00E9;todos devuelven</p>
<list list-type="bullet">
<list-item><p>Un objeto <code>SRE_Match</code> si han tenido &#x00E9;xito (que se eval&#x00FA;a como <italic>cierto</italic>)</p></list-item>
</list></list-item>
</list>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># regex01.py</italic>
import re
regex=re.compile(&#x201C;aa+&#x201D;)
m=regex.match(&#x201C;taartamudo&#x201D;)
print(m) <italic># None</italic>
m=regex.search(&#x201C;taartamudo&#x201D;)
print(m) <italic># Cierto</italic>
m=regex.match(&#x201C;aaahora&#x201D;)
print(m) <italic># Cierto</italic>
</preformat></p></td></tr></tbody></table></table-wrap>
<p>Casi siempre hay m&#x00E1;s de una regex posible. Ejemplo: Capturar una direcci&#x00F3;n IP</p>
<p>Estas sentencias son equivalentes</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
direccion_ip=re.compile(r&#x0022;&#x0022;&#x0022;\d\d?\d?\.\d\d?\d?\.\d\d?\d?\.\d\d?\d?&#x0022;&#x0022;&#x0022;)
direccion_ip=re.compile(r&#x0022;&#x0022;&#x0022;(\d\d?\d?\.){3}\d\d?\d?&#x0022;&#x0022;&#x0022;)
direccion_ip=re.compile(r&#x0022;&#x0022;&#x0022;\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}&#x0022;&#x0022;&#x0022;)
direccion_ip=re.compile(r&#x0022;&#x0022;&#x0022;(\d{1,3}\.){3}\d{1,3}&#x0022;&#x0022;&#x0022;)
</preformat></p></td></tr></tbody></table></table-wrap>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_199"/>Es necesario <italic>escapar</italic> el punto</p></list-item>
<list-item><label>&#x25FE;</label> <p>Obs&#x00E9;rvese que esta regex no se corresponde exactamente con una direcci&#x00F3;n IP. Por ejemplo admitir&#x00ED;a <code>315.15.256.715</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>Suele ser conveniente definir la regex con <italic>cadenas crudas</italic> de python (<code>r&#x0022;&#x0022;&#x0022;cadena&#x0022;&#x0022;&#x0022;</code>)</p>
<p>Esto evita tener que escapar las barras invertidas para que se tomen como literales.</p>
<p>Tambi&#x00E9;n permite, por ejemplo, que la secuencia <code>\n</code> se tome como como una barra invertida y una ene. (Y no como un salto de l&#x00ED;nea carro)</p></list-item>
</list>
</sec>
<sec id="c12-s9-s3-3">
<title><bold>Comentarios en las regex</bold></title>
<p>El flag <code>re.VERBOSE</code> es muy &#x00FA;til. Al activarlo se ignoran</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Los espacios (no <italic>escapados</italic>)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Las almohadillas y todo el texto posterior, hasta fin de l&#x00ED;nea</p></list-item>
</list>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
ip = re.compile(r&#x0022;&#x0022;&#x0022;
&#x00A0;&#x00A0;&#x00A0;(\d{1,3}\.){3} &#x00A0;&#x00A0;&#x00A0;# de 1 a 3 digitos y punto, repetido 3 veces
&#x00A0;&#x00A0;&#x00A0;\d{1,3} &#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;# de 1 a 3 digitos
&#x00A0;&#x00A0;&#x00A0;&#x0022;&#x0022;&#x0022;, re.VERBOSE)
</preformat></p></td></tr></tbody></table></table-wrap>
</sec>
<sec id="c12-s9-s3-4">
<title><bold>Otros flags</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>re.VERBOSE</code>
<preformat>
re.X
</preformat></p>
<p>Permite comentarios dentro de la regex</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>re.IGNORECASE</code>
<preformat>
re.I
</preformat></p>
<p>No distingue entre may&#x00FA;sculas y min&#x00FA;sculas</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>re.LOCALE</code>
<preformat>
re.L
</preformat></p>
<p>Hace que <code>\w, \W, \b, \B, \s</code> y <code>\S</code> tengan en cuenta las <italic>locales</italic></p></list-item>
</list>
<p>Para combinar m&#x00E1;s de un flag, se usa la barra vertical (<code>&#x0027;|&#x0027;</code>), que es el operador <italic>or</italic> a nivel de bit.</p>
</sec>
<sec id="c12-s9-s3-5">
<title><target target-type="page" id="pges_200"/><bold>Grupos</bold></title>
<p>Un objeto <code>SRE_Match</code> devuelve en el atributo <code>group</code> las partes de la cadena que han encajado en la regex</p>
<p><code>group[0]</code> es el texto que ha encajado en la regex completa</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># regex02.py</italic>
import re
ip = re.compile(r&#x0022;&#x0022;&#x0022;
&#x00A0;&#x00A0;&#x00A0;(\d{1,3}\.){3} # 1,2 o 3 digitos y punto, repetido 3 veces
&#x00A0;&#x00A0;&#x00A0;\d{1,3} # 1,2 o 3 d&#x00ED;gitos
&#x00A0;&#x00A0;&#x00A0;&#x0022;&#x0022;&#x0022;, re.VERBOSE)
texto=
r&#x0022;&#x0022;&#x0022;Mi correo es j.perez@alumnos.urjc.es
&#x00A0;&#x00A0;&#x00A0;y mi direcci&#x00F3;n IP, 192.168.1.27&#x0022;&#x0022;&#x0022;for linea in texto.split(&#x0027;\n&#x0027;):
&#x00A0;&#x00A0;&#x00A0;m=ip.search(linea)
&#x00A0;&#x00A0;&#x00A0;if m:
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;print(m.group(0))
</preformat></p></td></tr></tbody></table></table-wrap>
<p>Ejecuci&#x00F3;n:</p>
<p><code>192.168.1.27</code></p>
<p>Los par&#x00E9;ntesis</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Como hemos visto, definen el &#x00E1;mbito y precedencia de los dem&#x00E1;s operadores</p></list-item>
<list-item><label>&#x25FE;</label> <p>Adem&#x00E1;s, definen grupos. El resultado de cada b&#x00FA;squeda devuelve en</p>
<p>group[n] el grupo n-&#x00E9;simo</p></list-item>
</list>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># regex03.py</italic>
import re
correo_alumno = re.compile(r&#x0022;&#x0022;&#x0022;
(
\b&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;# L&#x00ED;mite de palabra
[\w.]+&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;# 1 o m&#x00E1;s caracteres de palabra o punto
\b&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;# l&#x00ED;mite de palabra
(alumnos\.urjc\.es) # Grupo 2
&#x0022;&#x0022;&#x0022;, re.VERBOSE)
<target target-type="page" id="pges_201"/>
texto= r&#x0022;&#x0022;&#x0022;Lleg&#x00F3; un correo de j.perez@alumnos.urjc.es preguntando
&#x00A0;&#x00A0;&#x00A0;si hay clase ma&#x00F1;ana&#x0022;&#x0022;&#x0022;

for linea in texto.split(&#x0027;\n&#x0027;):
&#x00A0;&#x00A0;&#x00A0;m=correo_alumno.search(linea)
&#x00A0;&#x00A0;&#x00A0;if m:
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;print(&#x201C;Alumno: {}&#x201D;.format(m.group(1))) <italic># j.perez</italic>
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;print(&#x201C;Dominio: {}&#x201D;.format(m.group(2))) <italic># alumnos.urjc.es</italic>
</preformat></p></td></tr></tbody></table></table-wrap>
<p>Dentro de una regex, podemos hacer referencia a un grupo</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env</italic>
<italic>python3 #</italic>
<italic>regex04.py</italic>
import re

regex=re.compile(r&#x0022;&#x0022;&#x0022;(\b\w+\b) # Una palabra
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;\s+ # Espacios
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;\1 # Grupo 1: la misma palabra
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x0022;&#x0022;&#x0022;, re.VERBOSE)

texto=r&#x0022;&#x0022;&#x0022;Buscando palabras repetidas repetidas&#x0022;&#x0022;&#x0022;
for linea in texto.split(&#x0027;\n&#x0027;):
&#x00A0;&#x00A0;&#x00A0;m=regex.search(linea)
&#x00A0;&#x00A0;&#x00A0;if m:
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;print(m.group(1)) <italic># Devuelve &#x201C;repetidas&#x201D;</italic>
</preformat></p></td></tr></tbody></table></table-wrap>
<p>Ejemplo de definici&#x00F3;n expl&#x00ED;cita de palabra espa&#x00F1;ola</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># regex05.py</italic>
import re
regex=re.compile(r&#x0022;&#x0022;&#x0022;
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;(\b&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;# L&#x00ED;mite de palabra
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;[\w&#x00E1;&#x00E9;&#x00ED;&#x00F3;&#x00FA;&#x00C1;&#x00C9;&#x00CD;&#x00D3;&#x00DA;&#x00F1;&#x00D1;&#x00FC;&#x00DC;]+&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;# Palabra, incluyendo letras espa&#x00F1;olas
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;\b)
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;\s*&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;# Espacios, opcionalmente
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;$&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;# Fin de l&#x00ED;nea
&#x0022;&#x0022;&#x0022;, re.VERBOSE)
texto=r&#x0022;&#x0022;&#x0022;Buscando la &#x00FA;ltima palabra de la l&#x00ED;nea &#x0022;&#x0022;&#x0022;
<target target-type="page" id="pges_202"/>
for linea in texto.split(&#x0027;\n&#x0027;):
m=regex.search(linea) if m:
print(m.group(1)) <italic># Devuelve &#x201C;l&#x00ED;nea&#x201D;</italic>
</preformat></p></td></tr></tbody></table></table-wrap>
</sec>
<sec id="c12-s9-s3-6">
<title><bold>Sustituciones</bold></title>
<p>Adem&#x00E1;s de <code>search</code> y <code>match</code>, los objetos regex tienen el m&#x00E9;todo <code>sub(reemplazo,cadena)</code> que</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Busca el patr&#x00F3;n en la cadena</p></list-item>
<list-item><label>&#x25FE;</label> <p>Si lo encuentra, reempleza el texto que ha encajado por <code>reemplazo</code></p>
<p>Dentro de reemplazo se pueden usar referencias a grupos</p></list-item>
<list-item><label>&#x25FE;</label> <p>Devuelve el texto resultante</p></list-item>
</list>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># regex06.py</italic>
import re
<italic># reemplazamos los correos login@urjc.es por # [Correo de login en la URJC ]</italic>
correo_urjc = re.compile(r&#x0022;&#x0022;&#x0022;
(
\b&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;# L&#x00ED;mite de palabra
[\w.]+&#x00A0;&#x00A0;&#x00A0;# 1 o m&#x00E1;s caracteres de palabra o punto
\b&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;# l&#x00ED;mite de palabra
)
@urjc\.es
&#x0022;&#x0022;&#x0022;, re.VERBOSE)
texto=
&#x201C;Si es necesario, escribe <ext-link ext-link-type="uri" xlink:href="mailto:aj.perez@urjc.es">aj.perez@urjc.es</ext-link>&#x201D; for linea in texto.split(&#x0027;\n&#x0027;):
print(correo_urjc.sub(r&#x0022;&#x0022;&#x0022;[Correo de \1 en la URJC]&#x0022;&#x0022;&#x0022;,linea))
</preformat></p></td></tr></tbody></table></table-wrap>
<p>Resultado de la ejecuci&#x00F3;n</p>
<preformat>
koji@mazinger:<sup>~</sup>/python$ ./test.py
Si es necesario, escribe a [Correo de j.perez en la URJC]
</preformat>
</sec>
<sec id="c12-s9-s3-7">
<title><target target-type="page" id="pges_203"/><bold>Regex multil&#x00ED;nea</bold></title>
<p>Hasta ahora hemos procesado cada l&#x00ED;nea de forma independiente de las dem&#x00E1;s, lo cual es bastante frecuente</p>
<p>En este caso</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>El metacar&#x00E1;cter <code>&#x0027;^&#x0027;</code> representa el principio de cadena, lo que equivale al principio de l&#x00ED;nea</p></list-item>
<list-item><label>&#x25FE;</label> <p>El metacar&#x00E1;cter <code>&#x0027;$&#x0027;</code> representa el fin de cadena, lo que equivale al fin de l&#x00ED;nea</p></list-item>
<list-item><label>&#x25FE;</label> <p>El metacar&#x00E1;cter <code>&#x0027;.&#x0027;</code> no encaja en el fin de l&#x00ED;nea</p></list-item>
</list>
<p>Pero en otras ocasiones querremos aplicar la regex a m&#x00E1;s de una l&#x00ED;nea. Esto generalmente requiere de algunos <italic>flags</italic> adicionales</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>re.DOTALL</code>
<preformat>
re.S
</preformat></p>
<p>Hace que el metacar&#x00E1;cter &#x0027;.&#x0027; encaje en el fin de l&#x00ED;nea</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>re.MULTILINE</code>
<preformat>
re.M
</preformat></p>
<p>Hace que el metacar&#x00E1;cter <code>&#x0027;^&#x0027;</code> represente el principio de l&#x00ED;nea</p>
<p>El metacar&#x00E1;cter <code>&#x0027;$&#x0027;</code> representa el fin de l&#x00ED;nea</p></list-item>
</list>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" style="background-color:#e7e7e8"><p><preformat>
<italic>#!/usr/bin/env python3</italic>
<italic># regex07.py</italic>
import re
regex=re.compile(r&#x0022;&#x0022;&#x0022;
&#x00A0;&#x00A0;&#x00A0;^&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;# Principio de l&#x00ED;nea
&#x00A0;&#x00A0;&#x00A0;(\b
&#x00A0;&#x00A0;&#x00A0;[\w&#x00E1;&#x00E9;&#x00ED;&#x00F3;&#x00FA;&#x00B4;A&#x00B4;E&#x00B4;I&#x00B4;O&#x00B4;U~n~N]+&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;# Palabra
&#x00A0;&#x00A0;&#x00A0;\b)&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;#
&#x00A0;&#x00A0;&#x00A0;.*&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;# Resto de la l&#x00ED;nea
&#x00A0;&#x00A0;&#x00A0;^&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;# Comienzo de l&#x00ED;nea
&#x00A0;&#x00A0;&#x00A0;\1&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;# La misma palabra
&#x00A0;&#x00A0;&#x00A0;&#x0022;&#x0022;&#x0022;, re.VERBOSE|re.MULTILINE|re.DOTALL)

texto=r&#x0022;&#x0022;&#x0022;En este ejemplo estamos
buscando dos l&#x00ED;neas que comiencen igual
buscando l&#x00ED;neas con primera palabra
coincidente
&#x0022;&#x0022;&#x0022;
<target target-type="page" id="pges_204"/>
m=regex.search(texto) if m:
&#x00A0;&#x00A0;&#x00A0;print(m.group(1)) <italic># Devuelve &#x201C;buscando&#x201D;</italic>
</preformat></p></td></tr></tbody></table></table-wrap>
</sec>
<sec id="c12-s9-s3-8">
<title><bold>Split con regex</bold></title>
<p>Se puede trocear una cadena indicando con una regex cu&#x00E1;l es el separador</p>
<p>Ejemplo: queremos una lista con todos los unos consecutivos, separados por ceros</p>
<preformat>
&#x003E;&#x003E;&#x003E; import re
&#x003E;&#x003E;&#x003E; miregex=re.compile(r&#x0027;0+&#x0027;)
&#x003E;&#x003E;&#x003E; miregex.split(&#x0027;10011100011110001&#x0027;)
[&#x0027;1&#x0027;, &#x0027;111&#x0027;, &#x0027;1111&#x0027;, &#x0027;1&#x0027;]
</preformat>
<p>Atenci&#x00F3;n: el separador, por definici&#x00F3;n, est&#x00E1; entre dos elementos. No antes del primero ni despu&#x00E9;s del &#x00FA;ltimo.</p>
<p>En el siguiente ejemplo los ceros no se comportan como separadores, por lo que el resultado no es exactamente el deseado (aunque se acerca mucho)</p>
<preformat>
&#x003E;&#x003E;&#x003E; miregex.split(&#x0027;00100111000111100010&#x0027;)
[&#x0027;&#x0027;, &#x0027;1&#x0027;, &#x0027;111&#x0027;, &#x0027;1111&#x0027;, &#x0027;1&#x0027;, &#x0027;&#x0027;]
</preformat>
</sec>
</sec></sec>
</body>
<back>
<fn-group>
<fn id="FN10"><p><sup>10</sup> O tambi&#x00E9;n <italic>se corresponde</italic>, <italic>se ajusta a</italic>. En ingl&#x00E9;s, <italic>match</italic></p></fn>
<fn id="FN11"><p><sup>11</sup> la palabra <italic>car&#x00E1;cter</italic> es llana y lleva tilde, no es aguda. El plural es caracteres, tambi&#x00E9;n es llana</p></fn>
</fn-group>
</back>
</book-part>
<book-part id="c13" book-part-type="chapter">
<book-part-meta>
<book-part-id book-part-id-type="publisher-id">URJC</book-part-id>
<title-group>
<label>13.</label>
<title><target target-type="page" id="pges_205"/>SISTEMAS DE FICHEROS</title>
</title-group>
</book-part-meta>
<body>
<sec id="c13-s1">
<label><bold>13.1.</bold></label>
<title><bold>Estructura del sistemas de fichero</bold></title>
<sec id="c13-s1-1">
<title><bold>Introducci&#x00F3;n</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Un sistema de ficheros es una forma de almacenar y organizar ficheros para permitir su uso</p></list-item>
<list-item><label>&#x25FE;</label> <p>Pueden usar un dispositivo de almacenamiento (disco, cdrom), la red o ser s&#x00F3;lo un interfaz para acceder a datos</p></list-item>
<list-item><label>&#x25FE;</label> <p>Para poder empezar a almacenar informaci&#x00F3;n en un sistema de ficheros, &#x00E9;ste tiene que ser <italic>inicializado</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>En Unix, para poder usarlo, hay que <italic>montarlo</italic> en alguna parte de la jerarq&#x00FA;&#x0131;a de directorios, un &#x00E1;rbol cuya r&#x00E1;&#x0131;z es el directorio llamado <code>/.</code></p></list-item>
</list>
<p>On a UNIX system, everything is a file; if something is not a file, it is a process</p>
<p>Los ficheros pueden ser</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Ficheros normales</p></list-item>
<list-item><label>&#x25FE;</label> <p>Directorios</p></list-item>
<list-item><label>&#x25FE;</label> <p>Ficheros especiales (Entrada y salida. Est&#x00E1;n en <italic>/dev</italic> )</p></list-item>
<list-item><label>&#x25FE;</label> <p>Enlaces</p></list-item>
<list-item><label>&#x25FE;</label> <p>Fifos. (Pipes con nombre). Para comunicaci&#x00F3;n entre procesos</p></list-item>
<list-item><label>&#x25FE;</label> <p>Sockets de dominio. Similares a los sockets TCP/IP</p></list-item>
</list>
<p>El primer caracter de <code>ls -l</code> representa:</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>-</code></p></td>
<td valign="top" align="left"><p><code>Regular file</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>d</code></p></td>
<td valign="top" align="left"><p><code>Directory</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>c</code></p></td>
<td valign="top" align="left"><p><code>Special file</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>l</code></p></td>
<td valign="top" align="left"><p><code>Link</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>p</code></p></td>
<td valign="top" align="left"><p><code>Named pipe</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>s</code></p></td>
<td valign="top" align="left"><p><code>Socket</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>b</code></p></td>
<td valign="top" align="left"><p><code>Block device</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
</sec>
<sec id="c13-s1-2">
<title><target target-type="page" id="pges_206"/><bold>Jerarq&#x00FA;&#x0131;a del Sistema de Ficheros</bold></title>
<p>Para quien se acerca a Linux resulta confuso un <code>ls -l /</code></p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p>drwxr-xr-x</p></td>
<td valign="top" align="left"><p>2</p></td>
<td valign="top" align="left"><p>root</p></td>
<td valign="top" align="left"><p>root</p></td>
<td valign="top" align="left"><p>4096</p></td>
<td valign="top" align="left"><p>ene</p></td>
<td valign="top" align="left"><p>30</p></td>
<td valign="top" align="left"><p>20:34</p></td>
<td valign="top" align="left"><p>bin</p></td>
</tr>
<tr>
<td valign="top" align="left"><p>drwxr-xr-x</p></td>
<td valign="top" align="left"><p>2</p></td>
<td valign="top" align="left"><p>root</p></td>
<td valign="top" align="left"><p>root</p></td>
<td valign="top" align="left"><p>4096</p></td>
<td valign="top" align="left"><p>mar</p></td>
<td valign="top" align="left"><p>12</p></td>
<td valign="top" align="left"><p>19:46</p></td>
<td valign="top" align="left"><p>boot</p></td>
</tr>
<tr>
<td valign="top" align="left"><p>drwxr-xr-x</p></td>
<td valign="top" align="left"><p>5</p></td>
<td valign="top" align="left"><p>root</p></td>
<td valign="top" align="left"><p>root</p></td>
<td valign="top" align="left"><p>24576</p></td>
<td valign="top" align="left"><p>may</p></td>
<td valign="top" align="left"><p>22</p></td>
<td valign="top" align="left"><p>06:27</p></td>
<td valign="top" align="left"><p>dev</p></td>
</tr>
<tr>
<td valign="top" align="left"><p>drwxr-xr-x</p></td>
<td valign="top" align="left"><p>66</p></td>
<td valign="top" align="left"><p>root</p></td>
<td valign="top" align="left"><p>root</p></td>
<td valign="top" align="left"><p>4096</p></td>
<td valign="top" align="left"><p>may</p></td>
<td valign="top" align="left"><p>19</p></td>
<td valign="top" align="left"><p>00:26</p></td>
<td valign="top" align="left"><p>etc</p></td>
</tr>
<tr>
<td valign="top" align="left"><p>drwxrwsr-x</p></td>
<td valign="top" align="left"><p>7</p></td>
<td valign="top" align="left"><p>root</p></td>
<td valign="top" align="left"><p>staff</p></td>
<td valign="top" align="left"><p>4096</p></td>
<td valign="top" align="left"><p>abr</p></td>
<td valign="top" align="left"><p>16</p></td>
<td valign="top" align="left"><p>17:36</p></td>
<td valign="top" align="left"><p>home</p></td>
</tr>
<tr>
<td valign="top" align="left"><p>drwxr-xr-x</p></td>
<td valign="top" align="left"><p>6</p></td>
<td valign="top" align="left"><p>root</p></td>
<td valign="top" align="left"><p>root</p></td>
<td valign="top" align="left"><p>4096</p></td>
<td valign="top" align="left"><p>feb</p></td>
<td valign="top" align="left"><p>1</p></td>
<td valign="top" align="left"><p>18:02</p></td>
<td valign="top" align="left"><p>lib</p></td>
</tr>
<tr>
<td valign="top" align="left"><p>drwxr-xr-x</p></td>
<td valign="top" align="left"><p>2</p></td>
<td valign="top" align="left"><p>root</p></td>
<td valign="top" align="left"><p>root</p></td>
<td valign="top" align="left"><p>16384</p></td>
<td valign="top" align="left"><p>nov</p></td>
<td valign="top" align="left"><p>7</p></td>
<td valign="top" align="left"><p>2000</p></td>
<td valign="top" align="left"><p>lost+found</p></td>
</tr>
<tr>
<td valign="top" align="left"><p>dr-xr-xr-x</p></td>
<td valign="top" align="left"><p>2</p></td>
<td valign="top" align="left"><p>root</p></td>
<td valign="top" align="left"><p>root</p></td>
<td valign="top" align="left"><p>4096</p></td>
<td valign="top" align="left"><p>nov</p></td>
<td valign="top" align="left"><p>10</p></td>
<td valign="top" align="left"><p>2000</p></td>
<td valign="top" align="left"><p>mix</p></td>
</tr>
<tr>
<td valign="top" align="left"><p>dr-xr-xr-x</p></td>
<td valign="top" align="left"><p>67</p></td>
<td valign="top" align="left"><p>root</p></td>
<td valign="top" align="left"><p>root</p></td>
<td valign="top" align="left"><p>0</p></td>
<td valign="top" align="left"><p>may</p></td>
<td valign="top" align="left"><p>19</p></td>
<td valign="top" align="left"><p>02:25</p></td>
<td valign="top" align="left"><p>proc</p></td>
</tr>
<tr>
<td valign="top" align="left"><p>drwxr-xr-x</p></td>
<td valign="top" align="left"><p>14</p></td>
<td valign="top" align="left"><p>root</p></td>
<td valign="top" align="left"><p>root</p></td>
<td valign="top" align="left"><p>4096</p></td>
<td valign="top" align="left"><p>feb</p></td>
<td valign="top" align="left"><p>12</p></td>
<td valign="top" align="left"><p>19:28</p></td>
<td valign="top" align="left"><p>root</p></td>
</tr>
<tr>
<td valign="top" align="left"><p>drwxr-xr-x</p></td>
<td valign="top" align="left"><p>2</p></td>
<td valign="top" align="left"><p>root</p></td>
<td valign="top" align="left"><p>root</p></td>
<td valign="top" align="left"><p>4096</p></td>
<td valign="top" align="left"><p>ene</p></td>
<td valign="top" align="left"><p>30</p></td>
<td valign="top" align="left"><p>20:30</p></td>
<td valign="top" align="left"><p>sbin</p></td>
</tr>
<tr>
<td valign="top" align="left"><p>drwxrwxrwt</p></td>
<td valign="top" align="left"><p>9</p></td>
<td valign="top" align="left"><p>root</p></td>
<td valign="top" align="left"><p>root</p></td>
<td valign="top" align="left"><p>4096</p></td>
<td valign="top" align="left"><p>may</p></td>
<td valign="top" align="left"><p>22</p></td>
<td valign="top" align="left"><p>10:19</p></td>
<td valign="top" align="left"><p>tmp</p></td>
</tr>
<tr>
<td valign="top" align="left"><p>drwxr-xr-x</p></td>
<td valign="top" align="left"><p>15</p></td>
<td valign="top" align="left"><p>root</p></td>
<td valign="top" align="left"><p>root</p></td>
<td valign="top" align="left"><p>4096</p></td>
<td valign="top" align="left"><p>nov</p></td>
<td valign="top" align="left"><p>8</p></td>
<td valign="top" align="left"><p>2000</p></td>
<td valign="top" align="left"><p>usr</p></td>
</tr>
<tr>
<td valign="top" align="left"><p>drwxr-xr-x</p></td>
<td valign="top" align="left"><p>16</p></td>
<td valign="top" align="left"><p>root</p></td>
<td valign="top" align="left"><p>root</p></td>
<td valign="top" align="left"><p>4096</p></td>
<td valign="top" align="left"><p>nov</p></td>
<td valign="top" align="left"><p>9</p></td>
<td valign="top" align="left"><p>2000</p></td>
<td valign="top" align="left"><p>var</p></td>
</tr>
</tbody>
</table>
</table-wrap>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>La estructura de todos los Unix se <italic>parece</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>La estructura de todas las distribuciones Linux se <italic>parece mucho</italic></p></list-item>
</list>
</sec>
<sec id="c13-s1-3">
<title><bold>Jerarq&#x00FA;&#x0131;a cl&#x00E1;sica</bold></title>
<p>La jerarq&#x00FA;&#x0131;a actual puede resultar algo il&#x00F3;gica, pero hay motivos hist&#x00F3;ricos En los primeros Unix los discos eran m&#x00E1;s peque&#x00F1;os y m&#x00E1;s caros,</p>
<p>en uno estaba lo <italic>imprescindible</italic> para que el sistema funcionase:</p>
<preformat>
/
/etc
/lib
/tmp
/bin
/root
</preformat>
<p>y en un segundo disco, se montaba /usr</p>
<preformat>
/usr/spool
/usr/bin
/usr/include
/usr/tmp
/usr/adrn
/usr/lib
</preformat>
</sec></sec>
<sec id="c13-s2">
<label><bold>13.2.</bold></label>
<title><target target-type="page" id="pges_207"/><bold>FHS Filesystem Hierarchy Standard</bold></title>
<sec id="c13-s2-1">
<title><bold>FHS Filesystem Hierarchy Standard</bold></title>
<p>Est&#x00E1;ndar propuesto para todos los Linux y para los UNIX que quieran unirse. A&#x00F1;o 1994. Versi&#x00F3;n actual: 3.0 (junio 2015)</p>
<p>Dos criterios</p>
<p>&#x00BF;Un fichero puede almacenarse en una m&#x00E1;quina y usarse en otra?</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>S&#x00ED;: Compartibles. (<italic>shareable</italic>)</p></list-item>
<list-item><label>&#x25FE;</label> <p>No: No compartibles. (<italic>unshareable</italic>)</p></list-item>
</list>
<p>&#x00BF;Un fichero puede cambiar sin intervenci&#x00F3;n del administrador?</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>S&#x00ED;: Din&#x00E1;micos.</p></list-item>
<list-item><label>&#x25FE;</label> <p>No: Est&#x00E1;ticos. Pueden almacenarse el modo s&#x00F3;lo-lectura. Copias de seguridad menos frecuentes</p></list-item>
</list>
<list list-type="order">
<list-item><p>Directorios de usuarios</p></list-item>
<list-item><p>Programas (incluyendo mandatos y librer&#x00ED;as)</p></list-item>
<list-item><p>Configuraci&#x00F3;n del sistema</p></list-item>
<list-item><p>El Hardware</p></list-item>
<list-item><p>Documentaci&#x00F3;n</p></list-item>
<list-item><p>Ficheros Temporales</p></list-item>
<list-item><p>Otros directorios relacionados con el S.O.</p></list-item>
<list-item><p>Puntos de montaje</p></list-item>
</list>
</sec>
<sec id="c13-s2-s1">
<label><bold>13.2.1.</bold></label>
<title><bold>Directorios de usuarios</bold></title>
<sec id="c13-s2-s1-1">
<title><bold>Directorios de usuarios</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Directorio del administrador
<preformat>
/root
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_208"/>Usuarios locales
<preformat>
/home/jperez
</preformat></p>
<p>o bien</p>
<p><code>/home/profesores</code></p>
<p><code>/home/alumnos</code></p></list-item>
</list>
</sec></sec>
<sec id="c13-s2-s2">
<label><bold>13.2.2.</bold></label>
<title><bold>Programas y mandatos</bold></title>
<sec id="c13-s2-s2-1">
<title><bold>Programas y mandatos</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Mandatos &#x00FA;tiles para todos los usuarios</p>
<p>/bin</p>
<p>/usr/bin</p></list-item>
<list-item><label>&#x25FE;</label> <p>Mandatos &#x00FA;tiles para el root</p>
<p>/sbin</p>
<p>/usr/sbin</p></list-item>
</list>
<p>(Todo lo que haya bajo <code>/usr</code> deber&#x00ED;a ser s&#x00F3;lo lectura)</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Programas</p>
<list list-type="bullet">
<list-item><p>Software no incluido en la distribuci&#x00F3;n Linux</p>
<p>/usr/local</p></list-item>
<list-item><p>Grandes aplicaciones en la distribuci&#x00F3;n</p>
<p>/opt</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Librer&#x00ED;as est&#x00E1;ticas y din&#x00E1;micas</p>
<p>/lib</p>
<p>/usr/lib</p>
<p>/usr/local/lib</p></list-item>
<list-item><label>&#x25FE;</label> <p>Ficheros de cabecera (para compilar)</p>
<p>/usr/include</p></list-item>
<list-item><label>&#x25FE;</label> <p>Ficheros independientes de la arquitectura</p>
<p>/usr/share</p></list-item>
</list>
</sec></sec>
<sec id="c13-s2-s3">
<label><bold>13.2.3.</bold></label>
<title><target target-type="page" id="pges_209"/><bold>Configuraci&#x00F3;n del sistema</bold></title>
<sec id="c13-s2-s3-1">
<title><bold>Configuraci&#x00F3;n del sistema</bold></title>
<p>Directorio <code>/etc</code></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Informaci&#x00F3;n sobre el sistema de ficheros (puntos de montaje, opciones)</p>
<p>/etc/fstab</p></list-item>
<list-item><label>&#x25FE;</label> <p>cuentas de usuarios</p>
<p>/etc/passwd</p></list-item>
<list-item><label>&#x25FE;</label> <p>Passwords de los usuarios
<preformat>
/etc/shadow
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Scripts para arranque del sistema</p>
<p>/etc/init.d</p></list-item>
<list-item><label>&#x25FE;</label> <p>...</p></list-item>
</list>
</sec></sec>
<sec id="c13-s2-s4">
<label><bold>13.2.4.</bold></label>
<title><bold>El Hardware</bold></title>
<sec id="c13-s2-s4-1">
<title><bold>El Hardware</bold></title>
<p>Los dispositivos del sistema <code>/dev</code></p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>/dev/hda</code></p></td>
<td valign="top" align="left"><p><code>IDE primario master</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/dev/hdb</code></p></td>
<td valign="top" align="left"><p><code>IDE primario slave</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/dev/hdc</code></p></td>
<td valign="top" align="left"><p><code>IDE secundario master</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/dev/hdd</code></p></td>
<td valign="top" align="left"><p><code>IDE secundario slave</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/dev/hda1</code></p></td>
<td valign="top" align="left"><p><code>Primera partici&#x00F3;n primaria del hda</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/dev/hda2</code></p></td>
<td valign="top" align="left"><p><code>...</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/dev/sda</code></p></td>
<td valign="top" align="left"><p><code>Primer disco SCSI</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/dev/sdb</code></p></td>
<td valign="top" align="left"><p><code>Segundo disco SCSI</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/dev/sda1</code></p></td>
<td valign="top" align="left"><p><code>...</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/dev/nvme0n1</code></p></td>
<td valign="top" align="left"><p><code>Primer disco nvme</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/dev/nvme0n2</code></p></td>
<td valign="top" align="left"><p><code>Segundo disco nvme</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/dev/nvme0n1p1</code></p></td>
<td valign="top" align="left"><p><code>Primera partici&#x00F3;n del primer disco nvme</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" colspan="2"><p><code>/dev/cdrom</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/dev/fd0</code></p></td>
<td valign="top" align="left"><p><code>disquete</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/dev/audio</code></p></td>
<td valign="top" align="left"><p><code>tarjeta sonido</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/dev/modem</code></p></td>
<td valign="top" align="left"><p></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/dev/mouse</code></p></td>
<td valign="top" align="left"><p></p></td>
</tr>
<tr>
<td valign="top" align="left" colspan="2"><p><code>/dev/input/mouse0</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/dev/ttyN</code></p></td>
<td valign="top" align="left"><p><code>donde N es el <sup>n&#x00B0;</sup> de consola (no gr&#x00E1;fica)</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/dev/pts/N</code></p></td>
<td valign="top" align="left"><p><code>Consola (X Window)</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<p><target target-type="page" id="pges_210"/>El est&#x00E1;ndar no dice mucho sobre /dev, es bastante variable</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Ficheros <italic>virtuales</italic> que representan las estructuras del Kernel en ejecuci&#x00F3;n, dan informaci&#x00F3;n sobre la cpu...</p></list-item>
</list>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>/proc/cpuinfo</code></p></td>
<td valign="top" align="left"><p><code>CPU</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/proc/pci</code></p></td>
<td valign="top" align="left"><p><code>Tarjetas PCI</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/proc/ioports</code></p></td>
<td valign="top" align="left"><p><code>Puertos I/O</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/proc/meminfo</code></p></td>
<td valign="top" align="left"><p><code>Informaci&#x00F3;n sobre la memoria</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/proc/NN</code></p></td>
<td valign="top" align="left"><p><code>Informaci&#x00F3;n sobre el proceso de pid NN</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<p>Los directorios <code>/proc</code> y <code>/sys</code> no se corresponden con discos f&#x00ED;sicos, sino que son un medio de enviar y recibir informaci&#x00F3;n directamente del <italic>kernel</italic>.</p>
<p>Cuando se lee o se escribe alg&#x00FA;n fichero del <code>/proc</code>, se est&#x00E1; pidiendo o recibiendo informaci&#x00F3;n del kernel</p>
</sec></sec>
<sec id="c13-s2-s5">
<label><bold>13.2.5.</bold></label>
<title><bold>Documentaci&#x00F3;n</bold></title>
<sec id="c13-s2-s5-1">
<title><bold>Documentaci&#x00F3;n</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>/usr/share/doc</p>
<p>Documentaci&#x00F3;n sobre el software del sistema</p></list-item>
<list-item><label>&#x25FE;</label> <p>/usr/man</p>
<p>Ficheros del mandato <italic>man</italic></p></list-item>
</list>
</sec></sec>
<sec id="c13-s2-s6">
<label><bold>13.2.6.</bold></label>
<title><bold>Ficheros Temporales</bold></title>
<sec id="c13-s2-s6-1">
<title><bold>Ficheros Temporales</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Ficheros temporales</p>
<p>(se borran cuando la m&#x00E1;quina arranca)</p>
<p><code>/tmp</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>Fragmentos de ficheros recuperados
<preformat>
/lost+found
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Ficheros que cambian con frecuencia, de aplicaciones
<preformat>
/var
</preformat></p></list-item>
</list>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><target target-type="page" id="pges_211"/><code>/var/log/syslog</code></p></td>
<td valign="top" align="left"><p><code>bit&#x00E1;cora principal del sistema</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/var/log/messages</code></p></td>
<td valign="top" align="left"><p><code>otra bit&#x00E1;cora con diversos mensajes</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/var/log/dmesg</code></p></td>
<td valign="top" align="left"><p><code>mensajes del sistema al arrancar</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/var/spool/lpd/lp</code></p></td>
<td valign="top" align="left"><p><code>spool de la impresora</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/var/tmp</code></p></td>
<td valign="top" align="left"><p><code>Ficheros temporales</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/var/mail</code></p></td>
<td valign="top" align="left"><p><code>Correo de los usuarios</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Ficheros del sistema que cambian con frecuencia
<preformat>
/run
</preformat></p>
<p>Esta es la principal novedad respecto a la versi&#x00F3;n anterior, ( FHS 2.3, a&#x00F1;o 2004). El directorio equivalente a este era <code>/var/run</code>. Resultaba problem&#x00E1;tico porque <code>/var/run</code> normalmente no estaba disponible durante el arranque (/var no es especialmente importante, pod&#x00ED;a estar en una partici&#x00F3;n distinta)</p></list-item>
</list>
</sec></sec>
<sec id="c13-s2-s7">
<label><bold>13.2.7.</bold></label>
<title><bold>Otros directorios relacionados con el S.O.</bold></title>
<sec id="c13-s2-s7-1">
<title><bold>Otros directorios relacionados con el S.O.</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>/boot</code></p>
<p>Todo lo requerido para el arranque, antes de que el sistema ejecute programas de usuario</p></list-item>
<list-item><label>&#x25FE;</label> <p>C&#x00F3;digo fuente</p>
<list list-type="bullet">
<list-item><p>C&#x00F3;digo fuente del software de sistema
<preformat>
/usr/src
</preformat></p></list-item>
<list-item><p>C&#x00F3;digo fuente del kernel linux
<preformat>
/usr/src/linux
</preformat></p></list-item>
</list></list-item>
</list>
</sec></sec>
<sec id="c13-s2-s8">
<label><bold>13.2.8.</bold></label>
<title><bold>Puntos de Montaje</bold></title>
<sec id="c13-s2-s8-1">
<title><bold>Puntos de Montaje</bold></title>
<p>Unidades extraibles: Disquetes, cdrom, <italic>pendrives</italic></p>
<p>Sol&#x00ED;an colocarse en el raiz p.e. <code>/cdrom.</code> Pero esto llena el raiz de directorios En FHS 2.3 (a&#x00F1;o 2004) aparece <code>/media</code></p>
<p><code>/media/cdrom /media/cdrecorder /media/zip /media/floppy</code></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Si solo hay uno de un tipo:
<preformat>
/media/cdrom
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_212"/>Si hay m&#x00E1;s de uno del mismo tipo
<preformat>
/media/cdrom0
</preformat></p>
<p><code>/media/cdrom1</code></p>
<p><code>/media/cdrom -&#x003E; /media/cdrom1</code></p></list-item>
</list>
<preformat>
/mnt
</preformat>
<p>Directorio vac&#x00ED;o para que el administrador monte un sistema de ficheros que necesita temporalmente. Los programas no deber&#x00ED;an usarlo</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>/mnt/cdrom</code> &#x00A1;No es est&#x00E1;ndar!</p>
<p>Es una costumbre reciente, va contra el est&#x00E1;ndar. Dentro de /mnt debe estar directamente el sistema de ficheros temporal, sin subdirectorios</p></list-item>
</list>
</sec></sec>
<sec id="c13-s3">
<label><bold>13.3.</bold></label>
<title><bold>Montaje de sistemas de ficheros</bold></title>
<sec id="c13-s3-1">
<title><bold>Montaje de sistemas de ficheros</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Normalmente, no todos los ficheros del &#x00E1;rbol de directorios se encuentran en el mismo disco.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Punto de montaje: directorio que pertenece a un disco (o partici&#x00F3;n) distinto, junto con todo su contenido (excluyendo otros puntos de montaje).</p></list-item>
<list-item><label>&#x25FE;</label> <p>Se pueden consultar los puntos de montaje junto con los discos o particiones que est&#x00E1;n montadas en ellos con las &#x00F3;rdenes mount y df</p></list-item>
</list>
</sec>
<sec id="c13-s3-s1">
<label><bold>13.3.1.</bold></label>
<title><bold>mount, df, lsblk</bold></title>
<sec id="c13-s3-s1-1">
<title><bold>mount, df, lsblk</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>mount:</code> Muestra las particiones, puntos de montaje, tipo de partici&#x00F3;n y opciones de cada una de ellas:
<preformat>
/dev/hda2 on / type ext3 (rw,noatime
proc on /proc type proc (rw)
sysfs on /sys type sysfs (rw)
devpts on /dev/pts type devpts (rw,gid=5,mode=620)
/dev/hda5 on /scratch type ext3 (ro,noatime)
tmpfs on /tmp type tmpfs (rw)
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_213"/><code>df:</code> Muestra cada una de las particiones <italic>con ficheros reales</italic> montadas en el sistema, el punto en el que est&#x00E1; montada, su capacidad y su uso:</p></list-item>
</list>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>Filesystem</code></p></td>
<td valign="top" align="right"><p><code>1K-blocks</code></p></td>
<td valign="top" align="right"><p><code>Used</code></p></td>
<td valign="top" align="right"><p><code>Available</code></p></td>
<td valign="top" align="right"><p><code>Use%</code></p></td>
<td valign="top" align="right"><p><code>Mounted on</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/dev/hda2</code></p></td>
<td valign="top" align="right"><p><code>28842780</code></p></td>
<td valign="top" align="right"><p><code>6957692</code></p></td>
<td valign="top" align="right"><p><code>20419960</code></p></td>
<td valign="top" align="right"><p><code>26%</code></p></td>
<td valign="top" align="right"><p><code>/</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/dev/hda5</code></p></td>
<td valign="top" align="right"><p><code>38448276</code></p></td>
<td valign="top" align="right"><p><code>32838556</code></p></td>
<td valign="top" align="right"><p><code>3656620</code></p></td>
<td valign="top" align="right"><p><code>90%</code></p></td>
<td valign="top" align="right"><p><code>/scratch</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>tmpfs</code></p></td>
<td valign="top" align="right"><p><code>517960</code></p></td>
<td valign="top" align="right"><p><code>1196</code></p></td>
<td valign="top" align="right"><p><code>516764</code></p></td>
<td valign="top" align="right"><p><code>1%</code></p></td>
<td valign="top" align="right"><p><code>/tmp</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>lsblk -f:</code> Muestra cada disco (dispositivo de bloques)</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>blkid:</code> Muestra cada disco, aunque no est&#x00E9; montado</p></list-item>
</list>
<p>Para montar un sistema de ficheros</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Crear el directorio si no existe:
<preformat>
mkdir /var
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Hacer visible el sistema de ficheros bajo ese directorio:
<preformat>
mount -t ext2 -o rw /dev/hda3 /var
</preformat></p>
<p>(es m&#x00E1;s habitual indicar las opciones en <code>/etc/fstab</code>)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Si queremos desmontar (o hacer invisible) un sistema de ficheros que est&#x00E9; montado en el directorio <code>/var:</code>
<preformat>
umount /var
</preformat></p></list-item>
</list>
</sec></sec>
<sec id="c13-s3-s2">
<label><bold>13.3.2.</bold></label>
<title><bold>El fichero /etc/fstab</bold></title>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code># &#x003C;filesystem&#x003E;</code></p></td>
<td valign="top" align="left"><p><code>&#x003C;mount point&#x003E;</code></p></td>
<td valign="top" align="left"><p><code>&#x003C;type&#x003E;</code></p></td>
<td valign="top" align="left"><p><code>&#x003C;options&#x003E;</code></p></td>
<td valign="top" align="left"><p><code>&#x003C;dump&#x003E;</code></p></td>
<td valign="top" align="left"><p><code>&#x003C;pass&#x003E;</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>proc</code></p></td>
<td valign="top" align="left"><p><code>/proc</code></p></td>
<td valign="top" align="left"><p><code>proc</code></p></td>
<td valign="top" align="left"><p><code>defaults</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/dev/hda2</code></p></td>
<td valign="top" align="left"><p><code>/</code></p></td>
<td valign="top" align="left"><p><code>ext3</code></p></td>
<td valign="top" align="left"><p><code>noatime</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
<td valign="top" align="left"><p><code>1</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/dev/hda5</code></p></td>
<td valign="top" align="left"><p><code>/scratch</code></p></td>
<td valign="top" align="left"><p><code>ext3</code></p></td>
<td valign="top" align="left"><p><code>noatime,ro</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
<td valign="top" align="left"><p><code>1</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/dev/hda6</code></p></td>
<td valign="top" align="left"><p><code>none</code></p></td>
<td valign="top" align="left"><p><code>swap</code></p></td>
<td valign="top" align="left"><p><code>sw</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>tmpfs</code></p></td>
<td valign="top" align="left"><p><code>/tmp</code></p></td>
<td valign="top" align="left"><p><code>tmpfs</code></p></td>
<td valign="top" align="left"><p><code>defaults</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/dev/sda1</code></p></td>
<td valign="top" align="left"><p><code>/media/pendrive</code></p></td>
<td valign="top" align="left"><p><code>vfat</code></p></td>
<td valign="top" align="left"><p><code>defaults,user,noauto</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>mount -a</code> monta todo lo indicado en este fichero</p></list-item>
<list-item><label>&#x25FE;</label> <p>En el arranque se ejecuta <code>mount -a</code></p></list-item>
<list-item><label>&#x25FE;</label> <p><code>mount /media/pendrive</code></p>
<p>monta el pendrive con todas las opciones indicadas en fstab</p></list-item>
</list>
<p><code>&#x003C;dump&#x003E;</code> &#x00BF;Incluir en las copias de seguridad hechas con <italic>dump</italic>? (Normalmente no)</p>
<p><target target-type="page" id="pges_214"/><code>&#x003C;pass&#x003E;</code> Orden para el <code>fsck</code> del arranque (0: desactivado).</p>
<preformat>
&#x003C;options&#x003E;
</preformat>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>rw:</code> Permisos de lectura y escritura.</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>ro:</code> S&#x00F3;lo lectura.</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>auto/noauto:</code> &#x00BF;Montar autom&#x00E1;ticamente con <code>mount -a</code>?</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>user/nouser:</code> &#x00BF;Los usuarios normales pueden montar y desmontar? (o hace falta ser <code>root</code>)</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>exec/noexec:</code> &#x00BF;Se pueden ejecutar binarios?</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>sync:</code> Al modificar un fichero, se escribe f&#x00ED;sicamente de inmediato</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>async:</code> Se usan buffers</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>defaults: rw, suid, dev, exec, auto, nouser, async</code></p></list-item>
<list-item><label>&#x25FE;</label> <p><code>. . .</code></p></list-item>
</list>
</sec>
<sec id="c13-s3-s3">
<label><bold>13.3.3.</bold></label>
<title><bold>Tipos de sistemas de ficheros</bold></title>
<sec id="c13-s3-s3-1">
<title><bold>Tipos de sistemas de ficheros</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Tradicionales</p>
<list list-type="bullet">
<list-item><p><code>msdos:</code> El usado por MS-DOS y Windows pre-95, sin permisos ni due&#x00F1;os, nombres de fichero de 8 caracteres con extensiones de 3 caracteres</p></list-item>
<list-item><p><code>vfat:</code> Usado a partir de Windows-95, compatible con MS-DOS pero con posibilidad de nombres de fichero largos</p></list-item>
<list-item><p><code>ntfs:</code> Desde Windows NT hasta Windows XP. A&#x00F1;ade caracter&#x00ED;sticas de seguridad (permisos, due&#x00F1;os, etc). Los primeros <italic>drivers</italic> para Linux ten&#x00ED;an limitaciones, en la actualidad se puede leer y escribir con normalidad</p></list-item>
<list-item><p><code>iso9660:</code> Sistema de fichero utilizado en los CDs de datos</p></list-item>
<list-item><p><code>minix:</code> usado por MINIX y por los primeros Linux</p></list-item>
<list-item><p><code>ext2:</code> Sistema de ficheros tradicional en Linux</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_215"/>Con <italic>journal</italic></p>
<list list-type="bullet">
<list-item><p><code>ext4:</code> sucesor de <code>ext2</code> y <code>ext3</code>. El m&#x00E1;s utilizado actualmente</p></list-item>
<list-item><p><code>reiserfs, jfs, xfs</code></p>
<p>Mejores prestaciones, pero incompatibles con <code>ext2</code></p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Con caracter&#x00ED;sticas especiales :
<preformat>
romfs, cramfs, autofs, umsdos
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>No asociados a dispositivo
<preformat>
proc, sysfs, devfs, devpts, tmpfs, ramfs, usbfs
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Remotos:</p>
<list list-type="bullet">
<list-item><p><code>nfs:</code> <italic>Network File System</italic>, desarrollado por SUN, el m&#x00E1;s usado entre los sistemas ficheros remotos en UNIX</p></list-item>
<list-item><p><code>smb/cifs:</code> Sistema de ficheros remotos usado por Microsoft</p></list-item>
<list-item><p><code>ncp:</code> <italic>Netwate Core Protocol</italic>, protocolo sobre IPX para montar sistemas de ficheros de Novell Netware</p></list-item>
<list-item><p><code>sshfs:</code> <italic>Secure SHell FileSystem</italic>, protocolo basado en ssh</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Soporte de otras plataformas:</p>
<p><code>hfs</code> (Apple Macintosh), <code>bfs</code> (<italic>Boot File System</italic>, SCO), <code>efs</code> (SGI, IRIX), <code>jffs</code> (<italic>Journaling Flash File System</italic>), <code>hpfs</code> (OS/2), <code>qnx4, sysv</code> (System V), <code>ufs</code> (SunOS, FreeBSD, NetBSD, OpenBSD). . .</p></list-item>
</list>
</sec></sec>
<sec id="c13-s3-s4">
<label><bold>13.3.4.</bold></label>
<title><bold>Sistemas de Ficheros en Espacio de usuario Sistemas de Ficheros en Espacio de usuario</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Los sistemas de ficheros tradicionales est&#x00E1;n implementados en el n&#x00FA;cleo. A&#x00F1;adir uno sistema de ficheros es complicado, y puede comprometer la integridad del sistema.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Los sistemas de ficheros en espacio de usuario son aplicaciones <italic>normales</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>Para Linux, FreeBSD, NetBSD, OpenSolaris y Mac OS X exite FUSE <italic>Filesystem in Userspace</italic>. Es un m&#x00F3;dulo del n&#x00FA;cleo que act&#x00FA;a de puente entre el n&#x00FA;cleo y el c&#x00F3;digo del sistema de ficheros</p></list-item>
</list>
<p><target target-type="page" id="pges_216"/>Ejemplos de sistemas de ficheros FUSE</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>sshfs</p></list-item>
<list-item><label>&#x25FE;</label> <p>GmailFS. Almacena los datos sobre correos de gmail. No es fiable porque no est&#x00E1; aprobado por google. (Tampoco prohibido, al menos expl&#x00ED;citamente)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Acceso a ficheros empaquetados (tgz, zip, etc)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Almacenamiento en Bases de Datos</p></list-item>
<list-item><label>&#x25FE;</label> <p>Encriptaci&#x00F3;n</p></list-item>
<list-item><label>&#x25FE;</label> <p>Hardware poco com&#x00FA;n</p></list-item>
<list-item><label>&#x25FE;</label> <p>Sistemas de versiones de ficheros (CVS, SVN...)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Monitorizaci&#x00F3;n de sistemas de ficheros</p></list-item>
</list>
</sec>
<sec id="c13-s3-s5">
<label><bold>13.3.5.</bold></label>
<title><bold>frametitle</bold></title>
<p></p></sec>
<sec id="c13-s3-s6">
<label><bold>13.3.6.</bold></label>
<title><bold>sshfs</bold></title>
<p>Secure SHell FileSystem. Basado en FUSE. Sistema de ficheros de red</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Menos eficiente pero m&#x00E1;s seguro que NFS</p></list-item>
<list-item><label>&#x25FE;</label> <p>En el servidor basta disponer del demonio ssh convencional</p></list-item>
<list-item><label>&#x25FE;</label> <p>En el cliente basta instalar el paquete <code>sshfs</code></p></list-item>
</list>
<p>Montar el <italic>home</italic> remoto:</p>
<p><code>sshfs -C usuario@maquina: /punto/de/montaje</code></p>
<p>Montar un directorio remoto</p>
<p><code>sshfs -C usuario@maquina:/un/directorio /punto/de/montaje</code></p>
<p>Desmontar:</p>
<p><code>fusermount -u /punto/de/montaje</code></p>
</sec>
<sec id="c13-s3-s6-1">
<title><target target-type="page" id="pges_217"/><bold>upstart</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>El sistema de arranque tradicional de Linux (System V) no es adecuado para las m&#x00E1;quinas actuales</p>
<list list-type="bullet">
<list-item><p>Son externos: aparecen y desaparecen</p></list-item>
<list-item><p>Est&#x00E1;n en red</p></list-item>
<list-item><p>Ahorran energ&#x00ED;a</p></list-item>
<list-item><p>. . .</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p><italic>Upstart</italic> es un sistema de arranque basado en eventos, desarrollado por Ubuntu, con el prop&#x00F3;sito de extenderlo a todos los Linux</p>
<p>Aparece en Ubuntu 6.10 <italic>edgy</italic> (Octubre de 2006)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Alternativas: launchd (macOS X), initng, SMF</p></list-item>
<list-item><label>&#x25FE;</label> <p>Est&#x00E1; previsto que reemplace a cron y tal vez a inetd, manteniendo siempre la compatibilidad</p></list-item>
</list>
<p>En <italic>upstart</italic> se modifica la columna &#x003C;filesystem&#x003E; de /etc/fstab, incorporando un <italic>Universally Unique Identifier</italic></p>
<preformat>
# &#x003C;file system&#x003E; &#x003C;mount point&#x003E; &#x003C;type&#x003E; &#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x003C;options&#x003E;&#x003C;dump&#x003E;&#x003C;pass&#x003E;
proc &#x00A0;&#x00A0;&#x00A0;/proc &#x00A0;&#x00A0;&#x00A0;proc &#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;defaults 0 0
UUID=e8a76033-f833-490d-8a55-ceca132c2ba7 / ext3 &#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;defaults,errors=remount-ro 0 1
UUID=e38c8abf-1af7-49be-bba5-bcf45dab8dc2 /home &#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;ext3 defaults 0 2
UUID=967cf88c-7b0b-42a9-bf93-deb7b710aad2 /media/sda6 &#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;ext3 defaults 0 2
UUID=f5c3bc51-7795-4bc9-b18e-4a16b7496e93 none &#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;swap sw 0 0
/dev/hda /media/cdrom0 &#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;udf,iso9660 user,noauto 0 0
</preformat>
</sec></sec>
</sec>
<sec id="c13-s4">
<label><bold>13.4.</bold></label>
<title><bold>Codificaci&#x00F3;n de caracteres</bold></title>
<sec id="c13-s4-1">
<title><bold>Codificaci&#x00F3;n de caracteres</bold></title>
<p>Correspondencia entre un car&#x00E1;cter de lenguaje natural y un s&#x00ED;mbolo en otro sistema de representaci&#x00F3;n. En inform&#x00E1;tica, uno o m&#x00E1;s octetos</p>
<p>A veces se llama <italic>code pages</italic> (IBM, Microsoft)</p>
</sec>
<sec id="c13-s4-s1">
<label><bold>13.4.1.</bold></label>
<title><target target-type="page" id="pges_218"/><bold>Codificaciones cl&#x00E1;sicas</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>EBCDIC: Extended Binary Coded Decimal Interchange Code. IBM, a&#x00F1;o 1963. 8 bits. Se usa en algunos equipos IBM. Diferentes versiones incompatibles entre s&#x00ED;</p></list-item>
<list-item><label>&#x25FE;</label> <p>ASCII: American Standard Code for Information Interchange. ANSI, American National Standards Institute, a&#x00F1;o 1963). 7 bits. Solo ingl&#x00E9;s</p></list-item>
</list>
</sec>
<sec id="c13-s4-s2">
<label><bold>13.4.2.</bold></label>
<title><bold>ASCII extendido</bold></title>
<sec id="c13-s4-s2-1">
<title><bold>ASCII extendido</bold></title>
<p>8 bits. Cada conjunto de idiomas necesita su propia variante. Compatible con ASCII</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Code Pages 437. Ingl&#x00E9;s. Primeros IBM PC, MS-DOS</p>
<p>Code Pages 850. Europa occidental. Primeros IBM PC, MS-DOS</p></list-item>
<list-item><label>&#x25FE;</label> <p>ISO-8859 (Organizaci&#x00F3;n Internacional para la Estandarizaci&#x00F3;n), a&#x00F1;o 1992. Habitual en linux hasta mediados de los a&#x00F1;os <italic>cerenta</italic></p>
<p>ISO-8859-1, informalmente conocido como Latin-1</p>
<p>ISO-8859-2 europa central, ISO-8859-5 cir&#x00ED;lico , ISO-8859-6 &#x00E1;rabe, ...</p>
<p>ISO-8859-15 o Latin-9. A&#x00F1;o 1998. Muy parecido a Latin-1, incluye el s&#x00ED;mbolo del euro</p></list-item>
<list-item><label>&#x25FE;</label> <p>windows-1252. Parecido a ISO-8859-1. Se confunden con frecuencia. Se empleaba en los primeros Windows</p></list-item>
</list>
</sec>
<sec id="c13-s4-s3">
<label><bold>13.4.3.</bold></label>
<title><bold>Unicode</bold></title>
<sec id="c13-s4-s3-1">
<title><bold>Unicode</bold></title>
<p>Est&#x00E1;ndar industrial. <italic>Unicode Consortium</italic>, a&#x00F1;o 1991. Compatible con ISO 10646.</p>
<p>Asocia un n&#x00FA;mero a cada car&#x00E1;cter empleado por alg&#x00FA;n lenguaje escrito del mundo. M&#x00E1;s de 100.000 caracteres</p>
<p><target target-type="page" id="pges_219"/>Se puede codificar de diferentes maneras</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>UTF-8 es la forma en Unix de codificar unicode.</p>
<p>Compatible con ASCII. Cada car&#x00E1;cter ocupa entre 1 y 4 octetos</p></list-item>
<list-item><label>&#x25FE;</label> <p>UTF-16. Cada car&#x00E1;cter ocupa entre 2 y 4 octetos.</p>
<p>Nativo en Windows desde Windows 2000, aunque se seg&#x00FA;&#x0131;a usando windows-1252.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Punycode. RFC 3492. Empleado en la Internacionalizaci&#x00F3;n de Nombres de Dominio en Aplicaciones (IDNA). A&#x00F1;os 2003-2005. Permite nombres de dominio en unicode.</p>
<p><table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>espan~a.es</code></p></td>
<td valign="top" align="left"><p>-&#x003E;</p></td>
<td valign="top" align="left"><p>xn--espaa-rta.es</p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>ortun~o.es</code></p></td>
<td valign="top" align="left"><p>-&#x003E;</p></td>
<td valign="top" align="left"><p>xn--ortuo-rta.es</p></td>
</tr>
</tbody>
</table>
</table-wrap></p></list-item>
<list-item><label>&#x25FE;</label> <p>UCS-2, UCS-4, SCSU, ...</p></list-item>
</list>
</sec></sec>
<sec id="c13-s4-s4">
<label><bold>13.4.4.</bold></label>
<title><bold>recode</bold></title>
<sec id="c13-s4-s4-1">
<title><bold>recode</bold></title>
<p>Orden que convierte ficheros entre diferentes codificaciones</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>recode utf-8</code></p>
<p>Lee <italic>stdin</italic>, convierte desde utf-8 hasta las locales actuales y escribe en <italic>stdout</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p><code>recode latin-1..utf-8</code></p>
<p>Lee <italic>stdin</italic>, convierte desde latin-1 hasta utf-8 y escribe en <italic>stdout</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p><code>recode utf-8..windows-1252 fichero</code></p>
<p>Modifica el fichero, convirtiendo desde utf-8 hasta windows-1252<target target-type="page" id="pges_220"/></p></list-item>
</list>
</sec></sec></sec>
</sec>
</body>
</book-part>
<book-part id="c14" book-part-type="chapter">
<book-part-meta>
<book-part-id book-part-id-type="publisher-id">URJC</book-part-id>
<title-group>
<label>14.</label>
<title><target target-type="page" id="pges_221"/>DEVOPS</title>
</title-group>
</book-part-meta>
<body>
<sec id="c14-s1">
<label><bold>14.1.</bold></label>
<title><bold>DevOps</bold></title>
<sec id="c14-s1-1">
<title><bold>Definici&#x00F3;n de DevOps</bold></title>
<p>DevOps es un t&#x00E9;rmino acu&#x00F1;ado por Andrew Shafer y Patrick Debois en la conferencia de desarrollo Agile del a&#x00F1;o 2008</p>
<p>Se origina con la composici&#x00F3;n de dos palabras:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><italic>Development</italic></p>
<p>Desarrollo, programaci&#x00F3;n de software en sentido amplio, esto es, an&#x00E1;lisis, dise&#x00F1;o, codificaci&#x00F3;n y prueba</p></list-item>
<list-item><label>&#x25FE;</label> <p><italic>Operations</italic></p>
<p>Operaciones, explotaci&#x00F3;n del software, puesta en producci&#x00F3;n, uso real</p></list-item>
</list>
<p>Definici&#x00F3;n de Bass, Weber y Zhu [<xref ref-type="bibr" rid="CIT004">4</xref>]:</p>
<p>DevOps is a set of practices intended to reduce the time between committing a change to a system and the change being placed into normal production, while ensuring high quality</p>
<p>Definici&#x00F3;n de Davis y Daniels [<xref ref-type="bibr" rid="CIT002">2</xref>]:</p>
<p>Devops is a cultural movement that changes how individuals think about their work, values the diversity of work done, supports intentional processes that accelerate the rate by which businesses realize value, and measures the effect of social and technical change. It is a way of thinking and a way of working that enables individuals and organizations to develop and maintain sustainable work practices. It is a cultural framework for sharing stories and developing empathy, enabling people and teams to practice their crafts in effective and lasting ways</p>
<p>Definici&#x00F3;n de Huttermann [<xref ref-type="bibr" rid="CIT001">1</xref>]:</p>
<p>DevOps is a mix of patterns intended to improve collaboration between development and operations. DevOps addresses shared goals and incentives as well as shared processes and tools. Because of the natural conflicts among <target target-type="page" id="pges_222"/>different groups, shared goals and incentives may not always be achievable. However, they should at least be aligned with one another</p>
<p>Aq&#x00FA;&#x0131; lo definiremos como</p>
<preformat>
Grupo de t&#x00E9;cnicas que buscan optimizar el trabajo
conjunto de desarrolladores de software y administradores de
sistemas
</preformat>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Optimizar: que sea r&#x00E1;pido, sencillo, barato, de calidad y sin conflictos</p>
<p>Estas <italic>t&#x00E9;cnicas</italic> se agrupan en dos grandes categor&#x00ED;as</p></list-item>
<list-item><label>&#x25FE;</label> <p>Culturales, pol&#x00ED;ticas, organizativas. Referidas a la interacci&#x00F3;n entre personas</p></list-item>
<list-item><label>&#x25FE;</label> <p>Herramientas software</p></list-item>
</list>
<p>Las segundas son importantes, pero las primeras, m&#x00E1;s</p>
<p>Hay algunas cosas relativamente claras:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Qu&#x00E9; es <italic>DevOps</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>Qu&#x00E9; no es <italic>DevOps</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>Qu&#x00E9; problemas se quieren solucionar</p></list-item>
<list-item><label>&#x25FE;</label> <p>Qu&#x00E9; objetivos se buscan</p></list-item>
<list-item><label>&#x25FE;</label> <p>Lo relativo a herramientas software</p></list-item>
</list>
<p>Lo que no est&#x00E1; tan claro es <italic>c&#x00F3;mo</italic>. <italic>DevOps</italic> es una disciplina compleja</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Organizar el trabajo de personas es dif&#x00ED;cil. No se aprende en un libro. Lo que puede valer en un entorno, puede ser inaplicable en otro</p></list-item>
</list>
</sec>
<sec id="c14-s1-2">
<title><bold>&#x00BF;Qu&#x00E9; NO es DevOps? (1)</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><italic>DevOps</italic> no significa que desaparezca la frontera entre desarrollo y producci&#x00F3;n</p></list-item>
<list-item><label>&#x25FE;</label> <p>No significa que los desarrolladores controlen el software en producci&#x00F3;n</p></list-item>
<list-item><label>&#x25FE;</label> <p>No significa que los administradores editen el c&#x00F3;digo fuente</p>
<p><target target-type="page" id="pges_223"/>Seg&#x00FA;n la bibliograf&#x00ED;a especializada, DevOps nunca deber&#x00ED;a ser un departamento en una empresa, ni un cargo. No tiene sentido hablar de Ingeniero DevOps</p>
<list list-type="bullet">
<list-item><p>Aunque la industria s&#x00ED; usa este t&#x00E9;rmino</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>No significa que una sola persona desarrolle y opere</p>
<list list-type="bullet">
<list-item><p>Excepto tal vez en empresas muy peque&#x00F1;as</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>No significa que una persona trabaje por dos (y cobre por una)</p></list-item>
</list>
</sec>
<sec id="c14-s1-3">
<title><bold>&#x00BF;Qu&#x00E9; NO es DevOps? (2)</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><italic>DevOps</italic> no es una herramienta software. Tienen su utilidad, pero ni son suficientes ni son imprescindibles</p></list-item>
<list-item><label>&#x25FE;</label> <p><italic>DevOps</italic> no es una certificaci&#x00F3;n. No es una metodolog&#x00ED;a concreta y &#x00FA;nica que se pueda ense&#x00F1;ar, que se pueda seguir y de la que uno se pueda examinar</p></list-item>
</list>
<p>Con frecuencia, en una ofertas de empleo donde se solicita un <italic>ingeniero devops</italic>, realmente lo que se est&#x00E1; buscando es un desarrollador con conocimientos de integraci&#x00F3;n continua/entrega continua/despliegue continuo.</p>
<fig>
<caption><title>Word Cloud para DevOps en ofertas de empleo</title></caption>
<graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-23.jpg"/>
<attrib>Fuente: <ext-link ext-link-type="uri" xlink:href="https://www.dataset.com/2017/08/devops-job-market">The DevOps Job Market, Scalyr blog</ext-link></attrib>
</fig>
</sec></sec>
<sec id="c14-s2">
<label><bold>14.2.</bold></label>
<title><target target-type="page" id="pges_224"/><bold>Desarrollo &#x00C1;gil</bold></title>
<sec id="c14-s2-1">
<title><bold>Desarrollo &#x00C1;gil</bold></title>
<p>DevOps est&#x00E1; muy ligado con el desarrollo de software agile (&#x00E1;gil ), proviene de la misma comunidad, tiene objetivos muy similares</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Podemos considerar DevOps una extensi&#x00F3;n del moviento agil a la explotaci&#x00F3;n del software, no solo a su desarrollo</p></list-item>
</list>
</sec>
<sec id="c14-s2-2">
<title><bold>Modelo de desarrollo de software en cascada</bold></title>
<p>El desarrollo en cascada (<italic>waterfall</italic> ) es el tradicional, generalmente aceptado y pr&#x00E1;cticamente &#x00FA;nico hasta los a&#x00F1;os 1990.</p>
<p>Formado por pasos que se siguen secuencialmente, de forma r&#x00ED;gida, uno tras otro, sin vuelta atr&#x00E1;s</p>
<list list-type="order">
<list-item><p>An&#x00E1;lisis de requerimientos</p></list-item>
<list-item><p>Dise&#x00F1;o</p></list-item>
<list-item><p>Desarrollo (programaci&#x00F3;n)</p></list-item>
<list-item><p>Prueba</p></list-item>
<list-item><p>Despliegue</p></list-item>
<list-item><p>Mantenimiento</p></list-item>
</list>
</sec>
<sec id="c14-s2-3">
<title><bold>Desarrollo &#x00C1;gil</bold></title>
<p>En los a&#x00F1;os 1990 empiezan a aparecer diversas metodolog&#x00ED;as de desarrollo de software que cuestionan el modelo en cascada</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><italic>rapid application development, the unified process, dynamic systems development method (DSDM), scrum, extreme programming (XP), featuredriven development</italic></p></list-item>
</list>
<p>En 2001 se publica el <italic>Manifesto for Agile Software Development</italic> que resume y condensa todas estas metodolog&#x00ED;as</p>
<p><target target-type="page" id="pges_225"/>Manifiesto por el desarrollo &#x00E1;gil de software:</p>
<preformat>
Estamos descubriendo formas mejores de desarrollar
software tanto por nuestra propia experiencia como
ayudando a terceros. A trav&#x00E9;s de este trabajo hemos
aprendido a valorar:
</preformat>
<preformat>
Individuos e interacciones sobre procesos y herramientas.
Software funcionando sobre documentaci&#x00F3;n extensiva.
Colaboraci&#x00F3;n con el cliente sobre negociaci&#x00F3;n contractual.
Respuesta ante el cambio sobre seguir un plan.
</preformat>
<preformat>
Aunque valoramos los elementos de la derecha,
valoramos m&#x00E1;s los de la izquierda.
</preformat>
<p>12 principios del manifiesto &#x00E1;gil</p>
<p><ext-link ext-link-type="uri" xlink:href="http://agilemanifesto.org/iso/es/principles.html">http://agilemanifesto.org/iso/es/principles.html</ext-link></p>
</sec>
<sec id="c14-s2-4">
<title><bold>Scrum</bold></title>
<p>Scrum es una de las metodolog&#x00ED;as de desarrollo de software &#x00E1;gil m&#x00E1;s populares. Una idea dentro de la filosofia <italic>DevOps</italic> es incluir las operaciones en los <italic>sprints</italic> de <italic>Scrum</italic>. Esto se puede hacer de varias formas, es una materia abierta</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Es posible integrar personal de operaciones en los equipos <italic>Scrum</italic>, aunque no es una idea muy habitual, va contra el principio de separaci&#x00F3;n desarrollo-operaciones</p></list-item>
<list-item><label>&#x25FE;</label> <p>Es m&#x00E1;s natural una integraci&#x00F3;n m&#x00E1;s d&#x00E9;bil: p.e. asistencia de personal de operaciones a las reuniones de <italic>Scrum</italic>, como miembro externo</p></list-item>
<list-item><label>&#x25FE;</label> <p>Tambi&#x00E9;n se pueden adaptar las t&#x00E9;cnicas de <italic>Scrum</italic> dentro del equipo de operaciones</p></list-item>
</list>
<p>Scrum es una metodolog&#x00ED;a de desarrollo &#x00E1;gil de software elaborada por Ken Schwaber y Jeff Sutherland, publicada en 1995</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Se forman equipos de desarrolladores, t&#x00ED;picamente entre 5 y 9 (m&#x00E1;s un <italic>product owner m&#x00E1;s un scrum master )</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>El trabajo se descompone en ciclos denominados <italic>sprints</italic>, que duran entre 1 y 4 semanas. T&#x00ED;picamente 2</p></list-item>
<list-item><label>&#x25FE;</label> <p>Al final de cada <italic>sprint</italic> se entrega una versi&#x00F3;n del software</p></list-item>
</list>
</sec>
<sec id="c14-s2-5">
<title><target target-type="page" id="pges_226"/><bold>Equipos de Scrum</bold></title>
<p>En los equipos de <italic>scrum</italic> hay tres roles</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><italic>Due&#x00F1;o del producto, Product owner</italic></p>
<p>Es una persona, que representa al cliente. Tiene la visi&#x00F3;n del producto final y poder de decisi&#x00F3;n sobre c&#x00F3;mo debe ser el producto.</p></list-item>
<list-item><label>&#x25FE;</label> <p><italic>Scrum master</italic></p>
<p>Es el responsable de que se siga la metodolog&#x00ED;a <italic>scrum</italic> . Modera las reuniones, dirije al equipo en lo necesario para que el equipo se autodirija</p></list-item>
<list-item><label>&#x25FE;</label> <p>Miembro del equipo</p>
<p>Son los desarrolladores. Los equipos son multifuncionales, sin distinci&#x00F3;n de roles entre analista/programador/tester. Todos puedes hacer cualquier funci&#x00F3;n y son responsables de todo (aunque cada uno tenga una especialidad propia)</p></list-item>
</list>
<p>Los valores en <italic>scrum</italic> son</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Respeto entre las personas</p></list-item>
<list-item><label>&#x25FE;</label> <p>Responsabilidad y disciplina auto-impuesta</p></list-item>
<list-item><label>&#x25FE;</label> <p>Compromiso</p></list-item>
<list-item><label>&#x25FE;</label> <p>Trabajo enfocado en aportar valor al cliente</p></list-item>
</list>
<p>Las unidades b&#x00E1;sicas de construcci&#x00F3;n del producto son las <italic>historias de usuario</italic></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>El usuario de tipo xxxx quiere hacer yyyy. Esto le aporta el valor zzzz</p></list-item>
<list-item><label>&#x25FE;</label> <p>Las historias de usuario las aporta el product owner</p></list-item>
</list>
</sec>
<sec id="c14-s2-10">
<title><bold>Reuniones de trabajo en Scrum</bold></title>
<p>Planificaci&#x00F3;n del <italic>sprint</italic></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Reuni&#x00F3;n de todo el equipo, t&#x00ED;picamente de unas 4 horas, antes de cada <italic>sprint</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_227"/>A partir de las historias de usuario que propone el product owner, el equipo decide cu&#x00E1;les implementar y c&#x00F3;mo</p>
<p>Scrum diario</p></list-item>
<list-item><label>&#x25FE;</label> <p>Reuni&#x00F3;n de 15 minutos, del equipo al completo, siempre en el mismo sitio, a la misma hora, de pie, con horario inflexible, falte quien falte</p></list-item>
<list-item><label>&#x25FE;</label> <p>Cada miembro explica qu&#x00E9; hizo ayer, qu&#x00E9; har&#x00E1; hoy, qu&#x00E9; obst&#x00E1;culos cree que pueden impedir el sprint</p></list-item>
</list>
<p>Evaluaci&#x00F3;n de <italic>sprint</italic></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Reuni&#x00F3;n de unas dos horas al final del sprint</p></list-item>
<list-item><label>&#x25FE;</label> <p>Se presenta lo realizado (solo lo concluido)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Se eval&#x00FA;a el trabajo.</p></list-item>
</list>
</sec>
<sec id="c14-s2-11">
<title><bold>Kanban</bold></title>
<p>Kanban es una metodolog&#x00ED;a de gesti&#x00F3;n de procesos</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Tiene su origen en la industria del autom&#x00F3;vil: <italic>Toyota Prodution System</italic> y <italic>Lean Manufacturing</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>Se usa, entre otras cosas, para desarrollo de software, como metodolog&#x00ED;a &#x00E1;gil y especialmente ligera</p></list-item>
<list-item><label>&#x25FE;</label> <p>Es muy adecuada para <italic>DevOps</italic>. Se puede usar de diversas formas, por ejemplo que desarrollo y operaciones compartan la misma pizarra Kanban, aunque cada equipo gestione sus tareas</p></list-item>
</list>
<p>El elemento principal es el tablero Kanban, tambi&#x00E9;n llamado pizarra Kanbam. Es un diagrama que representa el flujo de trabajo</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Tradicionalmente se usaba una pizarra con tarjetas adhesivas o imanes,</p></list-item>
<list-item><label>&#x25FE;</label> <p>Hay versiones software, t&#x00ED;picamente como aplicaci&#x00F3;n web. P.e. <code><ext-link ext-link-type="uri" xlink:href="https://trello.com">https://trello.com</ext-link></code></p></list-item>
</list>
<fig><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-24.jpg"/></fig>
<p><target target-type="page" id="pges_228"/>Tablero Kanban con Trello</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Cada tarea, caracter&#x00ED;stica o historia de usuario se anota en una tarjeta o <italic>post-it</italic>, que se va desplazando desde la columna de la izquierda hasta la columna de la derecha</p></list-item>
<list-item><label>&#x25FE;</label> <p>WIP: <italic>Work in Progress</italic>. Tarjetas que circulan por el tablero. Es importante minimizar el WIP</p></list-item>
<list-item><label>&#x25FE;</label> <p>El numero de columnas es variable, entre 4 y 7 son valores t&#x00ED;picos. La denominaci&#x00F3;n de cada columna se adapta para cada empresa</p>
<p>Ejemplo:
<preformat>
Pendiente | Analizando | En desarrollo | Probando | Aceptado | Producci&#x00F3;n
</preformat></p></list-item>
</list>
</sec>
<sec id="c14-s2-12">
<title><bold>Tarjetas Kanban</bold></title>
<p>Cada tarjeta tiene</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Descripci&#x00F3;n de la tarea</p></list-item>
</list>
<p>Puede tener:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Qui&#x00E9;n la est&#x00E1; haciendo</p></list-item>
<list-item><label>&#x25FE;</label> <p>Su fecha l&#x00ED;mite</p></list-item>
<list-item><label>&#x25FE;</label> <p>Distintos colores</p>
<list list-type="bullet">
<list-item><p>Tal vez seg&#x00FA;n la urgencia</p></list-item>
<list-item><p>M&#x00E1;s habitualmente, por el tipo de trabajo</p>
<p>P.e. Verde: mantenimiento. Amarillo: historia de usuario. Rojo: Bug</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Indicador de progreso</p></list-item>
</list>
</sec></sec>
<sec id="c14-s3">
<label><bold>14.3.</bold></label>
<title><target target-type="page" id="pges_229"/><bold>Problemas habituales</bold></title>
<sec id="c14-s3-1">
<title><bold>Diferencias desarrollo-operaciones</bold></title>
<p>La mayor&#x00ED;a de problemas que se procura resolver con <italic>DevOps</italic> parten de que normalmente hay diferencias marcadas entre desarrollo y operaciones. Son equipos muy separados, con diferente lenguaje, culturas, habilidades, objetivos...</p>
<p>Una de las mayores diferencias es que</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Los desarrolladores (analistas, programadores, <italic>testers</italic> y responsables de calidad) buscan el cambio continuamente, para corregir errores y a&#x00F1;adir funcionalidad</p></list-item>
<list-item><label>&#x25FE;</label> <p>Los administradores (administradores de sistemas, de bases de datos y de redes) buscan estabilidad. Ven cualquier cambio como un riesgo potencial. Nadie les agradecer&#x00E1; la nueva funcionalidad. Pero s&#x00ED; les culpar&#x00E1;n de los problemas de explotaci&#x00F3;n provocados por los cambios</p></list-item>
</list>
</sec>
<sec id="c14-s3-2">
<title><bold>Problemas t&#x00ED;picos (1)</bold></title>
<p>Enumeramos a continuaci&#x00F3;n algunos problemas habituales entre el equipo de desarrollo y el equipo de operaciones, que las t&#x00E9;cnicas <italic>DevOps</italic> buscan solucionar</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>El software que funcionaba en los equipos de desarrollo, da errores en producci&#x00F3;n</p>
<list list-type="bullet">
<list-item><p>Desarrollo echa la culpa a operaciones</p></list-item>
<list-item><p>Operaciones echa la culpa a desarrollo</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Aparece alg&#x00FA;n problema menor en la funcionalidad o el rendimiento. El equipo de desarrollo hace un parche r&#x00E1;pido sin pasar todos los controles de calidad</p>
<list list-type="bullet">
<list-item><p>Operaciones instala el parche, arregla una cosa pero rompe otra</p></list-item>
<list-item><p>Operaciones no instala el parche, porque sabe que los parches son peligrosos</p></list-item>
</list></list-item>
</list>
</sec>
<sec id="c14-s3-3">
<title><target target-type="page" id="pges_230"/><bold>Problemas t&#x00ED;picos (2)</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Desarrollo hace un producto de baja calidad, lo m&#x00ED;nimo para ser aceptado</p>
<list list-type="bullet">
<list-item><p>El trabajo ya est&#x00E1; <italic>hecho</italic>. El diagrama de Gantt est&#x00E1; cumplido. Los problemas posteriores no importan, son cosa de <italic>mantenimiento</italic>, de otro contrato, de otra subcontrata, de otro presupuesto...</p></list-item>
<list-item><p>Consumir m&#x00E1;s recursos (tiempo, esfuerzo) para entregar un producto de m&#x00E1;s calidad, no reportar&#x00E1; beneficios al equipo de desarrollo</p></list-item>
</list></list-item>
</list>
</sec>
<sec id="c14-s3-4">
<title><bold>Problemas t&#x00ED;picos (3)</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Problema contrario al anterior: Desarrollo <italic>ananc&#x00E1;stico</italic> (demasiado perfeccionista)</p>
<list list-type="bullet">
<list-item><p>El equipo de desarrollo prepara un un software con cambios radicales (nuevo lenguajes, nuevas librer&#x00ED;as). O preparado para eventualidades poco probables.</p></list-item>
<list-item><p>Todo lo contrario al <italic>peque&#x00F1;o cambio incremental</italic>. No tiene en cuenta las implicaciones para operaciones. Dispara los costes y/o los plazos, peligrando la viabilidad de la empresa. A operaciones o al mercado no llegan las soluciones adecuadas porque la soluci&#x00F3;n <italic>&#x00F3;ptima</italic> no est&#x00E1; disponible</p></list-item>
</list></list-item>
</list>
</sec>
<sec id="c14-s3-5">
<title><bold>Problemas t&#x00ED;picos (4)</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Para intentar evitar los problemas anteriores, se mejora la especificaci&#x00F3;n de los deliverables (entregables) que unos proporcionan a otros</p>
<list list-type="bullet">
<list-item><p>Negociaciones duras, criterios muy r&#x00ED;gidos, contratos complicados, especificaciones a la defensiva. Lo fundamental es, en caso de problema, dejar claro <italic>qui&#x00E9;n tiene la culpa</italic></p></list-item>
<list-item><p>Esto aumenta los tr&#x00E1;mites, la preparaci&#x00F3;n y la frecuencia de las entregas</p></list-item>
<list-item><p>Aumenta la distancia entre los equipos</p></list-item>
</list></list-item>
</list>
</sec>
<sec id="c14-s3-6">
<title><target target-type="page" id="pges_231"/><bold>Problemas t&#x00ED;picos (5)</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Cultura del h&#x00E9;roe (<italic>rock star, ninja, crack</italic> )</p>
<list list-type="bullet">
<list-item><p>Programador individualista. Con frecuencia hace software con errores y no documentado. Aparentemente es muy valioso porque solo >&#x00E9;l sabe arreglar esos errores</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Planificaci&#x00F3;n r&#x00ED;gida</p>
<list list-type="bullet">
<list-item><p>Inicialmente se prepara un diagrama de Gantt. Luego todo se fuerza para que encaje en el diagrama</p></list-item>
</list></list-item>
</list>
</sec></sec>
<sec id="c14-s4">
<label><bold>14.4.</bold></label>
<title><bold>Aspectos humanos</bold></title>
<sec id="c14-s4-1">
<title><bold>Valores a promover</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Equipos motivados y productivos</p></list-item>
<list-item><label>&#x25FE;</label> <p>Compromiso con objetivos y valores comunes</p></list-item>
<list-item><label>&#x25FE;</label> <p>Respeto al otro</p></list-item>
<list-item><label>&#x25FE;</label> <p>Colaboraci&#x00F3;n bienintencionada entre las partes/las empresas</p></list-item>
<list-item><label>&#x25FE;</label> <p>Aceptaci&#x00F3;n de un cierto ratio de errores como inevitables, sin buscar culpables</p></list-item>
</list>
<p>Todo esto</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Tiene ventajas evidentes</p></list-item>
<list-item><label>&#x25FE;</label> <p>Es dif&#x00ED;cil de conseguir</p></list-item>
</list>
</sec>
<sec id="c14-s4-2">
<title><bold>Motivaci&#x00F3;n de equipos</bold></title>
<p>Seg&#x00FA;n Poppendieck, la motivaci&#x00F3;n se puede conseguir con:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Sensaci&#x00F3;n de pertenencia</p></list-item>
<list-item><label>&#x25FE;</label> <p>Confianza en una cierta tolerancia a los errores</p></list-item>
<list-item><label>&#x25FE;</label> <p>Confianza en la capacidad propia y del resto del equipo</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_232"/>Celebraci&#x00F3;n conjunta de los progresos</p>
<list list-type="bullet">
<list-item><p>Teniendo en cuenta que no todo el mundo sale de copas o juega al Paintball</p></list-item>
</list></list-item>
</list>
</sec>
<sec id="c14-s4-3">
<title><bold>Trabajo en equipo productivo</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Definir objetivos, m&#x00E9;todos, pasos, plazos temporales... y reajustarlo cuando sea necesario</p></list-item>
<list-item><label>&#x25FE;</label> <p>Evitar la microgesti&#x00F3;n. Que los gestores digan qu&#x00E9; hacer, pero sin demasiados detalles del c&#x00F3;mo. El equipo se auto-organiza</p>
<list list-type="bullet">
<list-item><p>Esto suele ser m&#x00E1;s eficiente</p></list-item>
<list-item><p>Hace al equipo sentirse m&#x00E1;s valorado</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Hacer peque&#x00F1;os experimentos. Fallar a menudo pero pronto y con peque&#x00F1;as cosas</p></list-item>
<list-item><label>&#x25FE;</label> <p>De vez en cuando (una vez al d&#x00ED;a, a la semana...) reservar un rato para alguien de operaciones se siente con alguien de desarrollo</p></list-item>
<list-item><label>&#x25FE;</label> <p>Evitar el <italic>presentismo laboral</italic>. Respetar el equilibrio entre el trabajo y la vida personal. No esperar que los empleados hagan jornadas maratonianas en la oficina y que luego contesten al correo a cualquier hora</p></list-item>
</list>
</sec>
<sec id="c14-s4-4">
<title><bold>Comunicaci&#x00F3;n efectiva</bold></title>
<p>Que los individuos y equipos:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Comprendan las circustancias y dificultades de los dem&#x00E1;s</p></list-item>
<list-item><label>&#x25FE;</label> <p>Busquen influenciar en otros de forma positiva.</p>
<p>No porque <italic>te lo mando</italic> o <italic>me debes una</italic>, sino porque esto es lo mejor para todos</p></list-item>
<list-item><label>&#x25FE;</label> <p>Reconozcan el trabajo ajeno. Hacerlo en p&#x00FA;blico es mucho m&#x00E1;s efectivo. Y si hay que hacer alg&#x00FA;n reproche, con mucha mano izquierda y en privado</p></list-item>
</list>
<p><target target-type="page" id="pges_233"/>Reuniones de calidad</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Todos los convocados llegan puntuales. La reuni&#x00F3;n acaba puntualmente</p></list-item>
<list-item><label>&#x25FE;</label> <p>Meta-decisiones claras. Ya sea por jerarq&#x00FA;&#x0131;a, por votaci&#x00F3;n, o idealmente, por consenso</p></list-item>
<list-item><label>&#x25FE;</label> <p>Los participantes hablan de uno en uno</p></list-item>
<list-item><label>&#x25FE;</label> <p>Lo que solo afecta a unos pocos, no se trata en el tiempo de todos</p></list-item>
<list-item><label>&#x25FE;</label> <p>...</p></list-item>
</list>
<p>Sin olvidar las</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Discusiones retrospectivas. Reuniones con periodicidad predeterminada para tratar las etapas superadas, para analizarlas y extraer conclusiones.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Reuniones <italic>post morten</italic>. Similares a las retrospectivas, pero provocadas por un problema concreto. Siempre es necesario trabajar de forma constructiva sin echar la culpa a nadie, pero en estos casos, mas que nunca.</p></list-item>
</list>
</sec></sec>
<sec id="c14-s5">
<label><bold>14.5.</bold></label>
<title><bold>Aspectos t&#x00E9;cnicos</bold></title>
<sec id="c14-s5-1">
<title><bold>Automatizaci&#x00F3;n</bold></title>
<p>La automatizaci&#x00F3;n es una t&#x00E9;cnica fundamental en <italic>DevOps</italic></p>
<p>Tareas que se pueden automatizar:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Construcci&#x00F3;n (compilaci&#x00F3;n)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Pruebas</p></list-item>
<list-item><label>&#x25FE;</label> <p>Despliegue (puesta en producci&#x00F3;n)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Configuraci&#x00F3;n en los distintos entornos</p></list-item>
<list-item><label>&#x25FE;</label> <p>Monitorizaci&#x00F3;n</p></list-item>
<list-item><label>&#x25FE;</label> <p>Control de incidencias</p></list-item>
</list>
<p><target target-type="page" id="pges_234"/>Ultimamente se ha introducido el t&#x00E9;rmino <italic>orquestar</italic>:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Automatizar</p>
<p>Usar herramientas (scripts o similares) que permitan realizar una tarea sin intervenci&#x00F3;n de una persona</p></list-item>
<list-item><label>&#x25FE;</label> <p>Orquestar</p>
<p>Coordinar diversas automatizaciones de tareas individuales, para que formen procesos / flujos de trabajo</p></list-item>
</list>
<p>Automatizar (incluyendo orquestar) es, en general, positivo. Con algunas salvedades</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Debemos asegurarnos de que merezca la pena. Que el esfuerzo necesario para preparar y mantener la automatizaci&#x00F3;n, sea menor que el esfuerzo de realizar las tareas a mano.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Paradoja del exceso de automatizaci&#x00F3;n</p>
<p>Inevitablemente, habr&#x00E1; ocasiones en que el sistema requiera intervenci&#x00F3;n humana (errores, cambios no previstos...). C&#x00FA;anto m&#x00E1;s automatizado est&#x00E9; el sistema:</p>
<list list-type="bullet">
<list-item><p>M&#x00E1;s complejos ser&#x00E1;n estos cambios, m&#x00E1;s especializado tendr&#x00E1; que ser el personal</p></list-item>
<list-item><p>Menos especializado ser&#x00E1; el personal del d&#x00ED;a a d&#x00ED;a.</p></list-item>
<list-item><p>Como esto lo puede llevar cualquiera, el resultado es que lo acaba llevando cualquiera</p></list-item>
</list></list-item>
</list>
</sec>
<sec id="c14-s5-2">
<title><bold>T&#x00E9;cnicas de despliegue (1)</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Despliegue frecuente</p>
<p>Un cambio grande puede ser muy dr&#x00E1;stico. Por el contrario, el despliegue frecuente pone en producci&#x00F3;n peque&#x00F1;os cambios, de forma continua</p>
<list list-type="bullet">
<list-item><p>Esto familiariza a todo el equipo con el proceso de introducir novedades</p></list-item>
<list-item><p><target target-type="page" id="pges_235"/>Los cambios menores implican problemas potenciales menores</p></list-item>
<list-item><p>Hay t&#x00E9;cnicas m&#x00E1;s avanzadas (integraci&#x00F3;n continua, entrega continua, despliegue continuo). Pero realizar al menos <italic>despliegue frecuente</italic> es pr&#x00E1;cticamente imprescindible dentro de la filosof&#x00ED;a <italic>DevOps</italic></p></list-item>
</list></list-item>
</list>
</sec>
<sec id="c14-s5-3">
<title><bold>T&#x00E9;cnicas de despliegue (2)</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Conmutaci&#x00F3;n de funciones</p>
<p>Ejemplo: Ponemos en producci&#x00F3;n funcionalidad nueva. Pero si falla y decidimos desactivarla, se puede hacer con un conmutador sencillo desde el c&#x00F3;digo. Sin necesidad de volver a desplegar el c&#x00F3;digo <italic>viejo</italic></p>
<list list-type="bullet">
<list-item><p>Los contenedores pueden hacer innecesaria esta t&#x00E9;cnica</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p><italic>Dark Launching</italic></p>
<p>Las nuevas versiones se aplican solo a unos pocos usuarios</p>
<list list-type="bullet">
<list-item><p>Esto facilita la correcci&#x00F3;n de problemas y limita los problemas potenciales</p></list-item>
<list-item><p>Pueden ser los empleados, pueden ser voluntarios, pueden ser usuarios que hemos detectado como <italic>avanzados</italic> o pueden ser aleatorios</p></list-item>
</list></list-item>
</list>
</sec>
<sec id="c14-s5-4">
<title><bold>T&#x00E9;cnicas de despliegue (3)</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><italic>Blue Green Deployment</italic></p>
<p>La versi&#x00F3;n nueva y la versi&#x00F3;n anterior se preparan para que funcionen en paralelo</p>
<list list-type="bullet">
<list-item><p>Para conmutar de la <italic>versi&#x00F3;n azul</italic> a la <italic>versi&#x00F3;n verde</italic> no hay que cambiar el c&#x00F3;digo, solo el router/el <italic>balanceador</italic> de carga o alg&#x00FA;n fichero de configuraci&#x00F3;n</p></list-item>
</list></list-item>
</list>
</sec>
<sec id="c14-s5-5">
<title><target target-type="page" id="pges_236"/><bold>Integraci&#x00F3;n, entrega y despliegue continuo</bold></title>
<p>Las siguiente t&#x00E9;cnicas son habituales en <italic>DevOps</italic>, aunque</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>No son imprescindibles para hacer <italic>DevOps</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>Implementarlas no significa estar haciendo <italic>DevOps</italic></p></list-item>
</list>
<p>En cualquier entorno de desarrollo moderno de cierto tama&#x00F1;o, hay varios desarrolladores, utilizando un sistema de control de versiones. Cada uno tiene su copia de trabajo del software, que con cierta periodicidad, integra en el repositorio principal</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><italic>Continuous Integration</italic> (CI)</p>
<p>Realizar esta integraci&#x00F3;n muy a menudo. T&#x00ED;picamente varias veces al d&#x00ED;a</p></list-item>
<list-item><label>&#x25FE;</label> <p><italic>Continuous Delivery</italic> (CD)</p>
<p>No solamente hacer <italic>Continuous Integration</italic>, sino dar un paso m&#x00E1;s all&#x00E1;. Adem&#x00E1;s de integrar el c&#x00F3;digo, asegurarse de que est&#x00E1; listo para ponerse en producci&#x00F3;n muy a menudo. Esto es, pasar los controles de calidad y automatizar la puesta en producci&#x00F3;n. Tal vez no tan a menudo como la CI, pero s&#x00ED; muy a menudo. P.e. una vez al d&#x00ED;a.</p></list-item>
<list-item><label>&#x25FE;</label> <p><italic>Continuous Deployment</italic></p>
<p>No solo hacer <italic>Continuous Delivery</italic>, sino dar un paso m&#x00E1;s all&#x00E1;. Adem&#x00E1;s de asegurarse de que el c&#x00F3;digo tiene calidad como para ponerse en producci&#x00F3;n, ponerlo realmente en producci&#x00F3;n</p></list-item>
</list>
</sec></sec>
<sec id="c14-s6">
<label><bold>14.6.</bold></label>
<title><bold>Herramientas</bold></title>
<sec id="c14-s6-1">
<title><bold>Herramientas</bold></title>
<p>Las siguientes herramientas son habituales cuando se siguen los principios</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>DevOps</p></list-item>
<list-item><label>&#x25FE;</label> <p>Contenedores Docker</p>
<p><ext-link ext-link-type="uri" xlink:href="https://gsyc.urjc.es/~mortuno/lagrs/02-virtualizacion_I.pdf">https://gsyc.urjc.es/~mortuno/lagrs/02-virtualizacion_I.pdf</ext-link></p>
<p><ext-link ext-link-type="uri" xlink:href="https://gsyc.urjc.es/~mortuno/lagrs/02-virtualizacion_III.pdf">https://gsyc.urjc.es/~mortuno/lagrs/02-virtualizacion_III.pdf</ext-link></p></list-item>
<list-item><label>&#x25FE;</label> <p>Ansible</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_237"/>Jenkins</p></list-item>
<list-item><label>&#x25FE;</label> <p>Vagrant</p>
<p><ext-link ext-link-type="uri" xlink:href="https://gsyc.urjc.es/~mortuno/lagrs/vagrant.pdf">https://gsyc.urjc.es/~mortuno/lagrs/vagrant.pdf</ext-link></p></list-item>
</list>
</sec>
<sec id="c14-s6-2">
<title><bold>Jenkins</bold></title>
<fig><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-25.jpg"/></fig>
<p><ext-link ext-link-type="uri" xlink:href="https://jenkins.io">https://jenkins.io</ext-link></p>
<p>Jenkins es una herramienta para implementar Integraci&#x00F3;n Continua (C.I.)</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Aparece en el a&#x00F1;o 2011. Es software libre, muy popular</p></list-item>
<list-item><label>&#x25FE;</label> <p>Aplicaci&#x00F3;n basada en web</p></list-item>
<list-item><label>&#x25FE;</label> <p>Va un paso m&#x00E1;s all&#x00E1; de herramientas como Maven, que construyen el fuente pero no hacen C.I.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Su entorno nativo es Java, pero tiene <italic>plugins</italic> para distintos lenguajes, herramientas de control de versiones, de virtualizaci&#x00F3;n, de testing, de comunicaci&#x00F3;n con el personal, etc</p></list-item>
<list-item><label>&#x25FE;</label> <p>La unidad principal es el <italic>build</italic> : el conjunto de pasos para desplegar una aplicaci&#x00F3;n software</p>
<list list-type="bullet">
<list-item><p>Un <italic>build</italic> se pueden disparar manualmente, por un commit, o con planificaci&#x00F3;n peri&#x00F3;dica similar a cron</p>
<p>Un <italic>build</italic> se organiza en <italic>pipelines</italic>, cada una compuesta de <italic>steps</italic><target target-type="page" id="pges_238"/></p></list-item>
</list></list-item>
</list>
<preformat>
Jenkinsfile (Declarative Pipeline)
pipeline {
&#x00A0;&#x00A0;&#x00A0;agent any
&#x00A0;&#x00A0;&#x00A0;stages {
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;stage(&#x0027;Build&#x0027;) {
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;steps {
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;echo &#x0027;Building&#x0027;
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;}
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;}
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;stage(&#x0027;Test&#x0027;) {
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;steps {
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;echo &#x0027;Testing&#x0027;
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;}
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;}
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;stage(&#x0027;Deploy&#x0027;) {
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;steps {
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;echo &#x0027;Deploying&#x0027;
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;}
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;}
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;}
&#x00A0;&#x00A0;&#x00A0;}
</preformat>
</sec>
<sec id="c14-s6-3">
<title><bold>Ansible</bold></title>
<fig><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-26.jpg"/></fig>
<p><ext-link ext-link-type="uri" xlink:href="https://www.ansible.com">https://www.ansible.com</ext-link></p>
<p>Ansible es un software de gesti&#x00F3;n de configuraciones</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Creado por Michael DeHaan en 2012. En la actualidad pertenece a RedHat</p></list-item>
<list-item><label>&#x25FE;</label> <p>software libre, muy popular</p></list-item>
<list-item><label>&#x25FE;</label> <p>Arquitectura cliente servidor</p></list-item>
<list-item><label>&#x25FE;</label> <p>Los clientes se denominan <italic>nodos</italic>. Son las m&#x00E1;quinas controladas. Solo necesitan un servidor de ssh, por tanto soporta cualquier Linux, Unix, macOS. Tambi&#x00E9;n funciona sobre la PowerShell de Microsoft Windows</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_239"/>El servidor se denomina <italic>controlling machine</italic>. Es la m&#x00E1;quina que administra y controla los nodos. El soporte nativo es para Linux. Hay versiones para macOS y otras plataformas, pr&#x00E1;cticamente cualquiera donde funcione python y pip</p></list-item>
</list>
<fig><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-27.jpg"/>
<attrib>Iconos: <ext-link ext-link-type="uri" xlink:href="http://www.vecteezy.com">www.vecteezy.com</ext-link></attrib>
</fig>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Ansible sigue un paradigma <italic>push</italic>: el controlador env&#x00ED;a las &#x00F3;rdenes</p></list-item>
<list-item><label>&#x25FE;</label> <p>Otras herramientas similares como <italic>puppet</italic> tienen un enfoque <italic>pull</italic>, que resulta m&#x00E1;s complejo: la m&#x00E1;quina administrada corre un demonio que, peri&#x00F3;dicamente, se <italic>trae</italic> las &#x00F3;rdenes</p></list-item>
</list>
</sec>
<sec id="c14-s6-4">
<title><bold>Conceptos principales en Ansible</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><italic>Inventory</italic></p>
<p>Fichero que contiene el listado de las m&#x00E1;quinas administradas, con su nombre y/o direcci&#x00F3;n IP, puerto, claves ssh, etc</p></list-item>
<list-item><label>&#x25FE;</label> <p><italic>Playbook</italic></p>
<p>Es una especie de <italic>howto</italic> automatizable, un script de prop&#x00F3;sito espec&#x00ED;fica (configurar una m&#x00E1;quina), de alto nivel y f&#x00E1;cilmente legible por humanos</p>
<list list-type="bullet">
<list-item><p>Palabra inglesa que significa libro de juego, libro de t&#x00E1;cticas o libro de reglas</p></list-item>
<list-item><p>Escrito en formato YAML, similar a JSON pero con sintaxis pensada para que sea c&#x00F3;modo para las personas. Filosof&#x00ED;a an&#x00E1;loga al formato <italic>markdown</italic>, pero para datos, no para texto</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_240"/><italic>Role</italic></p>
<p>Estructura de nivel superior al <italic>playbook</italic>. Permite hacer plantillas de <italic>playbooks. Est&#x00E1; formado por playbooks, ficheros, dependencias entre</italic> <italic>playbooks...</italic></p></list-item>
</list>
<p><italic>Ansible Galaxy</italic></p>
<p>Repositorio centralizado de roles. Facilita la instalaci&#x00F3;n de software. Equivalente a tener un repositorio de libros de instrucciones, pero que se autoejecutan</p>
<p><ext-link ext-link-type="uri" xlink:href="https://galaxy.ansible.com">https://galaxy.ansible.com</ext-link></p>
<preformat>
# Configurar un servidor web b&#x00E1;sico con nginx
- name: Configurar servidor web con nginx
hosts: miServidor01
sudo: yes
tasks:
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;- name: install nginx
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;apt: name=nginx update_cache=yes
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;- name: copy nginx conf file
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;copy: src=files/nginx.conf dest=/etc/nginx/nginx.conf
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;- name: copy nginx server file
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;copy: src=files/server.conf dest=/etc/nginx/sites-available/default
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;- name: enable config
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;file: &#x003E;
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;dest=/etc/nginx/conf.d/default
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;src=/etc/nginx/sites-available/default
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;state=link
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;- name: copy index.html
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;template: src=templates/index.html.j2 dest=/usr/share/nginx/html/index.html mode=0644
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;- name: restart nginx
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;service: name=nginx state=restarted enabled=yes
</preformat>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>El gui&#x00F3;n denota un elemento de una lista (una tarea, (<italic>task</italic>)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Cada tarea suele estar compuesta por varias l&#x00ED;neas.</p>
<p>La primera suele ser el nombretiene un nombre (opcional)</p>
<p><target target-type="page" id="pges_241"/>A continuaci&#x00F3;n, la orden</p>
<p>p.e.</p>
<p><code>apt: name=nginx update_cache=yes</code></p>
<p>ejecutar&#x00E1; en cada nodo</p>
<p><code>apt update; apt install -y nginx</code></p></list-item>
</list>
</sec></sec>
</body>
<back>
<ref-list id="b1">
<title><bold>Referencias</bold></title>
<ref id="CIT001"><label>[1]</label><mixed-citation publication-type="book"><source><italic>DevOps for Developers</italic></source><x> </x><person-group person-group-type="editor"><string-name><given-names>Michael</given-names><x> </x><surname>Huttermann</surname></string-name></person-group><x>. </x><role>Ed.</role><x> </x><publisher-name>Apress</publisher-name><x>, </x><year>2012</year><x> </x><ext-link ext-link-type="uri" xlink:href="http://proquest.safaribooksonline.com/book/software-engineering-and-development/9781430245698">http://proquest.safaribooksonline.com/book/software-engineering-and-development/9781430245698</ext-link></mixed-citation></ref>
<ref id="CIT002"><label>[2]</label><mixed-citation publication-type="book"><source><italic>Effective DevOps: Building a Culture of Collaboration, Affinity, and Tooling at Scale</italic></source><x> </x><person-group person-group-type="editor"><string-name><given-names>Jennifer</given-names><x> </x><surname>Davis</surname></string-name><x>, </x><string-name><given-names>Katherine</given-names><x> </x><surname>Daniels</surname></string-name></person-group><x>. </x><role>Ed.</role><x> </x><publisher-name>O&#x2019;Reilly</publisher-name><x>, </x><year>2016</year><x> </x><ext-link ext-link-type="uri" xlink:href="http://proquest.safaribooksonline.com/book/software-engineering-and-development/9781491926291">http://proquest.safaribooksonline.com/book/software-engineering-and-development/9781491926291</ext-link></mixed-citation></ref>
<ref id="CIT003"><label>[3]</label><mixed-citation publication-type="book"><source><italic>DevOps for Web Development</italic></source><x> </x><person-group person-group-type="editor"><string-name><given-names>Mitesh</given-names><x> </x><surname>Soni</surname></string-name></person-group><x>. </x><role>Ed.</role><x> </x><publisher-name>Pack</publisher-name><x>, </x><year>2016</year><x>.</x><x> </x><ext-link ext-link-type="uri" xlink:href="http://proquest.safaribooksonline.com/book/web-design-and-development/9781786465702">http://proquest.safaribooksonline.com/book/web-design-and-development/9781786465702</ext-link></mixed-citation></ref>
<ref id="CIT004"><label>[4]</label><mixed-citation publication-type="book"><source><italic>DevOps: A Software Architect&#x2019;s Perspective</italic></source><x> </x><person-group person-group-type="editor"><string-name><given-names>Len</given-names><x> </x><surname>Bass</surname></string-name><x>, </x><string-name><given-names>Ingo</given-names><x> </x><surname>Weber</surname></string-name><x>, </x><string-name><given-names>Liming</given-names><x> </x><surname>Zhu</surname></string-name></person-group><x>. </x><role>Ed.</role><x> </x><publisher-name>Pearson</publisher-name><x>, </x><year>2015</year><x> </x><ext-link ext-link-type="uri" xlink:href="http://proquest.safaribooksonline.com/book/software-engineering-and-development/9780134049885">http://proquest.safaribooksonline.com/book/software-engineering-and-development/9780134049885</ext-link></mixed-citation></ref>
<ref id="CIT005"><label>[5]</label><mixed-citation publication-type="book"><source><italic>Kanban in Action</italic></source><x> </x><person-group person-group-type="editor"><string-name><given-names>Marcus</given-names><x> </x><surname>Hammarberg</surname></string-name><x>, </x><string-name><given-names>Joakim</given-names><x> </x><surname>Sunden</surname></string-name></person-group><x>. </x><role>Ed.</role><x> </x><publisher-name>Manning</publisher-name><x>, </x><year>2014</year><x> </x><ext-link ext-link-type="uri" xlink:href="http://proquest.safaribooksonline.com/book/software-engineering-and-development/agile-development/9781617291050">http://proquest.safaribooksonline.com/book/software-engineering-and-development/agile-development/9781617291050</ext-link></mixed-citation></ref>
<ref id="CIT006"><label>[6]</label><mixed-citation publication-type="book"><source><italic>The Elements of Scrum</italic></source><x> </x><person-group person-group-type="editor"><string-name><given-names>Chris</given-names><x> </x><surname>Sims</surname></string-name><x>, </x><string-name><given-names>Hillary</given-names><x> </x><surname>Louise Johnson</surname></string-name></person-group><x>. </x><role>Ed.</role><x> </x><publisher-name>Dymaxicon</publisher-name><x>, </x><year>2011</year><target target-type="page" id="pges_242"/></mixed-citation></ref>
</ref-list>
</back>
</book-part>
<book-part id="c15" book-part-type="chapter">
<book-part-meta>
<book-part-id book-part-id-type="publisher-id">URJC</book-part-id>
<title-group>
<label>15.</label>
<title><target target-type="page" id="pges_243"/>ADMINISTRACI&#x00D3;N DE SERVICIOS</title>
</title-group>
</book-part-meta>
<body>
<sec id="c15-s1">
<label><bold>15.1.</bold></label>
<title><bold>Empaquetado de ficheros</bold></title>
<sec id="c15-s1-1">
<title><bold>Empaquetado de ficheros</bold></title>
<p>Almacenar varios ficheros en uno solo, no necesariamente con compresi&#x00F3;n Utilidad:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>M&#x00E1;s c&#x00F3;modo de manejar (copiar, enviar por correo, etc)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Conservar metainformaci&#x00F3;n (permisos) o incluso may&#x00FA;sculas/min&#x00FA;sculas, tildes, etc si los ficheros van a pasar por un sistema de ficheros diferente</p>
<list list-type="bullet">
<list-item><p>ISO9660 (cdrom)</p></list-item>
<list-item><p>vfat (Windows, discos externos, pendrives)</p></list-item>
<list-item><p>ntfs (Windows)</p></list-item>
</list></list-item>
</list>
</sec>
<sec id="c15-s1-2">
<title><bold>gzip</bold></title>
<p>Comprime o descomprime 1 fichero</p>
<p>Extensi&#x00F3;n: <code>fichero.z fichero.gz</code></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Comprimir y descomprimir (borrando el original):
<preformat>
gzip fichero
gunzip fichero.gz
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Comprimir y descomprimir (manteniendo el original):
<preformat>
gzip -c fichero &#x003E; fichero.gz
zcat fichero.gz &#x003E; fichero
zcat fichero.gz | less
</preformat></p>
</list-item>
</list>
</sec>
<sec id="c15-s1-3">
<title><bold>tar + gzip</bold></title>
<p>Comprime o descomprime varios ficheros, directorios</p>
<p>Extensi&#x00F3;n: <code>fichero.tar.gz fichero.tgz</code></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_244"/>Comprimir:
<preformat>
tar -cvzf fichero.tgz fichero1 fichero2
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Descomprimir:
<preformat>
tar -xvzf fichero.tgz
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_245"/>Mostrar contenido:
<preformat>
tar -tzf fichero.tgz
</preformat></p></list-item>
</list>
</sec>
<sec id="c15-s1-4">
<title><bold>WinZip</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Por motivos de licencias, originalmente no hab&#x00ED;a compresores para Linux. (Pero las aplicaciones Windows saben descomprimir descomprimir <code>.tgz</code>)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Descomprimir: <code>unzip fichero.zip</code></p></list-item>
</list>
</sec>
<sec id="c15-s1-5">
<title><bold>bz2</bold></title>
<p>Formato que ofrece compresi&#x00F3;n m&#x00E1;s alta que .gz, (empleando m&#x00E1;s CPU y memoria)</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Comprimir y descomprimir 1 fichero, borrando el original
<preformat>
bzip2 fichero
bunzip2 fichero.bz2
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Comprimir y descomprimir 1 fichero, manteniendo el original
<preformat>
bzip2 -c fichero &#x003E; fichero.bz2
bunzip2 -c fichero.bz2 &#x003E; fichero
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Comprimir y descomprimir varios ficheros, manteniendo el original
<preformat>
tar -c fichero1 fichero2 | bzip2 &#x003E; fichero.bz2
tar -xjf fichero.bz2
</preformat></p></list-item>
</list>
</sec>
<sec id="c15-s1-6">
<title><bold>Fragmentaci&#x00F3;n de ficheros</bold></title>
<p>Si necesitas trocear una imagen de gran tama&#x00F1;o en ficheros que quepan en un <italic>pendrive</italic> o cdrom</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Empaquetar y comprimir un directorio:
<preformat>
tar -cvzf mi_imagen.tgz mi_directorio
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Mostrar contenido:
<preformat>
tar -tzf mi_imagen.tgz
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Trocear:
<preformat>
# tama&#x00F1;o fichero prefijo split -b 500MB mi_imagen.tgz mi_imagen.tgz.
</preformat></p>
<p>(Observa que el segundo par&#x00E1;metro es igual al primero, pero a&#x00F1;adiendo un punto)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Habremos generado
<preformat>
mi_imagen.tgz.aa mi_imagen.tgz.ab mi_imagen.tgz.ac
</preformat></p></list-item>
</list>
<p>En la m&#x00E1;quina destino (no importa si en el <italic>host</italic> el S.O. es distinto)</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Unir los fragmentos cat mi_imagen.tgz.* &#x003E; mi_imagen.tgz</p>
<p>(En MS Windows para este paso podemos emplear HjSplit, Free File Splitter o cualquier otro programa similar)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Descomprimir y desempaquetar:
<preformat>
tar -xvzf mi_imagen.tgz
</preformat></p>
<p>(En MS Windows podemos usar 7-Zip o similares)</p></list-item>
</list>
</sec></sec>
<sec id="c15-s2">
<label><bold>15.2.</bold></label>
<title><bold>Instalaci&#x00F3;n de paquetes</bold></title>
<sec id="c15-s2-1">
<title><bold>Instalaci&#x00F3;n de paquetes</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>M&#x00E9;todo cl&#x00E1;sico para instalar programas:</p>
<p>Formato .tgz</p>
<p>Descomprimir y seguir las instrucciones del fichero README</p>
<p>Suele ser del estilo de
<preformat>
./configure
make compile
make install
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Sistema de gesti&#x00F3;n de paquetes</p>
<p>Colecci&#x00F3;n de herramientas que automatizan la instalaci&#x00F3;n, actualizaci&#x00F3;n y eliminaci&#x00F3;n de programas.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Gesti&#x00F3;n de paquetes, Debian y derivados</p>
<p>Paquetes en formato <code>.deb</code></p>
<p>Se pueden manejar directamente con <code>dpkg</code>, o con <code>apt-get</code>, <code>apt</code>, <code>aptitude</code>, <code>dselect</code>, o <code>synaptic</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>Gesti&#x00F3;n de paquetes, RedHat y derivados</p>
<p>Paquetes en formato <code>.rpm</code></p>
<p>Se pueden manejar directamente con <code>rpm</code>, o con <code>up2date</code> o <code>yum</code></p></list-item>
</list>
</sec>
<sec id="c15-s2-s1">
<label><bold>15.2.1.</bold></label>
<title><target target-type="page" id="pges_246"/><bold>El sistema de paquetes de Debian</bold></title>
<sec id="c15-s2-s1-1">
<title><bold>El sistema de paquetes de Debian</bold></title>
<p>Los paquetes mantienen <italic>dependencias</italic> entre s&#x00ED;, de forma que la instalaci&#x00F3;n de un paquete puede:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><italic>depender</italic> de que se instale tambi&#x00E9;n otro</p></list-item>
<list-item><label>&#x25FE;</label> <p><italic>recomendar</italic> que se instale tambi&#x00E9;n otro</p></list-item>
<list-item><label>&#x25FE;</label> <p><italic>sugerir</italic> que se instale tambi&#x00E9;n otro</p></list-item>
<list-item><label>&#x25FE;</label> <p><italic>entrar en conflicto</italic> con otro actualmente instalado</p></list-item>
</list>
</sec></sec>
<sec id="c15-s2-s2">
<label><bold>15.2.2.</bold></label>
<title><bold>dpkg</bold></title>
<sec id="c15-s2-s2-1">
<title><bold>dpkg</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Es la herramienta b&#x00E1;sica de gesti&#x00F3;n de paquetes, que es usada por las otras (dselect, apt-get, apt, aptitude, synaptic).</p></list-item>
<list-item><label>&#x25FE;</label> <p>Usos principales:</p>
<list list-type="bullet">
<list-item><p><code>dpkg -i paquete_VVV-RRR.deb</code></p>
<p>Instala un paquete</p></list-item>
<list-item><p><code>dpkg -r paquete</code></p>
<p>Desinstala (<italic>remove</italic>) un paquete, elimina todo excepto los ficheros de configuraci&#x00F3;n</p></list-item>
<list-item><p>dpkg -P paquete</p>
<p>Purga un paquete, eliminando incluso los ficheros de configuraci&#x00F3;n</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Tiene muchas opciones. Puede esquivarse el esquema de dependencias (peligroso) con las opciones que empiezan por <code>--force-</code>...</p></list-item>
</list>
<p><target target-type="page" id="pges_247"/>Versiones de Ubuntu:</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" colspan="2"><p><code>nombre a&#x00F1;o.mes</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>Warty Warthog 4.10</code></p></td>
<td valign="top" align="left"><p><code>Hoary Hedgehog 5.04</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>...</code></p></td>
<td valign="top" align="left"><p><code>...</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>Saucy Salamander 13.10</code></p></td>
<td valign="top" align="left"><p><code>Trusty Tahr 14.04 LTS</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>Utopic Unicorn 14.10</code></p></td>
<td valign="top" align="left"><p><code>Vivid Vervet 15.04</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>Wily Werewolf 15.10</code></p></td>
<td valign="top" align="left"><p><code>Xenial Xerus 16.04 LTS</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>Yakketiy Yak 16.10</code></p></td>
<td valign="top" align="left"><p><code>Zesty Zapus 17.04</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>Artful Aardvark 17.10</code></p></td>
<td valign="top" align="left"><p><code>Bionic Beaver 18.04 LTS</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>Cosmic Cuttlefish 18.10</code></p></td>
<td valign="top" align="left"><p><code>Disco Dingo 19.04</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>Eoan Ermine 19.10</code></p></td>
<td valign="top" align="left"><p><code>Foca Fossa 20.04 LTS</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>Groovy Gorilla 20.10</code></p></td>
<td valign="top" align="left"><p><code>Hirsute Hippo 21.04</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>Impish Indri 21.10</code></p></td>
<td valign="top" align="left"><p><code>Jammy Jellyfish 22.04 LTS</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>Kinetic Kudu 22.20</code></p></td>
<td valign="top" align="left"><p></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<p>Versi&#x00F3;n est&#x00E1;ndar: Desde 13.04, soportada durante 9 meses (18 meses en las versiones anteriores)</p>
<p>LTS: Long Term Support: soportada durante 3 a&#x00F1;os en escritorio y 5 en servidor</p>
<p>Ubuntu Desktop / Ubuntu Server Edition / Ubuntu Server Edition JeOS Variantes de Ubuntu: Kubuntu, Xubuntu, Gobuntu, Ubuntu Studio</p>
</sec>
<sec id="c15-s2-s3">
<label><bold>15.2.3.</bold></label>
<title><bold>apt</bold></title>
<sec id="c15-s2-s3-1">
<title><bold>apt</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>La herramienta m&#x00E1;s sencilla de usar y m&#x00E1;s potente.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Usa <italic>repositorios</italic>: sitios centralizados donde se almacenan paquetes</p></list-item>
<list-item><label>&#x25FE;</label> <p>Las direcciones de los repositorios se indican en el fichero
<preformat>
/etc/apt/sources.list
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Los repositorios de ubuntu se dividen en 4 componentes</p>
<list list-type="order">
<list-item><p>Main. Soportado oficialmente por ubuntu. Libre</p></list-item>
<list-item><p>Restricted. Soportado oficialmente. No libre</p></list-item>
<list-item><p>Universe. No soportado oficialmente. Libre</p></list-item>
<list-item><p><target target-type="page" id="pges_248"/><italic>Multiverse</italic>. No soportado oficialmente. No libre</p></list-item>
</list>
<p>Adem&#x00E1;s, se pueden a&#x00F1;adir componentes de terceros</p></list-item>
</list>
<preformat>
# deb cdrom:[Ubuntu 6.06 _Dapper Drake_ - Release i386 (20060531)]/ dapper main restricted
deb <ext-link ext-link-type="uri" xlink:href="http://archive.ubuntu.com/ubuntu">http://archive.ubuntu.com/ubuntu</ext-link> edgy main restricted
deb <ext-link ext-link-type="uri" xlink:href="http://security.ubuntu.com/ubuntu">http://security.ubuntu.com/ubuntu</ext-link> edgy-security main restricted
deb <ext-link ext-link-type="uri" xlink:href="http://archive.ubuntu.com/ubuntu">http://archive.ubuntu.com/ubuntu</ext-link> edgy-updates main restricted
</preformat>
<preformat>
## All community supported packages, including security- and other updates
deb <ext-link ext-link-type="uri" xlink:href="http://archive.ubuntu.com/ubuntu">http://archive.ubuntu.com/ubuntu</ext-link> edgy universe multiverse
deb <ext-link ext-link-type="uri" xlink:href="http://security.ubuntu.com/ubuntu">http://security.ubuntu.com/ubuntu</ext-link> edgy-security universe multiverse
deb <ext-link ext-link-type="uri" xlink:href="http://archive.ubuntu.com/ubuntu">http://archive.ubuntu.com/ubuntu</ext-link> edgy-updates universe multiverse
</preformat>
<preformat>
# Google Picasa for Linux repository
deb <ext-link ext-link-type="uri" xlink:href="http://dl.google.com/linux/deb/">http://dl.google.com/linux/deb/</ext-link> stable non-free
</preformat>
</sec>
<sec id="c15-s2-s3-2">
<title><bold>Uso b&#x00E1;sico de apt</bold></title>
<p>Desde l&#x00ED;nea de comandos se puede usar apt-get</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>apt-get update</code></p>
<p>Actualizar lista de paquetes:</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>apt-get upgrade</code></p>
<p>Actualizar todos los paquetes instalados a la &#x00FA;ltima versi&#x00F3;n disponible (sin cambiar de distribuci&#x00F3;n)</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>apt-get install paquete</code></p>
<p>Instalar un paquete (resolviendo conflictos)</p></list-item>
</list>
<p>En 2014 aparece la herramienta apt, con la misma finalidad e interfaz de usuario, pero que resulta un poco m&#x00E1;s f&#x00E1;cil de manejar porque unifica <code>apt-get y apt-cache</code></p>
<preformat>
apt update
apt upgrade
apt install paquete
</preformat>
<p>Aunque indiquemos a nuestro sistema de paqueter&#x00ED;a que instale la &#x00FA;ltima versi&#x00F3;n de un paquete, tal vez no sea posible. Se dice que el paquete est&#x00E1; <italic>retenido</italic> (<italic>hold</italic>)</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_249"/>El paquete depende de otro no incluido en la distribuci&#x00F3;n actual</p></list-item>
<list-item><label>&#x25FE;</label> <p>El administrador lo ha retenido <italic>a mano</italic> (no le gusta, da problemas...)
<preformat>
sudo install feta
sudo feta hold nombre_del paquete
sudo feta unhold nombre_del paquete
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p><code>apt remove paquete</code></p>
<p>Desinstala un paquete</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>apt purge paquete</code></p>
<p>Desinstala un paquete y borra su configuraci&#x00F3;n</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>apt full-upgrade</code></p>
<p>Actualiza <italic>agresivamente</italic> todos los paquetes instalados, lo que puede incluir el paso a la versi&#x00F3;n m&#x00E1;s reciente de la distribuci&#x00F3;n</p></list-item>
</list>
</sec>
<sec id="c15-s2-s3-3">
<title><bold>Otros mandatos interesantes</bold></title>
<p>En los repositorios hay muchos paquetes &#x00BF;C&#x00F3;mo saber cu&#x00E1;l necesito?</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>apt search cadena</code></p>
<p>Buscar una cadena en el nombre o descripci&#x00F3;n de un paquete. Indica el estado del paquete (instalado, no instalado, borrado...)</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>apt show paquete</code></p>
<p>Muestra descripci&#x00F3;n del paquete</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>dpkg-reconfigure paquete</code></p>
<p>Reconfigurar un paquete</p></list-item>
</list>
</sec></sec>
<sec id="c15-s2-s4">
<label><bold>15.2.4.</bold></label>
<title><bold>Sistemas de paquetes en macOS Sistemas de paquetes en macOS</bold></title>
<p>Apple no tiene previsto el uso de estos sistemas para usuarios <italic>normales</italic>. Pero s&#x00ED; son muy &#x00FA;tiles para usuarios con perfil de desarrollador o administrador. Se usan pr&#x00E1;cticamente igual que apt-get</p>
<p>Actualmente podemos optar por tres sistemas</p>
<list list-type="order">
<list-item><p><target target-type="page" id="pges_250"/>fink</p>
<p>El m&#x00E1;s antiguo. Basado en apt-get. Poco usado hoy</p></list-item>
<list-item><p>macports</p>
<p>Basado en los ports de FreeBSD. Muy completo. Muy independiente de Apple</p></list-item>
<list-item><p>homebrew</p>
<p>El m&#x00E1;s moderno y mejor integrado con Apple. Tal vez el m&#x00E1;s popular hoy</p></list-item>
</list>
</sec></sec>
<sec id="c15-s2-s5">
<label><bold>15.2.5.</bold></label>
<title><bold>Paquetes Snap Recapitulaci&#x00F3;n: ficheros tar</bold></title>
<p>Como hemos visto, la forma cl&#x00E1;sica para distribuir aplicaciones, desde finales de los a&#x00F1;os 70 es el uso de ficheros .tar o tar.gz con el c&#x00F3;digo fuente y tal vez un <italic>Makefile</italic></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Exigen mucha intervenci&#x00F3;n manual por parte del administrador de la m&#x00E1;quina</p></list-item>
<list-item><label>&#x25FE;</label> <p>El n&#x00FA;mero de dependencias es muy alto, incluyendo &#x00A1;todas las herramientas de compilaci&#x00F3;n!</p></list-item>
</list>
</sec>
<sec id="c15-s2-s5-1">
<title><bold>Recapitulaci&#x00F3;n: paquetes tradicionales</bold></title>
<p>Desde finales de los a&#x00F1;os 90, sistemas de gesti&#x00F3;n de paquetes (deb, rpm, brew, etc)</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Los responsables de cada distribuci&#x00F3;n (Debian, Ubuntu, Fedora, Suse, etc) toman la versi&#x00F3;n original de cada aplicaci&#x00F3;n (denominada <italic>upstream</italic>) y la preparan para que funcione en una versi&#x00F3;n concreta de una distribuci&#x00F3;n concreta, con todas las dependencias necesarios.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Ya no requiere trabajo por pate del administrador local, pero s&#x00ED; de los responsables de la distribuci&#x00F3;n</p></list-item>
<list-item><label>&#x25FE;</label> <p>Para instalar un paquete, es necesario</p>
<list list-type="bullet">
<list-item><p>Disponer de la versi&#x00F3;n exacta para la distribuci&#x00F3;n exacta</p></list-item>
<list-item><p><target target-type="page" id="pges_251"/>Que ese paquete est&#x00E9; correctamente preparado</p></list-item>
<list-item><p>Que el estado de mis paquetes sea consistente (a veces hay conflictos, paquetes mal instalados, etc)</p></list-item>
</list></list-item>
</list>
</sec>
<sec id="c15-s2-s5-2">
<title><bold>Paquetes Snap</bold></title>
<p>En 2014 Canonical desarrolla los paquetes Snap, que est&#x00E1;n ganando popularidad recientemente</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Un &#x00FA;nico paquete Snap incluye todo lo necesario para que funcione la aplicaci&#x00F3;n, con todas sus dependencias en cualquier distribuci&#x00F3;n Linux, sin necesidad de adaptar nada</p></list-item>
<list-item><label>&#x25FE;</label> <p>Un paquete Snap incluye un fichero que se monta en el sistema de ficheros, que se descomprime sobre la marcha</p></list-item>
<list-item><label>&#x25FE;</label> <p>Cada aplicaci&#x00F3;n se actualiza autom&#x00E1;ticamente</p></list-item>
<list-item><label>&#x25FE;</label> <p>La aplicaci&#x00F3;n se ejecuta de forma aislada dentro de un <italic>sandbox</italic>, con acceso limitado al host</p></list-item>
</list>
<p>Inconvenientes:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>La integraci&#x00F3;n con el resto del sistema puede ser peor</p></list-item>
<list-item><label>&#x25FE;</label> <p>El tiempo necesario para comenzar a ejecutar la aplicaci&#x00F3;n puede ser peor</p></list-item>
</list>
</sec>
<sec id="c15-s2-s5-3">
<title><bold>Uso de Snap</bold></title>
<p>Podemos consultar todos los <italic>snaps</italic> disponibles en la <italic>Snap Store</italic></p>
<p><ext-link ext-link-type="uri" xlink:href="https://snapcraft.io/">https://snapcraft.io</ext-link></p>
<p>El manejo es muy sencillo</p>
<preformat>
snap find &#x003C;palabras clave&#x003E;
sudo snap install &#x003C;paquete&#x003E;
snap list
sudo snap remove &#x003C;paquete&#x003E;
</preformat>
<p>M&#x00E1;s informaci&#x00F3;n: <ext-link ext-link-type="uri" xlink:href="https://snapcraft.io/docs/getting-started">https://snapcraft.io/docs/getting-started</ext-link></p>
</sec></sec>
<sec id="c15-s3">
<label><bold>15.3.</bold></label>
<title><target target-type="page" id="pges_252"/><bold>B&#x00FA;squeda de ficheros</bold></title>
<sec id="c15-s3-1">
<title><bold>Localizar ficheros</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>find Busca un fichero</p></list-item>
<list-item><label>&#x25FE;</label> <p>find . | grep fichero Filtra la b&#x00FA;squeda</p></list-item>
<list-item><label>&#x25FE;</label> <p>locate Busca un fichero (en una base de datos)</p></list-item>
<list-item><label>&#x25FE;</label> <p>updatedb Actualiza la base de datos</p></list-item>
</list>
</sec></sec>
<sec id="c15-s4">
<label><bold>15.4.</bold></label>
<title><bold>Hora. Parada del sistema</bold></title>
<sec id="c15-s4-1">
<title><bold>Hora. Parada del sistema</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>shutdown -P now &#x2261; poweroff</code></p>
<p>Apaga el sistema</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>shutdown -r now &#x2261; reboot</code></p>
<p>Reinicia el sistema</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>sleep n</code></p>
<p>Duerme la shell segundos</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>sleep 28800 ; halt</code></p>
<p>Detiene la m&#x00E1;quina al cabo de 8 horas</p></list-item>
<list-item><label>&#x25FE;</label> <p>Poner fecha y hora:</p>
<list list-type="bullet">
<list-item><p>Autom&#x00E1;ticamente: Demonio <italic>ntpd</italic>, cliente de <italic>Network Time Protocol</italic></p></list-item>
<list-item><p>Manualmente
<preformat>
date -s AAAA-MM-DD
date -s HH:MM</preformat></p></list-item>
</list></list-item>
</list>
<p>Casi siempre hay varias soluciones para una tarea. Generales o particulares</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>find . |grep cadena</code>
<preformat>
find . -name cadena\*
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p><code>sleep 60 | shutdown -h now</code>
<preformat>
shutdown -h 1
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>etc, etc</p></list-item>
</list>
<p><target target-type="page" id="pges_253"/>Todas sirven. &#x00BF;No? &#x00BF;C&#x00FA;al es <italic>mejor</italic> ?</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Cuando somos novatos en un sistema, con una soluci&#x00F3;n general sabremos resolver ese problema y otros parecidos</p></list-item>
<list-item><label>&#x25FE;</label> <p>Cuando conocemos mejor un sistema y dominamos las soluciones generales, las soluciones particulares suelen ser m&#x00E1;s eficientes</p></list-item>
</list>
</sec></sec>
<sec id="c15-s5">
<label><bold>15.5.</bold></label>
<title><bold>Copias de seguridad</bold></title>
<sec id="c15-s5-1">
<title><bold>Copias de seguridad</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>tar</code> o similares</p>
<p>Problema: Siempre se duplican los directorios enteros</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>rsync</code></p>
<p>Mirror unidireccional. Permite mantener una r&#x00E9;plica de un directorio. Solo se actualizan las novedades. No permite modificar la r&#x00E9;plica</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>FreeFileSync, Synkron</code></p>
<p>Herramientas libres para sincronizaci&#x00F3;n bidireccional (Windows, Linux, OS X).</p>
<p>Sincronizan dos (o m&#x00E1;s) directorios: Cualquiera de los dos directorios puede modificarse</p></list-item>
<list-item><label>&#x25FE;</label> <p>Sistemas de almacenamiento permanente</p>
<p><code>Time Machine</code> (OS X)</p>
<p><code>dumpfs</code> (bsd)</p>
<p><code>pdumpfs</code> (Linux, Windows)</p>
<p><code>TimeVault, FlyBack</code> (Linux)</p>
<p><code>venti</code> (Plan 9)</p>
<list list-type="bullet">
<list-item><p>Se registran los cambios en los ficheros, sin borrar nunca nada</p></list-item>
<list-item><p>Mantienen una <italic>foto</italic> del estado diario del sistema de ficheros, en un directorio con formato yyyy/mm/dd</p></list-item>
<list-item><p>Parece mucho, pero hoy el almacenamiento es muy barato. P.e. si generamos 10 Mb diarios, necesitamos unos 4Gb anuales</p></list-item>
</list></list-item>
</list>
</sec></sec>
<sec id="c15-s6">
<label><bold>15.6.</bold></label>
<title><target target-type="page" id="pges_254"/><bold>Administraci&#x00F3;n de los demonios</bold></title>
<sec id="c15-s6-1">
<title><bold>Administraci&#x00F3;n de los demonios</bold></title>
<p>Los demonios son programas relativamente <italic>normales</italic>, con algunas particularidades</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Ofrecen servicios (impresi&#x00F3;n, red, ejecuci&#x00F3;n peri&#x00F3;dica de tareas, logs, etc)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Suelen estar creados por el proceso de arranque <italic>init</italic> (ppid=1)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Sus nombres suelen acaban en <italic>d</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>Se ejecutan en <italic>background</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>No est&#x00E1;n asociados a un usuario en una terminal</p></list-item>
<list-item><label>&#x25FE;</label> <p>El grueso de su configuraci&#x00F3;n suele hacerse desde un &#x00FA;nico fichero En el caso de debian, <code>/etc/midemonio.conf</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>Se inician y se detienen de manera uniforme</p></list-item>
</list>
</sec>
<sec id="c15-s6-2">
<title><bold>Unix System V</bold></title>
<p>Versi&#x00F3;n de Unix comercializada en 1983 por AT&#x0026;T</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>La mayor&#x00ED;a de los Unix, incluyendo Linux, son derivados de System V</p></list-item>
<list-item><label>&#x25FE;</label> <p>Otros Unix son derivados del Unix BSD de aquella &#x00E9;poca: esto incluye los BSD actuales y OS X (Apple)</p></list-item>
</list>
<p>System V introduce una forma de organizar los demonios basada en</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><italic>Niveles de ejecuci&#x00F3;n</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>Scripts en <code>/etc/init.d</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>Ordenaci&#x00F3;n lineal de sucesos:</p>
<p>Las tareas se ordenan secuencialmente en orden preestablecido, solo cuando una est&#x00E1; completamente acabada empieza la siguiente</p></list-item>
</list>
</sec></sec>
<sec id="c15-s6-3">
<title><target target-type="page" id="pges_255"/><bold>Systemd</bold></title>
<sec id="c15-s6-s1">
<label><bold>15.6.1.</bold></label>
<title><bold>Systemd</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>El sistema de arranque tradicional de Linux (System V) no es adecuado para las m&#x00E1;quinas actuales</p>
<list list-type="bullet">
<list-item><p>Son externos: aparecen y desaparecen</p></list-item>
<list-item><p>Est&#x00E1;n en red</p></list-item>
<list-item><p>Ahorran energ&#x00ED;a</p></list-item>
<list-item><p>. . .</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p><italic>Systemd</italic> es un sistema de arranque basado en eventos (pueden suceder en cualquier orden, puede haber tareas en paralelo)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Mantiene una capa adicional de software para que las &#x00F3;rdenes al estilo System V sigan funcionando</p></list-item>
<list-item><label>&#x25FE;</label> <p>El desarrollo de Systemd lo compienza Red Hat en el a&#x00F1;o 2010</p></list-item>
<list-item><label>&#x25FE;</label> <p>En Ubuntu solamente se utiliza desde la versi&#x00F3;n 15.04 (a&#x00F1;o 2015). Anteriormente empleaba <italic>upstart</italic>, un sistema similar, tambi&#x00E9;n compatible con System V</p></list-item>
</list>
</sec>
<sec id="c15-s6-s1-1">
<title><bold>Ficheros de configuraci&#x00F3;n</bold></title>
<p>Los ficheros donde se configura un demonio son:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Fichero principal de configuraci&#x00F3;n
<preformat>
/etc/midemonio.conf
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Configuraci&#x00F3;n de puesta en marcha y parada</p>
<list list-type="bullet">
<list-item><p>System V
<preformat>
/etc/init.d/midemonio
</preformat></p></list-item>
<list-item><p>Systemd
<preformat>
/etc/init/midemonio.conf
</preformat></p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Configuraci&#x00F3;n del administrador local
<preformat>
/etc/default/midemonio
</preformat></p>
<p>Solo existe en Debian y derivados (tambi&#x00E9;n en Ubuntu con Upstart). No lo usan todos los paquetes</p></list-item>
</list>
</sec>
<sec id="c15-s6-s1-2">
<title><target target-type="page" id="pges_256"/><bold>Directorio /etc/default</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>La inmensa mayor&#x00ED;a de los par&#x00E1;metros de <code>/etc/midemonio.conf</code> y de <code>/etc/init.d/midemonio</code> o de <code>/etc/init/midemonio.conf</code> los ha escrito el desarrollador del demonio o el empaquetador de la distribuci&#x00F3;n, es normal que el administrador local de cada m&#x00E1;quina concreta solo modifique unos pocos</p></list-item>
<list-item><label>&#x25FE;</label> <p>En alg&#x00FA;n momento habr&#x00E1; que actualizar el demonio a una versi&#x00F3;n nueva, que frecuentemente incluir&#x00E1; cambios en sus ficheros de configuraci&#x00F3;n, escritos por el desarrollador o el empaquetador</p>
<list list-type="bullet">
<list-item><p>&#x00BF;Instalamos los ficheros nuevos y <italic>machacamos</italic> los viejos? Problema: se pierde la configuraci&#x00F3;n que ha personalizado el administrador local</p></list-item>
<list-item><p>&#x00BF;Mantenemos los viejos y descartamos los nuevos?</p>
<p>Problema: se pierden los cambios de la versi&#x00F3;n actual, el fichero de configuraci&#x00F3;n (antiguo) podr&#x00ED;a incluso se incompatible con el demonio (actual)</p></list-item>
</list></list-item>
</list>
<p>Soluci&#x00F3;n: fichero /etc/default/midemonio</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Es un fichero muy corto, con muy pocos par&#x00E1;metros, muy importantes, que se sabe que ser&#x00E1;n modificados por el administrador local</p></list-item>
<list-item><label>&#x25FE;</label> <p>Cuando se instalan versiones nuevas del demonio, este fichero se mantiene</p></list-item>
<list-item><label>&#x25FE;</label> <p>Los cambios introducidos por las nuevas versiones de los demonios estar&#x00E1;n en
<preformat>
/etc/midemonio.conf o en /etc/init.d/midemonio o /etc/init/midemonio.conf
</preformat></p></list-item>
</list>
</sec>
<sec id="c15-s6-s1-3">
<title><bold>Administraci&#x00F3;n estilo System V</bold></title>
<p>El c&#x00F3;digo de un demonio puede estar en cualquier lugar del sistema de ficheros. Pero siempre se coloca en /etc/init.d/midemonio un script para manejarlo</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>/etc/init.d/midemonio start</code></p>
<p>Inicia el servicio</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>/etc/init.d/midemonio stop</code></p>
<p>Detiene el servicio</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_257"/><code>/etc/init.d/midemonio restart</code></p>
<p>Detiene e inicia el servicio. Suele ser <bold>necesario para releer los ficheros de configuraci&#x00F3;n</bold> si se han modificado (/etc/midemonio.conf)</p></list-item>
</list>
<p>Con frecuencia tambi&#x00E9;n est&#x00E1; disponible</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>/etc/init.d/midemonio reload</code></p>
<p>Lee el fichero de configuraci&#x00F3;n sin detener el servicio</p></list-item>
</list>
</sec></sec>
<sec id="c15-s6-s2">
<label><bold>15.6.2.</bold></label>
<title><bold>Niveles de ejecuci&#x00F3;n</bold></title>
<sec id="c15-s6-s2-1">
<title><bold>Niveles de ejecuci&#x00F3;n</bold></title>
<p>&#x00BF;Qu&#x00E9; demonios se ponen en marcha cuando se inicia el sistema?</p>
<p>Un Nivel de ejecuci&#x00F3;n (<italic>runlevel</italic>) es una configuraci&#x00F3;n de arranque. Para cada nivel, se define un conjunto de demonios que deben ejecutarse</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Este es un concepto de System V, en Systemd se usan targets, que son equivalentes</p></list-item>
</list>
<p>Supongamos una f&#x00E1;brica. Diferentes niveles (estados), no secuenciales. Al entrar en un nivel se apagan ciertos sistemas y se encienden otros</p>
<preformat>
Nivel 1 - Noche
&#x00A0;&#x00A0;&#x00A0;Al entrar en este nivel apagar
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;01 motores
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;02 luces principales
&#x00A0;&#x00A0;&#x00A0;Al entrar en este nivel encender
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;01 alarma
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;02 luces_auxiliares Nivel 2 - Producci&#x00F3;n normal
&#x00A0;&#x00A0;&#x00A0;Al entrar en este nivel apagar
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;01 alarma
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;02 luces auxiliares
&#x00A0;&#x00A0;&#x00A0;Al entrar en este nivel encender
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;....
Nivel 3- Mantenimiento
&#x00A0;&#x00A0;&#x00A0;Al entrar en este nivel apagar
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;01 motores
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;....
</preformat>
<p>El responsable de conectar y desconectarlo todo ser&#x00E1; el vigilante de seguridad, as&#x00ED; que hay que dejarle unas instrucciones muy claras:</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><target target-type="page" id="pges_258"/><code>ordinal</code></p></td>
<td valign="top" align="left"><p><code>[encender|apagar]</code></p></td>
<td valign="top" align="left"><p><code>nombre_del_sistema</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><bold>C&#x00F3;digo</bold></p></td>
<td valign="top" align="left"><p><bold>Significado</bold></p></td>
<td valign="top" align="left"><p><bold>Mandato</bold></p></td>
</tr>
<tr>
<td valign="top" align="left"><p>S10motor-ppal</p></td>
<td valign="top" align="left"><p>1&#x00B0; encender motor principal</p></td>
<td valign="top" align="left"><p><code>/etc/init.d/motor-ppal start</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p>S20motor-aux</p></td>
<td valign="top" align="left"><p>2&#x00B0; encender motor auxiliar</p></td>
<td valign="top" align="left"><p><code>/etc/init.d/motor-aux start</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p>K10alarma</p></td>
<td valign="top" align="left"><p>1&#x00B0; apagar alarma</p></td>
<td valign="top" align="left"><p><code>/etc/init.d/alarma stop</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<p>Dentro de cada nivel, las tareas se ordenan desde 00 hasta 99 (con un cero a la izquierda para los valores del 0 al 9)</p>
<p>El <italic>vigilante de seguridad</italic> es el proceso <code>init</code>.</p>
<p>Hay un directorio por nivel:</p>
<p>Debian y derivados</p>
<preformat>
/etc/rc0.d
/etc/rc1.d
...
</preformat>
<p>Red Hat y derivados</p>
<preformat>
/etc/rc.d/rc0.d
/etc/rc.d/rc1.d
...
</preformat>
<p>Hay otro directorio cuyos servicios se activan siempre, en cualquier nivel</p>
<preformat>
/etc/rcS.d
</preformat>
<p>Dentro de los directorios hay enlaces simb&#x00F3;licos</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Apuntan al script en <code>/etc/init.d</code> que controla el demonio</p></list-item>
<list-item><label>&#x25FE;</label> <p>Cada nombre del enlace indica conexi&#x00F3;n/desconexi&#x00F3;n, ordinal y script a manejar</p></list-item>
</list>
<p>Cuando entra en el nivel N, el proceso init se encarga de</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Ejecutar por orden todos los scripts en <code>/etc/rcN.d</code> que empiezen por K (de Kill). Les pasa el par&#x00E1;metro <italic>stop</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>A continuaci&#x00F3;n, ejecuta por orden todos los scripts en <code>/etc/rcN.d</code> que empiezen por S (de Start). Les pasa el par&#x00E1;metro <italic>start</italic></p></list-item>
</list>
<preformat>
who -r
</preformat>
<p>Indica el nivel de ejecuci&#x00F3;n actual</p>
<preformat>
0 Halt (Parada del sistema)
1 Modo monousuario, usuario root, sin red
<target target-type="page" id="pges_259"/>2-4 Diversos modos multiusuario, sin gr&#x00E1;ficos
5 Modo multiusuario completo, con X Window
6 Reboot (Reiniciar el sistema)
</preformat>
<p>Ejemplo del contenido de /etc/rc2.d/</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>S10acpid</code></p></td>
<td valign="top" align="left"><p><code>S18hplip</code></p></td>
<td valign="top" align="left"><p><code>S20postfix</code></p></td>
<td valign="top" align="left"><p><code>S89atd</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>S10powernowd.early</code></p></td>
<td valign="top" align="left"><p><code>S19cupsys</code></p></td>
<td valign="top" align="left"><p><code>S20powernowd</code></p></td>
<td valign="top" align="left"><p><code>S89cron</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>S10sysklogd</code></p></td>
<td valign="top" align="left"><p><code>S20apmd</code></p></td>
<td valign="top" align="left"><p><code>S20rsync</code></p></td>
<td valign="top" align="left"><p><code>S90binfmt-support</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>S10wacom-tools</code></p></td>
<td valign="top" align="left"><p><code>S20festival</code></p></td>
<td valign="top" align="left"><p><code>S20ssh</code></p></td>
<td valign="top" align="left"><p><code>S98usplash</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<p>Ejemplo del contenido de <code>/etc/rc6.d/</code></p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>K19cupsys</code></p></td>
<td valign="top" align="left"><p><code>K25mdadm</code></p></td>
<td valign="top" align="left"><p><code>S15wpa-ifupdown</code></p></td>
<td valign="top" align="left"><p><code>S50lvm</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>K19setserial</code></p></td>
<td valign="top" align="left"><p><code>K25nfs-user-server</code></p></td>
<td valign="top" align="left"><p><code>S20sendsigs</code></p></td>
<td valign="top" align="left"><p><code>S50mdadm-raid</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>K20dbus</code></p></td>
<td valign="top" align="left"><p><code>K30etc-setserial</code></p></td>
<td valign="top" align="left"><p><code>S30urandom</code></p></td>
<td valign="top" align="left"><p><code>S60umountroot</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>K20laptop-mode</code></p></td>
<td valign="top" align="left"><p><code>K50alsa-utils</code></p></td>
<td valign="top" align="left"><p><code>S31umountnfs.sh</code></p></td>
<td valign="top" align="left"><p><code>S90halt</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<p>Resumiendo, para ejecutar autom&#x00E1;ticamente un demonio manejamos 3 ficheros</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>El fichero con el ejecutable del demonio</p>
<p>p.e.</p>
<p><code>/usr/sbin/sshd</code></p>
<list list-type="bullet">
<list-item><p>Si se trata de un servicio que no es est&#x00E1;ndar en la distribuci&#x00F3;n, su sitio es el directorio <code>/usr/local</code></p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>El script que maneja el demonio</p>
<list list-type="bullet">
<list-item><p>Acepta los par&#x00E1;metros <code>start, stop, reload, ...</code> y llama el demonio en consecuencia</p></list-item>
</list>
<p>p.e.</p>
<p><code>/etc/init.d/ssh</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>El enlace, dentro del directorio correspondiente al nivel, que apunta al script</p>
<p>p.e.</p>
<p><code>/etc/rc5.d/S02ssh</code></p>
<p>apuntando a</p>
<p><code>/etc/init.d/ssh</code></p></list-item>
</list>
</sec></sec>
<sec id="c15-s7">
<label><bold>15.7.</bold></label>
<title><target target-type="page" id="pges_260"/><bold>Consulta de logs</bold></title>
<p>Los demonios no muestran informaci&#x00F3;n ni en la consola ni en ninguna aplicaci&#x00F3;n gr&#x00E1;fica</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Los demonios usan el demonio syslogd o sysklogd para notificar y almacenar informaci&#x00F3;n relevante: inicio, parada, estado, peticiones, respuestas, errores, etc</p>
<p>A partir del a&#x00F1;o 2009 es m&#x00E1;s habitual emplear <italic>rsyslogd</italic>, muy similar a <italic>syslogd</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>Todo esto se escribe en diversos ficheros de texto, siendo los m&#x00E1;s interesantes</p>
<list list-type="bullet">
<list-item><p>/var/log/syslog</p>
<p>Informaci&#x00F3;n general del sistema</p></list-item>
<list-item><p>/var/log/auth.log</p>
<p>Informaci&#x00F3;n sobre autenticaci&#x00F3;n de usuarios</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Podemos ver un fichero cualquiera, p.e. un log, con <italic>cat</italic>
<preformat>
cat /var/log/syslog
</preformat></p>
<p>(Muestra un fichero entero)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Es m&#x00E1;s pr&#x00E1;ctico usar <italic>tail</italic>
<preformat>
tail -20 /var/log/syslog
</preformat></p>
<p>(Muestra las &#x00FA;ltimas 20 l&#x00ED;nea)
<preformat>
tail /var/log/syslog
</preformat></p>
<p>(Muestra las &#x00FA;ltimas 10 l&#x00ED;nea)</p></list-item>
</list>
<p>O mejor a&#x00FA;n, si queremos monitorizar continuamente un log, abrimos un terminal exclusivamente para esto y ejecutamos</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>tail -f /var/log/syslog</code></p></list-item>
</list>
<p>(Muestra las &#x00FA;ltimas l&#x00ED;neas, se queda esperando a que haya novedades en el fichero, y cuando las hay, las muestra tambi&#x00E9;n)</p>
<p>Si estamos depurando un servicio y tenemos una sesi&#x00F3;n gr&#x00E1;fica, puede ser &#x00FA;til esta disposici&#x00F3;n del escritorio: dejar un terminal siempre visible, ejecutando <code>tail -f</code>, y trabajar en otro terminal con varias pesta&#x00F1;as</p>
<fig><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-28.jpg"/></fig>
<p><target target-type="page" id="pges_261"/>Si no tenemos gr&#x00E1;ficos, podemos pulsar Alt F2, Alt F3, etc, abrir una sesi&#x00F3;n y dedicarla a los logs (tambi&#x00E9;n dentro de VirtualBox)</p>
</sec>
<sec id="c15-s8">
<label><bold>15.8.</bold></label>
<title><bold>Tareas peri&#x00F3;dicas: cron</bold></title>
<sec id="c15-s8-1">
<title><bold>Tareas peri&#x00F3;dicas</bold></title>
<sec id="c15-s8-s1">
<label><bold>15.8.1.</bold></label>
<title><bold>Tareas peri&#x00F3;dicas</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Automatizan la gesti&#x00F3;n del sistema</p></list-item>
<list-item><label>&#x25FE;</label> <p>Fiabilidad. Protegen frente a olvidos</p></list-item>
<list-item><label>&#x25FE;</label> <p>Se ejecutan en el momento preciso (d&#x00ED;a y hora)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Ayudan o detectan situaciones de error</p></list-item>
<list-item><label>&#x25FE;</label> <p>Facilitan el control del sistema</p></list-item>
<list-item><label>&#x25FE;</label> <p>Programas:</p>
<list list-type="bullet">
<list-item><p><code>cron</code></p></list-item>
<list-item><p><code>anacron.</code> Permite ejecutar algo programado para un momento en que el sistema estaba apagado</p></list-item>
<list-item><p><code>at.</code> Ejecuta una tarea a la hora indicada por <italic>stdin</italic></p></list-item>
</list></list-item>
</list>
<p><target target-type="page" id="pges_262"/>Usos de las tareas peri&#x00F3;dicas</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Generaci&#x00F3;n de informes peri&#x00F3;dicos (fin de mes, etc.)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Estado de las comunicaciones</p></list-item>
<list-item><label>&#x25FE;</label> <p>Borrado de ficheros temporales (<code>/tmp, /var/tmp</code>)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Tareas de respaldo de informaci&#x00F3;n</p></list-item>
<list-item><label>&#x25FE;</label> <p>Control de los procesos presentes en el sistema</p></list-item>
<list-item><label>&#x25FE;</label> <p>Parada del sistema seg&#x00FA;n horarios de trabajo</p></list-item>
<list-item><label>&#x25FE;</label> <p>Recordatorios</p></list-item>
<list-item><label>&#x25FE;</label> <p>Descarga de <italic>software</italic> en horarios de poco tr&#x00E1;fico</p></list-item>
</list>
</sec>
<sec id="c15-s8-s1-1">
<title><bold>cron</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Es uno de los demonios esenciales de un sistema, siempre est&#x00E1; arrancado (<code>/usr/sbin/cron</code>)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Se encarga de ejecutar tareas programadas para un determinado momento, bajo la identidad del usuario que lo program&#x00F3; y con precisi&#x00F3;n de 1 minuto</p></list-item>
<list-item><label>&#x25FE;</label> <p>Se controla a trav&#x00E9;s del uso de determinados ficheros de configuraci&#x00F3;n (solo para el superusuario) y mediante el uso de la orden <code>&#x201C;crontab&#x201D;</code> (para todos los usuarios).</p></list-item>
</list>
</sec></sec>
<sec id="c15-s8-s2">
<label><bold>15.8.2.</bold></label>
<title><bold>Tabla de cron</bold></title>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" colspan="7"><p>SHELL=/bin/bash</p></td>
</tr>
<tr>
<td valign="top" align="left" colspan="7"><p>MAILTO=koji</p></td>
</tr>
<tr>
<td valign="top" align="left" colspan="7"><p>PATH=/usr/local/bin:/usr/bin:/bin</p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>#</code></p></td>
<td valign="top" align="left"><p><code>m</code></p></td>
<td valign="top" align="left"><p><code>h</code></p></td>
<td valign="top" align="left"><p><code>dayofmonth</code></p></td>
<td valign="top" align="left"><p><code>month</code></p></td>
<td valign="top" align="left"><p><code>dow</code></p></td>
<td valign="top" align="left"><p><code>command</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p></p></td>
<td valign="top" align="left"><p><code>16</code></p></td>
<td valign="top" align="left"><p><code>*</code></p></td>
<td valign="top" align="left"><p><code>*</code></p></td>
<td valign="top" align="left"><p><code>*</code></p></td>
<td valign="top" align="left"><p><code>*</code></p></td>
<td valign="top" align="left"><p><code>ping193.147.71.119 -c 1</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
<td valign="top" align="left"><p><code>9</code></p></td>
<td valign="top" align="left"><p><code>4</code></p></td>
<td valign="top" align="left"><p><code>8</code></p></td>
<td valign="top" align="left"><p><code>*</code></p></td>
<td valign="top" align="left"><p><code>echo "regar plantas"</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
<td valign="top" align="left"><p><code>15,18</code></p></td>
<td valign="top" align="left"><p><code>*</code></p></td>
<td valign="top" align="left"><p><code>*</code></p></td>
<td valign="top" align="left"><p><code>1-5</code></p></td>
<td valign="top" align="left"><p><code>echo "hora de salir" | wall</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<p><target target-type="page" id="pges_263"/>m: Minuto. De 0 a 59</p>
<p>h: Hora. De 0 a 23</p>
<p>dayofmonth: de 0 a 31</p>
<p>month: de 1 a 12</p>
<p>dayofweek: de 0 a 7. 0=7=domingo, 1=lunes, 2=martes. . .</p>
<p>Cada l&#x00ED;nea es una tarea</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Se pueden poner comentarios con <code>#</code> pero no en cualquier posici&#x00F3;n, solo siguiendo el patr&#x00F3;n <italic>principio de l&#x00ED;nea, 0 o m&#x00E1;s espacios, almohadilla</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>En las asignaciones <code>variable=valor</code>, el valor no se expande. Todo lo que hay a la derecha del igual se toma literalmente. Por tanto, no pueden hacerse cosas como p.e. <code>PATH=$HOME/bin:$PATH</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>Se puede usar la variable de entorno <code>$PATH</code> en un comando. Ejemplo:
<preformat>
0 18 * * * $HOME/blabla/bla.py
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Es necesario dejar una linea en blanco al final de la tabla</p></list-item>
</list>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>*</code></p></td>
<td valign="top" align="left"><p><code>-&#x003E; todos</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>1-4</code></p></td>
<td valign="top" align="left"><p><code>-&#x003E; 1,2,3 y 4</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>1,4</code></p></td>
<td valign="top" align="left"><p><code>-&#x003E; 1 y 4</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>*/3</code></p></td>
<td valign="top" align="left"><p><code>-&#x003E; cada 3</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>1-15/3</code></p></td>
<td valign="top" align="left"><p><code>-&#x003E; los primeros 15, cada 3</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<p>Ejemplos y contraejemplos:</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>#</code></p></td>
<td valign="top" align="left"><p><code>m</code></p></td>
<td valign="top" align="left"><p><code>h</code></p></td>
<td valign="top" align="left"><p><code>dayofmonth</code></p></td>
<td valign="top" align="left"><p><code>month</code></p></td>
<td valign="top" align="left"><p><code>dow</code></p></td>
<td valign="top" align="left"><p><code>command</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p></p></td>
<td valign="top" align="left"><p><code>*</code></p></td>
<td valign="top" align="left"><p><code>14-15</code></p></td>
<td valign="top" align="left"><p><code>*</code></p></td>
<td valign="top" align="left"><p><code>*</code></p></td>
<td valign="top" align="left"><p><code>*</code></p></td>
<td valign="top" align="left"><p><code>echo &#x201C;OJO: de 14 a 15:59&#x201D;</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p></p></td>
<td valign="top" align="left"><p><code>*</code></p></td>
<td valign="top" align="left"><p><code>23-7</code></p></td>
<td valign="top" align="left"><p><code>*</code></p></td>
<td valign="top" align="left"><p><code>*</code></p></td>
<td valign="top" align="left"><p><code>*</code></p></td>
<td valign="top" align="left"><p><code>echo &#x201C;RANGO ILEGAL, 23&#x003E;7&#x201D;</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>crontab -e</code></p>
<p>Edita la tabla de cron del usuario. Usa el editor por omisi&#x00F3;n (normalmente vi). Podemos usar otro cambiando la variable de entorno EDITOR</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>crontab -l</code></p>
<p>Muestra tabla de cron</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>crontab mi_tabla</code></p>
<p>El fichero <italic>mi_tabla</italic> pasa a ser nueva tabla de cron</p></list-item>
</list>
</sec>
<sec id="c15-s8-s2-1">
<title><target target-type="page" id="pges_264"/><bold>Ambig&#x00FC;edades en la especificaci&#x00F3;n del momento de ejecuci&#x00F3;n</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>El d&#x00ED;a en el que se ejecuta cada orden se puede indicar de 2 maneras:</p>
<list list-type="bullet">
<list-item><p>d&#x00ED;a del mes (3<sup>er</sup> <italic></italic>campo)</p></list-item>
<list-item><p>d&#x00ED;a de la semana (5<sup>o</sup> <italic></italic>campo)</p></list-item>
</list></list-item>
</list>
<p>En caso de aparecer los dos campos (esto es, que ninguno es <code>&#x201C;*&#x201D;</code>), la interpretaci&#x00F3;n que hace <code>cron</code> es que la orden debe ejecutarse cuando se cumpla <italic>cualquiera</italic> de ellos</p>
<p>Ejemplo:</p>
<p><code>0,30 * 13 * 5 echo &#x2019;Viernes 13!&#x2019; | wall</code></p>
<p>(ejecuta la orden cada media hora, todos los viernes y adem&#x00E1;s todos los d&#x00ED;as 13 de cada mes)</p>
</sec>
<sec id="c15-s8-s2-2">
<title><bold>Momentos &#x201C;especiales&#x201D; (solo Linux)</bold></title>
<p>En lugar de especificar los 5 primeros campos, se puede usar una cadena de las siguientes:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>@reboot:</code> Se ejecuta al iniciarse la m&#x00E1;quina.</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>@yearly:</code> Se ejecuta una vez al a&#x00F1;o.</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>@monthly:</code> Se ejecuta una vez al mes.</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>@weekly:</code> Se ejecuta una vez por semana.</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>@daily:</code> Se ejecuta una vez al d&#x00ED;a.</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>@hourly:</code> Se ejecuta una vez por hora.</p></list-item>
</list>
</sec>
<sec id="c15-s8-s2-3">
<title><bold>Entorno de ejecuci&#x00F3;n de las tareas</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Cada tarea de cron se ejecuta por una <italic>shell</italic> <code>/bin/sh.</code> (a menos que definamos otra cosa en SHELL)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Causa de <bold>errores frecuentes:</bold> El PATH con el que cron busca el mandato no es el del usuario, sino <code>/usr/bin:/bin.</code> Soluciones:</p>
<list list-type="bullet">
<list-item><p>Indicar PATH en la tabla</p></list-item>
<list-item><p><target target-type="page" id="pges_265"/>Especificar el path abosoluto del mandato (p.e. <code>/usr/local/bin/mimandato</code>)</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Quien ejecuta las tareas no es el due&#x00F1;o de la tabla, sino cron. Aunque emplea algunas variables de entorno del due&#x00F1;o de la tabla, como <code>LOGNAME y HOME</code>.</p></list-item>
<list-item><label>&#x25FE;</label> <p>La entrada est&#x00E1;ndar de cada tarea se redirige de <code>/dev/null</code>, la salida est&#x00E1;ndar y la de error se env&#x00ED;an por correo electr&#x00F3;nico al propietario de la tarea (si hay servidor de correo)<target target-type="page" id="pges_266"/></p></list-item>
</list>
</sec></sec>
</body>
</book-part>
<book-part id="c16" book-part-type="chapter">
<book-part-meta>
<book-part-id book-part-id-type="publisher-id">URJC</book-part-id>
<title-group>
<label>16.</label>
<title><target target-type="page" id="pges_267"/>OPENSSH</title>
</title-group>
</book-part-meta>
<body>
<sec id="c15-s8-s2-4">
<title><bold>OpenSSH</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>PGP: Pretty Good Privacy. Software criptogr&#x00E1;fico creado por Phil Zimmermann, a&#x00F1;o 1991. Base de la norma Open PGP.</p></list-item>
<list-item><label>&#x25FE;</label> <p>GPG: GNU Privacy Guard. Herramienta para cifrado y firmas digitales, que reemplaza a PGP</p></list-item>
<list-item><label>&#x25FE;</label> <p>Se puede emplear algoritmos como RSA o ed25519 (algo m&#x00E1;s moderno), ambos se consideran seguros. El algoritmo DSA ya no es recomendable</p></list-item>
<list-item><label>&#x25FE;</label> <p>Las distribuciones orientadas a sistemas empotrados no suelen usar OpenSSH sino Dropbear, un cliente y un servidor de ssh, compatible con OpenSSH, m&#x00E1;s ligero</p></list-item>
</list>
</sec>
<sec id="c16-s1">
<label><bold>16.1.</bold></label>
<title><bold>Criptograf&#x00ED;a de clave p&#x00FA;blica</bold></title>
<sec id="c16-s1-1">
<title><bold>Criptograf&#x00ED;a de clave p&#x00FA;blica</bold></title>
<p>Aparece con el algoritmo Diffie-Hellman, a&#x00F1;o 1976</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Clave de cifrado o p&#x00FA;blica <italic>E</italic> y de descifrado o privada <italic>D</italic> distintas (asim&#x00E9;tricas)</p></list-item>
<list-item><label>&#x25FE;</label> <p><italic>D(E(P)) = P</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>Muy dificil romper el sistema (p.e. obtener <italic>D</italic>) teniendo <italic>E</italic>.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Permite intercambiar claves por canal no seguro</p></list-item>
<list-item><label>&#x25FE;</label> <p>La clave privada sirve para descifrar. Debe mantenerse en secreto</p></list-item>
<list-item><label>&#x25FE;</label> <p>La clave p&#x00FA;blica sirve para cifrar. Puede conocerla todo el mundo (lo importante es que se conozca la clave correcta)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Conociendo la clave p&#x00FA;blica de alguien, podemos cifrar un mensaje que solo &#x00E9;l, con su clave privada, podr&#x00E1; descifrar</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_268"/>Los algoritmos de clave p&#x00FA;blica son mucho m&#x00E1;s lentos que los de clave secreta (100 a 1000 veces). Por eso se suelen usar s&#x00F3;lo para el intercambio de claves sim&#x00E9;tricas de sesi&#x00F3;n</p></list-item>
<list-item><label>&#x25FE;</label> <p>Tambi&#x00E9;n sirve para autenticar (como en OpenSSH)</p>
<p>Queremos desde una sesi&#x00F3;n en una m&#x00E1;quina local, abrir otra sesi&#x00F3;n en una m&#x00E1;quina remota sin volver a teclear contrase&#x00F1;a</p>
<list list-type="bullet">
<list-item><p>Una m&#x00E1;quina remota, no fiable, contiene clave p&#x00FA;blica</p></list-item>
<list-item><p>M&#x00E1;quina local, fiable, contiene la clave privada</p></list-item>
<list-item><p>La m&#x00E1;quina remota env&#x00ED;a un reto cifrado con la clave p&#x00FA;blica, si la m&#x00E1;quina local lo descifra, el usuario queda autenticado y puede abrir sesi&#x00F3;n en la m&#x00E1;quina remota sin teclear contrase&#x00F1;a</p></list-item>
</list></list-item>
</list>
</sec></sec>
<sec id="c16-s2">
<label><bold>16.2.</bold></label>
<title><bold>Uso de OpenSSH</bold></title>
<sec id="c16-s2-1">
<title><bold>Uso de OpenSSH</bold></title>
<preformat>
ssh usuario@maquina
</preformat>
<p>Abre una sesi&#x00F3;n remota mediante una conexi&#x00F3;n segura en la m&#x00E1;quina indicada, con el usuario indicado.</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>La primera vez que abrimos una sesi&#x00F3;n en una m&#x00E1;quina, ssh nos indica la huella digital de la m&#x00E1;quina remota
<preformat>
The authenticity of host &#x0027;gamma23 (212.128.4.133)&#x0027; can&#x0027;t be established.
RSA key fingerprint is de:fa:e1:02:dc:12:8d:ab:a8:79:8e:8f:c9:7d:99:eb.
Are you sure you want to continue connecting (yes/no)?
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Si necesitamos la certeza absoluta de que esta m&#x00E1;quina es quien dice ser, deber&#x00ED;amos comprobar esta huella digital por un medio seguro, alternativo</p></list-item>
<list-item><label>&#x25FE;</label> <p>El cliente ssh almacena las huellas digitales de las m&#x00E1;quinas en las que ha abierto sesi&#x00F3;n en el fichero <code>~/.ssh/known_hosts</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>El servidor almacena su propia huella digital en los ficheros
<preformat>
/etc/ssh/ssh_host_rsa_key
/etc/ssh/ssh_host_ed25519_key
</preformat></p>
<p>Si la huella que tiene el host en la actualidad no coincide con la huella que ten&#x00ED;a el host en la primera conexi&#x00F3;n, ssh mostrar&#x00E1; un error</p>
<p>Esto puede suceder porque
<list list-type="bullet">
<list-item><p><target target-type="page" id="pges_269"/>Alguien est&#x00E9; suplantando la identidad del host</p></list-item>
<list-item><p>El host ha sido reinstalado y el administrado no ha conservado estos ficheros</p></list-item>
</list>
<preformat>
ssh -C -X usuario@maquina
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>La opci&#x00F3;n <code>-X</code> (may&#x00FA;scula) redirige la salida del cliente X Window de la m&#x00E1;quina remota al servidor X Window de la m&#x00E1;quina local</p>
<p>Esto permite lanzar aplicaciones gr&#x00E1;ficas en la m&#x00E1;quina remota, usar&#x00E1;n la pantalla local</p>
<p>Es necesario</p>
<list list-type="bullet">
<list-item><p><code>X11Forwarding yes</code>
<preformat>
en /etc/ssh/sshd_config en la m&#x00E1;quina remota
</preformat></p></list-item>
<list-item><p>Que la m&#x00E1;quina local admita conexiones entrantes</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>La opci&#x00F3;n <code>-C</code> (may&#x00FA;scula) comprime el tr&#x00E1;fico. En conexiones r&#x00E1;pidas es conveniente omitir esta opci&#x00F3;n</p></list-item>
</list>
<p>Adem&#x00E1;s de para abrir una sesi&#x00F3;n en una m&#x00E1;quina remota, ssh permite la ejecuci&#x00F3;n de una &#x00FA;nica orden en la m&#x00E1;quina remota</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>ssh jperez@alpha ls</code></p></list-item>
</list>
<p>Ejecuta ls en la m&#x00E1;quina remota. Muestra en la m&#x00E1;quina local el stdout.</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>ssh jperez@alpha &#x0027;echo hola &#x003E; /tmp/prueba&#x0027;</code></p>
<p>Ejecuta en alpha
<preformat>
echo hola &#x003E; /tmp/prueba
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p><code>ssh jperez@alpha &#x201C;echo $HOSTNAME &#x003E; /tmp/prueba&#x201D;</code></p>
<p>Al poner comilla doble, la variable se expande en la m&#x00E1;quina local. La orden completa, redirecci&#x00F3;n incluida, se ejecuta en la m&#x00E1;quina remota</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>ssh jperez@alpha echo $HOSTNAME &#x003E; /tmp/prueba</code></p>
<p>Al no poner comilla, la variable se expande en la m&#x00E1;quina local. El resultado se redirige al fichero <code>/tmp/prueba</code> de la m&#x00E1;quina local</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>ssh jperez@alpha &#x0027;echo $HOSTNAME &#x003E; /tmp/prueba&#x0027;</code></p>
<p>Al poner comilla simple y recta, la variable se expande en la m&#x00E1;quina remota</p></list-item>
</list>
</sec>
<sec id="c16-s2-2">
<title><target target-type="page" id="pges_270"/><bold>Generaci&#x00F3;n de claves</bold></title>
<p>Para evitar teclear contrase&#x00F1;a en cada ssh, podemos autentificarnos con claves asim&#x00E9;tricas</p>
<p>Una vez configurado para ssh, tambi&#x00E9;n queda configurado para los servicios que corren sobre este (scp, sshfs)</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Se generan con <code>ssh-keygen</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>Se puede a&#x00F1;adir una <italic>pass phrase</italic>. Es una contrase&#x00F1;a adicional, tradicional. Pero no viaja por la red. Equivalente a la llave del armario de las llaves</p></list-item>
</list>
<p>Un usuario genera sus claves ejecutando en su <italic>home</italic> (de la m&#x00E1;quina local) la orden <italic>ssh-keygen</italic></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>rsa:
<preformat>
orden para generar las claves: ssh-keygen -t rsa
fichero donde quedar&#x00E1; (por omisi&#x00F3;n) la clave privada:
~/.ssh/id_rsa
fichero donde quedar&#x00E1; (por omisi&#x00F3;n) la clave p&#x00FA;blica:
~/.ssh/id_rsa.pub
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>ed25519:
<preformat>
orden generar las claves: ssh-keygen -t ed25519
fichero donde quedar&#x00E1; (por omisi&#x00F3;n) la clave privada:
~/.ssh/id_ed25519
fichero donde quedar&#x00E1; (por omisi&#x00F3;n) la clave publica:
~/.ssh/id_ed25519.pub
</preformat></p></list-item>
</list>
<p>Para poder entrar en m&#x00E1;quina remota sin emplear contrase&#x00F1;a, llevamos la clave p&#x00FA;blica a la m&#x00E1;quina remota, y la escribimos en el fichero</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>~/.ssh/authorized_keys</code> (Redhat, Debian)</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>/etc/dropbear/authorized_keys</code> (OpenWrt)</p></list-item>
</list>
<p>Este fichero (de la m&#x00E1;quina remota) en principio no existe</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>La primera vez que a&#x00F1;adamos una clave, podemos renombrar el fichero con la clave p&#x00FA;blica para que pase a llamarse authorized_keys</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_271"/>Si posteriormente a&#x00F1;adimos otras claves p&#x00FA;blicas, las pegamos inmediatamente despu&#x00E9;s de las que ya existan, usando un editor de texto o una redirecci&#x00F3;n de la shell</p></list-item>
</list>
</sec>
<sec id="c16-s2-3">
<title><bold>Permisos</bold></title>
<p>Es necesario que el directorio <code>~/.ssh</code> (local y remoto):</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Tenga el due&#x00F1;o y el grupo del usuario</p></list-item>
<list-item><label>&#x25FE;</label> <p>Tenga permisos 700</p></list-item>
<list-item><label>&#x25FE;</label> <p>Contenga todos sus ficheros con permisos 600</p></list-item>
<list-item><label>&#x25FE;</label> <p>Todos sus ficheros pertenezcan al usuario y tengan como grupo el del usuario</p></list-item>
</list>
<p>Es necesario que en mi <italic>home</italic> solo yo pueda escribir</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>En OpenWrt, tambi&#x00E9;n es necesario que <code>/etc/dropbear/authorized_keys</code> tenga permisos 600</p></list-item>
<list-item><label>&#x25FE;</label> <p>En Docker, en la m&#x00E1;quina donde corre el servidor es necesario configurar el demonio:
<preformat>
echo &#x201C;IdentityFile ~/.ssh/id_ed25519&#x201D; &#x003E;&#x003E; /etc/ssh/ssh_config
</preformat></p>
<p>O bien</p>
<p><code>echo &#x201C;IdentityFile ~/.ssh/id_rsa&#x201D; &#x003E;&#x003E; /etc/ssh/ssh_config</code></p></list-item>
</list>
</sec>
<sec id="c16-s2-4">
<title><bold>ssh-copy-id</bold></title>
<p>Se puede usar la orden <code>ssh-copy-id</code>, que se encarga de</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Copiar la clave p&#x00FA;blica a la m&#x00E1;quina remota</p></list-item>
<list-item><label>&#x25FE;</label> <p>Crear <code>authorized_keys</code> si no existe</p></list-item>
<list-item><label>&#x25FE;</label> <p>Cambiar todos los permisos</p></list-item>
</list>
<p><code>ssh-copy-id [-i [identity_file]] [user@]machine</code></p>
<p>Ejemplo:</p>
<p><code>ssh-copy-id -i id_ed25519.pub jperez@iota34</code></p>
</sec>
<sec id="c16-s2-5">
<title><target target-type="page" id="pges_272"/><bold>Ejemplo: Configuraci&#x00F3;n t&#x00ED;pica</bold></title>
<p>Una clave privada distinta para cada uno de mis ordenadores</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Soy jperez, a veces trabajo localmente en pc-casa, a veces trabajo localmente en pc-oficina</p></list-item>
<list-item><label>&#x25FE;</label> <p>Desde ambos sitios quiero entrar en pc-remoto</p></list-item>
<list-item><label>&#x25FE;</label> <p>Desde casa entro en la oficina</p></list-item>
<list-item><label>&#x25FE;</label> <p>Desde la oficina, entro en casa</p></list-item>
<list-item><label>&#x25FE;</label> <p>Creo una clave privada jperez@pc-casa</p></list-item>
<list-item><label>&#x25FE;</label> <p>Creo una clave privada jperez@pc-oficina</p></list-item>
<list-item><label>&#x25FE;</label> <p>En el <code>authorized_keys</code> de pc-remoto:</p>
<p>concateno claves p&#x00FA;blicas de jperez@pc-casa y jperez@pc-oficina</p></list-item>
<list-item><label>&#x25FE;</label> <p>En el <code>authorized_keys</code> de pc-casa</p>
<p>Escribo la clave p&#x00FA;blica de jperez@pc-oficina</p></list-item>
<list-item><label>&#x25FE;</label> <p>En el <code>authorized_keys</code> de pc-oficina</p>
<p>Escribo la clave p&#x00FA;blica de jperez@pc-casa</p></list-item>
</list>
</sec>
<sec id="c16-s2-6">
<title><bold>Ejemplo: Configuraci&#x00F3;n alternativa</bold></title>
<p>La misma clave para todos mis ordenadores</p>
<p>Aunque a las claves se les pone por omisi&#x00F3;n una etiqueta usuario@m&#x00E1;quina (que aparece como comentario al final de la clave), solo es un comentario orientativo, una misma clave privada puede usarse desde distintas m&#x00E1;quinas</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Creo una clave privada jperez, y la copio en pc-casa y en pc-oficina</p></list-item>
<list-item><label>&#x25FE;</label> <p>En el <code>authorized_keys</code> de pc-casa, de pc-oficina y de pc-remoto escribo la clave p&#x00FA;blica de jperez</p></list-item>
</list>
<p>Este enfoque es menos flexible y menos seguro</p>
<p>Es posible usar varias claves privadas (cada una en su fichero), basta indicar a ssh cu&#x00E1;l (o cu&#x00E1;les) debe emplear<target target-type="page" id="pges_273"/></p>
<preformat>
ssh jperez@alpha
# intenta autenticarse con ~/.ssh/id_rsa o ~/.ssh/id_ed25519 # (clave por omisi&#x00F3;n)
ssh -i ~/.ssh/id_alumno alumno@pc01 #
# lo intenta con id_alumno y con la clave por omisi&#x00F3;n
ssh -i ~/.ssh/id_alumno -i ~/.ssh/id_profe alumno@pc01
# lo intenta con id_alumno, con id_profe y con la clave por omisi&#x00F3;n
</preformat>
<p>scp tambi&#x00E9;n admite la opci&#x00F3;n -i</p>
<preformat>
scp -i ~/.ssh/id_alumno alumno@pc01:/tmp/test .
</preformat>
<p>(sshfs no admite la opci&#x00F3;n -i)</p>
</sec>
<sec id="c16-s2-7">
<title><bold>ssh-agent</bold></title>
<p>La manera habitual de autentificarse es mediante el demonio <italic>ssh-agent</italic></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><italic>ssh-agent</italic> contestar&#x00E1; por nosotros, gestionando retos y repuestas cifrados</p></list-item>
<list-item><label>&#x25FE;</label> <p><italic>ssh-agent</italic> tiene que ser el padre de nuestra shell, o nuestra sesi&#x00F3;n x</p>
<list list-type="bullet">
<list-item><p>Las distribuciones Linux con X Window suelen tenerlo instalado</p>
<p>Si no est&#x00E1; funcionando (como en ubuntu server)</p>
<p><code>exec ssh-agent /bin/bash</code></p></list-item>
<list-item><p>Esto hace que nuestra shell actual sea reemplazada por <italic>ssh-agent</italic>, quien a su vez crear&#x00E1; una shell hija suya</p></list-item></list>
</list-item>
<list-item><label>&#x25FE;</label> <p><code>ssh-add</code> a&#x00F1;ade una identidad a ssh-agent</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>ssh-add -l</code> indica las identidades manejadas por el ssh-agent</p></list-item>
</list>
<p>En ocasiones, por ejemplo si no empleamos <italic>pass phrase</italic>, el <italic>ssh-agent</italic> no es necesario</p>
</sec>
<sec id="c16-s2-8">
<title><bold>Depuraci&#x00F3;n</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>En el cliente:
<preformat>
ssh -v o ssh -vv o -vvv
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_274"/>En el servidor:
<preformat>
/var/log/auth.log
</preformat></p></list-item>
</list>
<p>Los errores m&#x00E1;s frecuentes suelen ser ficheros de configuraci&#x00F3;n con nombre incorrecto o permisos incorrectos</p>
</sec>
</sec>
<sec id="c16-s3">
<label><bold>16.3.</bold></label>
<title><bold>configuraci&#x00F3;n de ssh</bold></title>
<sec id="c16-s3-1">
<title><bold>Configuraci&#x00F3;n de ssh en el cliente</bold></title>
<p>El usuario puede configurar el comportamiento de su cliente ssh en el fichero <code>~/.ssh/config</code></p>
<p>Algunas de las opciones m&#x00E1;s interesante son</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Especificar cu&#x00E1;l ser&#x00E1; el usuario por omisi&#x00F3;n para una maquina en particular, sin necesidad de especificarlo en cada ocasi&#x00F3;n</p></list-item>
<list-item><label>&#x25FE;</label> <p>Indicar el nombre de una m&#x00E1;quina sin usar ni la direcci&#x00F3;n IP, ni el nombre completo (FQDN) ni modificar el fichero <code>/etc/hosts</code> (Que exije privilegios de root)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Enviar peri&#x00F3;dicamente un mensaje al servidor para que mantenga abierta la conexi&#x00F3;n</p></list-item>
</list>
<p>Fichero <code>~/.ssh/config</code> de ejemplo</p>
<preformat>
host alpha user juanperez
hostname 192.168.19.27
# Permite hacer directamente &#x0027;ssh alpha&#x0027;, sin indicar mi usuario # (Si el usuario coincide en m&#x00E1;quina local y remota, tampoco hace # falta especificarlo)
host alpha user juanperez
hostname <ext-link ext-link-type="uri" xlink:href="http://alpha.midominio.com">alpha.midominio.com</ext-link>
# Similar al caso anterior, pero con FQDN
host *
&#x00A0;&#x00A0;&#x00A0;ServerAliveInterval 60
&#x00A0;&#x00A0;&#x00A0;# Mantiene la conexi&#x00F3;n abierta
</preformat>
<p>Mas informaci&#x00F3;n en</p>
<p><code>man 5 ssh_config</code></p>
</sec>
<sec id="c16-s3-2">
<title><target target-type="page" id="pges_275"/><bold>Configuraci&#x00F3;n adicional</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>/etc/ssh/ssh_config</code></p></list-item>
<list-item><label>&#x25FE;</label> <p><code>/etc/ssh/sshd_config</code></p></list-item>
</list>
</sec></sec>
<sec id="c16-s4">
<label><bold>16.4.</bold></label>
<title><bold>sshfs</bold></title>
<sec id="c16-s4-1">
<title><bold>sshfs</bold></title>
<p>Supongamos que, usando la red, quiero trabajar con unos datos que est&#x00E1;n en una m&#x00E1;quina remota, su sitio no es la m&#x00E1;quina en la que yo estoy sentado. Tal vez porque uso un ordenador m&#x00F3;vil, pero los datos no son m&#x00F3;viles</p>
<p>Disponemos de much&#x00ED;simas soluciones, cada una con sus ventajas e inconvenientes</p>
<p>Podemos trabajar:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Por ssh</p>
<p>Pero estamos limitados a la shell. No podemos usar ninguna aplicaci&#x00F3;n gr&#x00E1;fica, resulta muy limitado en Windows, ...</p></list-item>
<list-item><label>&#x25FE;</label> <p>Con una sesi&#x00F3;n gr&#x00E1;fica remota: vnc, escritorio remoto de Windows, X window en remoto, etc</p>
<p>Pero necesitamos una conexi&#x00F3;n relativamente buena y cargamos mucho la m&#x00E1;quina remota, toda la aplicaci&#x00F3;n est&#x00E1; en la m&#x00E1;quina remota</p></list-item>
<list-item><label>&#x25FE;</label> <p>Podemos sincronizar al estilo Dropbox.</p>
<p>Pero necesitamos una cuenta, vinculada a 1 persona, con limitaciones de tama&#x00F1;o, dependencia del proveedor, etc Adem&#x00E1;s se pueden provocar discrepancias, y los ficheros solo se guardan en la m&#x00E1;quina remota cuando abro una sesi&#x00F3;n en la m&#x00E1;quina remota y sincronizo</p></list-item>
<list-item><label>&#x25FE;</label> <p>Podemos montar un sistema de ficheros por NFS (como en nuestros laboratorios Linux), por SAMBA o similar</p>
<p>Pero hace falta mucha administraci&#x00F3;n en la m&#x00E1;quina remota, y normalmente, por motivos de seguridad, el cliente solo podr&#x00E1; estar en sitios muy concretos</p></list-item>
<list-item><label>&#x25FE;</label> <p>Podemos usar una VPN, cuya administraci&#x00F3;n no es trivial</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_276"/>Podemos usar un directorio compartido de VirtualBox, pero solo en el caso (muy) particular de un <italic>guest</italic> VirtualBox y un <italic>host</italic> que lo soporte</p></list-item>
<list-item><label>&#x25FE;</label> <p>Otra de las alternativas es sshfs</p></list-item>
</list>
<p>sshfs: Secure SHell FileSystem</p>
<p>Sistema de ficheros de red basado en FUSE (<italic>Filesystem in userspace</italic>) Permite usar un sistema de fichero remoto como si fuera local</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Hay disponibles implementaciones libres y gratuitas, para Linux, macOS y Windows (SSFS-Win)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Menos eficiente pero m&#x00E1;s seguro que NFS</p></list-item>
<list-item><label>&#x25FE;</label> <p>No hace falta ninguna administraci&#x00F3;n en la m&#x00E1;quina remota (servidor) , basta con que tengamos una cuenta de ssh ordinaria</p></list-item>
<list-item><label>&#x25FE;</label> <p>En el cliente basta instalar el paquete sshfs y ejecutar una &#x00FA;nica orden</p></list-item>
</list>
</sec>
<sec id="c16-s4-2">
<title><bold>Inconvenientes de sshfs</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Dependemos continuamente de la red (con sistemas tipo Dropbox solo hace falta red cuando sincronizamos)</p></list-item>
<list-item><label>&#x25FE;</label> <p>En el ordenador local necesitamos todas las aplicaciones (mientras que con sistemas tipo vnc, el cliente puede ser mucho m&#x00E1;s ligero)</p></list-item>
<list-item><label>&#x25FE;</label> <p>M&#x00E1;s pesado y menos eficiente que NFS o samba</p></list-item>
</list>
</sec>
<sec id="c16-s4-3">
<title><bold>Punto de montaje</bold></title>
<p>En Unix/Linux, el punto de montaje es un directorio del sistema de ficheros <italic>normal</italic>, local, donde queremos que sea visible un nuevo sistema de ficheros</p>
<p>En el caso de sshfs, el nuevo sistema de ficheros ser&#x00E1; el de la m&#x00E1;quina remota, a la que accedemos por ssh</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>El punto de montaje es un directorio ordinario, que tiene que existir antes de montar el sistema remoto</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_277"/>Suele estar vac&#x00ED;o, pero puede contener ficheros</p>
<p>En el caso de sshfs, si no est&#x00E1; vac&#x00ED;o hay que a&#x00F1;adir la opci&#x00F3;n -o nonempty</p>
<list list-type="bullet">
<list-item><p>Al montar un sistema de ficheros en un punto de montaje, el contenido del punto de montaje queda inaccesible</p></list-item>
<list-item><p>Al desmontar, el contenido vuelve a ser visible</p></list-item>
</list></list-item>
</list>
</sec>
<sec id="c16-s4-4">
<title><bold>Montar un directorio con sshfs</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Montar el <italic>home</italic> remoto:
<preformat>
sshfs usuario@maquina: /punto/de/montaje
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Montar un directorio remoto cualquiera
<preformat>
sshfs usuario@maquina:/un/directorio /punto/de/montaje
</preformat></p>
<p>(Siempre path absoluto, no soporta ~)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Desmontar:
<preformat>
fusermount -u /punto/de/montaje
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Para poder usar sshfs (en el cliente) sin tener privilegios de root, es necesario activar la opci&#x00F3;n <code>user_allow_other</code> en el fichero <code>/etc/fuse.conf</code> (y reiniciar el demonio o la m&#x00E1;quina)</p></list-item>
<list-item><label>&#x25FE;</label> <p>En conexiones lentas puede ser conveniente a&#x00F1;adir la opci&#x00F3;n -C para que comprima el tr&#x00E1;fico
<preformat>
sshfs -C usuario@maquina:/path /punto/de/montaje
</preformat></p></list-item>
</list>
</sec></sec>
<sec id="c16-s5">
<label><bold>16.5.</bold></label>
<title><bold>T&#x00FA;neles con SSH</bold></title>
<sec id="c16-s5-1">
<title><bold>T&#x00FA;neles con SSH</bold></title>
<p>SSH permite hacer t&#x00FA;neles, aka <italic>port forwarding</italic></p>
<p>Concepto similar al de VPN, pero no es una verdadera VPN</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Se redirige un &#x00FA;nico puerto</p></list-item>
<list-item><label>&#x25FE;</label> <p>Solamente TCP, no UDP</p></list-item>
</list>
<p><target target-type="page" id="pges_278"/>A trav&#x00E9;s de un t&#x00FA;nel, las conexiones a cierto puerto TCP de una m&#x00E1;quina se redirigen a otro puerto TCP en otra m&#x00E1;quina</p>
<p>Dos tipos:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>T&#x00FA;nel local, aka t&#x00FA;nel (<italic>a secas</italic>). (<italic>local tunnel, tunnel</italic>)</p></list-item>
<list-item><label>&#x25FE;</label> <p>T&#x00FA;nel remoto, aka t&#x00FA;nel inverso. (<italic>remote tunnel, reverse tunel</italic>)</p></list-item>
</list>
</sec>
<sec id="c16-s5-s1">
<label><bold>16.5.1.</bold></label>
<title><bold>T&#x00FA;nel local</bold></title>
<sec id="c16-s5-s1-1">
<title><bold>T&#x00FA;nel local</bold></title>
<fig><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-29.jpg"/></fig>
<p>Escenario t&#x00ED;pico donde usamos un servicio sobre un canal no seguro</p>
<fig><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-30.jpg"/></fig>
<p><target target-type="page" id="pges_279"/>Si tenemos cuenta en una m&#x00E1;quina acccesible mediante ssh, podemos usarla como proxy</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Establecemos un t&#x00FA;nel ssh desde la m&#x00E1;quina local al proxy</p></list-item>
</list>
<p>Ventajas del t&#x00FA;nel local</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Permite asegurar el primer tramo, que suele ser el m&#x00E1;s peligroso</p></list-item>
<list-item><label>&#x25FE;</label> <p>Para el servidor, las peticiones vienen desde el proxy, no desde la m&#x00E1;quina local. Esto es de utilidad</p>
<list list-type="bullet">
<list-item><p>Si se trata de un servicio vinculado a la IP del cliente,</p></list-item>
<list-item><p>Para evitar cortafuegos, censura, etc</p></list-item>
<list-item><p>Burlar restricciones en la red del cliente</p></list-item>
</list>
<p>El tr&#x00E1;fico viaja cifrado en la red de la m&#x00E1;quina local, el administrador de esa red pierde todo control sobre &#x00E9;l</p>
<list list-type="bullet">
<list-item><p>El administrador de la red local puede solucionar este problema prohibiendo todo tr&#x00E1;fico cifrado, y con ello los t&#x00FA;neles</p></list-item>
</list></list-item>
</list>
<p>Inconvenientes</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Encaminamiento en tri&#x00E1;ngulo</p></list-item>
<list-item><label>&#x25FE;</label> <p>El proxy es un cuello de botella</p></list-item>
</list>
<fig><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-31.jpg"/></fig>
<p><target target-type="page" id="pges_280"/>En caso de que tengamos una cuenta en el servidor, accesible mediante ssh, podemos asegurar todo el trayecto</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Establecimiento del t&#x00FA;nel. En <code>maquina_local</code> ejecutamos
<preformat>
ssh -L puerto_local:maquina_destino:puerto_remoto usuario@proxy
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Uso del t&#x00FA;nel:</p>
<p>Indicamos al cliente que se conecte a <code>puerto_local</code> en <code>maquina_local</code></p>
<p>(El servicio estar&#x00E1; realmente en el <code>puerto_destino</code> de <code>maquina_remota</code>)</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>ssh -L</code></p>
<p>adem&#x00E1;s de redirigir los puertos, abre una sesi&#x00F3;n de shell ordinaria en el proxy</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>ssh -fNL</code></p>
<p>Hace que el t&#x00FA;nel se lance en segundo plano (tras preguntar contrase&#x00F1;as), pero no abre una sesi&#x00F3;n de shell en el proxy</p>
<list list-type="bullet">
<list-item><p>Esto puede ser conveniente para el usuario experimentado, as&#x00ED; no ocupa el terminal</p></list-item>
<list-item><p>Pero despista al usuario principiante, pues no resulta tan claro si el desv&#x00ED;o de puerto est&#x00E1; activo o no</p></list-item>
</list></list-item>
</list>
<p>Ejemplo:</p>
<p>Acceder al servidor web en <code>bilo.gsyc.es</code>, usando <code>epsilon01</code> como proxy</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Establecemos el t&#x00FA;nel en la m&#x00E1;quina local:
<preformat>
ssh -L 8080:bilo.gsyc.es:80 <ext-link ext-link-type="uri" xlink:href="mailto:milogin@epsilon01.aulas.gsyc.es">milogin@epsilon01.aulas.gsyc.es</ext-link>
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Usamos el t&#x00FA;nel:</p>
<p>En la m&#x00E1;quina local, introducimos en el navegador web la url</p>
<p><code><ext-link ext-link-type="uri" xlink:href="http://localhost:8080">http://localhost:8080</ext-link></code></p>
<p>El cliente cree conectarse a su m&#x00E1;quina local, de hecho eso hace. Pero el t&#x00FA;nel redirige ese tr&#x00E1;fico al proxy, y del proxy al destino</p></list-item>
</list>
</sec>
<sec id="c16-s5-s1-2">
<title><bold>Proxy SOCKS</bold></title>
<p>Un t&#x00FA;nel local ordinario no sirve para navegar normalmente por el web</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>La t&#x00E9;cnica anterior nos permite usar un t&#x00FA;nel ssh para acceder a 1 servidor web</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_281"/>Pero en cuanto hagamos clic sobre un enlace fuera de la m&#x00E1;quina remota, dejamos de usar el proxy</p></list-item>
<list-item><label>&#x25FE;</label> <p>Para una sesi&#x00F3;n de navegaci&#x00F3;n ordinaria tendr&#x00ED;amos que abrir 10, 15, 20 t&#x00FA;neles...</p></list-item>
</list>
<p>Pero openssh puede hacer <italic>port forwarding</italic> din&#x00E1;mico, como servidor del protocolo SOCKS</p>
</sec>
<sec id="c16-s5-s1-3">
<title><bold>Configuraci&#x00F3;n de proxy SOCKS</bold></title>
<list list-type="order">
<list-item><p>El usuario establece un t&#x00FA;nel desde la m&#x00E1;quina local hasta el proxy, a&#x00F1;adiendo la opci&#x00F3;n -D e indicando un puerto local, con lo que se instala en la m&#x00E1;quina local un servidor SOCKS</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>ssh -D puerto_local usuario@proxy</p></list-item>
</list>
<p>El puerto est&#x00E1;ndar es el 1080, puede usarse cualquier otro</p></list-item>
<list-item><p>El usuario indica a su navegador web que todas las peticiones debe hacerlas al servidor SOCKS que est&#x00E1; en <code>maquina_local:puerto_local</code></p></list-item>
<list-item><p>El servidor de la m&#x00E1;quina local pedir&#x00E1; al proxy que haga las peticiones, y este se las har&#x00E1; al servidor web</p></list-item>
<list-item><p>El servidor web responder&#x00E1; al proxy, que reenv&#x00ED;a la respuesta al servidor SOCKS, de donde la lee el navegador web</p></list-item>
</list>
<p>Error frecuente: el usuario indica a su navegador que el servidor SOCKS est&#x00E1; en el proxy. Esto es incorrecto. El servidor SOCKS est&#x00E1; en la m&#x00E1;quina local</p>
<p>La configuraci&#x00F3;n del navegador es, obviamente, dependiente del navegador. Cualquier navegador con un m&#x00ED;nimo de calidad permitir&#x00E1; hacerlo</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Firefox:
<preformat>
editar | preferencias | general | proxy de red |
</preformat></p>
<p><code>configuraci&#x00F3;n manual del proxy | host SOCKS</code></p>
</list-item>
<list-item><label>&#x25FE;</label> <p>Google Chrome</p>
<p>Es necesario lanzarlo desde la shell con el par&#x00E1;metro adecuado</p>
<list list-type="bullet">
<list-item><p>Linux
<preformat>
google-chrome --proxy-server=&#x0022;socks://localhost:1080&#x0022;
</preformat></p></list-item>
<list-item><p><target target-type="page" id="pges_282"/>macOS
<preformat>
open -a Google\ Chrome \
--args --proxy-server=&#x0022;socks://localhost:1080&#x0022;
</preformat></p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Las conexiones ssh pueden caerse.</p>
<p>En vez de <code>ssh</code>, podemos emplear <code>autossh</code>, que monitoriza la conexi&#x00F3;n y la reinicia si se cae</p>
<p>Esto es &#x00FA;til combinado con el acceso autom&#x00E1;tico sin contrase&#x00F1;a</p></list-item>
<list-item><label>&#x25FE;</label> <p>La aplicaci&#x00F3;n <code>tsocks</code> permite usar un proxy SOCKS de forma transparente a las aplicaciones (aplicaciones no preparadas para usar SOCKS)</p></list-item>
</list>
</sec></sec>
<sec id="c16-s5-s2">
<label><bold>16.5.2.</bold></label>
<title><bold>T&#x00FA;nel remoto</bold></title>
<sec id="c16-s5-s2-1">
<title><bold>T&#x00FA;nel remoto</bold></title>
<fig><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-32.jpg"/></fig>
<p>Con un t&#x00FA;nel remoto, aka t&#x00FA;nel inverso, podemos traer a la m&#x00E1;quina local las conexiones a cierto puerto del proxy</p>
<p>Esto puede tener al menos tres utilidades</p>
<list list-type="order">
<list-item><p>Proteger el servidor</p></list-item>
<list-item><p>Distribuir servicios</p></list-item>
<list-item><p>Acceder a servidor tras NAT, sin configurar el NAT</p></list-item>
</list>
</sec>
<sec id="c16-s5-s2-2">
<title><bold>Utilidad 1: Proteger el servidor</bold></title>
<p>Un t&#x00FA;nel inverso puede servir para proteger un servidor</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>El proxy es necesariamente una m&#x00E1;quina expuesta, puede necesitar gran visibilidad y muchos servicios (por ejemplo un servidor web)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Pero el resto de los servicios, los colocamos en el servidor, en una m&#x00E1;quina distinta, debidamente aislada</p></list-item>
</list>
<p><target target-type="page" id="pges_283"/>De esta forma, si un atacante comprometiera el proxy</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Tendr&#x00ED;amos un problema, podr&#x00ED;a hacer p.e. un <italic>website defacement</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>Pero el problema estar&#x00ED;a contenido, no tendr&#x00ED;a acceso al resto de servicios, p.e. la base de datos</p></list-item>
</list>
<p>Naturalmente, cabe la posibilidad de que el atacante rompa la seguridad del t&#x00FA;nel y acceda al servidor, pero esto a&#x00F1;ade una barrera adicional, relativamente robusta</p>
</sec>
<sec id="c16-s5-s2-3">
<title><bold>Utilidad 2: Distribuir servicios</bold></title>
<p>El proxy es una m&#x00E1;quina distinta al servidor</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Puede ser &#x00FA;til para equilibrar la carga</p></list-item>
<list-item><label>&#x25FE;</label> <p>Puede ser &#x00FA;til si queremos combinar distinto software o distintos sistemas operativos</p></list-item>
</list>
</sec>
<sec id="c16-s5-s2-4">
<title><bold>Utilidad 3:Acceder a servidor tras NAT, sin configurar el NAT</bold></title>
<p>Tenemos un servidor tras un NAT</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>La t&#x00E9;cnica habitual para permitir conexiones entrantes a este servidor es hacer <italic>port forwarding</italic> aka <italic>abrir puertos</italic> en el router que hace NAT</p></list-item>
<list-item><label>&#x25FE;</label> <p>Pero en vez esto, podemos conseguir un resultado similar, sin necesidad de modificar la configuraci&#x00F3;n del router NAT</p></list-item>
</list>
<p>Usar un t&#x00FA;nel ssh inverso (en vez del <italic>port forwarding</italic> tradicional), puede ser de utilidad:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Si es un NAT que nosotros no podemos administrar (somos usuarios ordinarios, sin privilegios de administrador)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Si <italic>abrir los puertos</italic> del NAT es inc&#x00F3;modo</p>
<list list-type="bullet">
<list-item><p>Hay un NAT tras otro NAT, tendr&#x00ED;a que configurar ambos</p></list-item>
<list-item><p>El NAT solo se puede configurar via web, t&#x00ED;picamente a mano, resulta complicado automatizarlo</p>
<p><target target-type="page" id="pges_284"/>(Mientras que el t&#x00FA;nel inverso se prepara con 1 mandato desde la shell, f&#x00E1;cil de incluir en un script, cron, etc)</p></list-item>
<list-item><p>Es necesario detener y reiniciar alg&#x00FA;n demonio (como el NAT de VirtualBox)</p></list-item>
</list></list-item>
</list>
<p>Inconvenientes de esta t&#x00E9;cnica:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Dependemos de la existencia y disponibilidad del proxy</p></list-item>
<list-item><label>&#x25FE;</label> <p>El proxy necesita una IP p&#x00FA;blica</p>
<list list-type="bullet">
<list-item><p>O bien el proxy est&#x00E1; tras un NAT que s&#x00ED; podemos administrar. Pondr&#x00ED;amos como direcci&#x00F3;n del proxy la del router que hace NAT, y redigir&#x00ED;amos a su vez esa conexi&#x00F3;n a una m&#x00E1;quina de la red privada</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Solo es aplicable a TCP, no a UDP</p></list-item>
<list-item><label>&#x25FE;</label> <p>Estamos cifrando todo el tr&#x00E1;fico (puede que no sea necesario)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Establecimiento del t&#x00FA;nel. En <code>maquina_local</code> ejecutamos
<preformat>
ssh -R puerto_proxy:localhost:puerto_local usuario@proxy
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Uso del t&#x00FA;nel:</p>
<p>Indicamos al cliente que se conecte a <code>puerto_proxy en proxy</code></p>
<p>El cliente cree conectarse al proxy, de hecho lo est&#x00E1; haciendo. Pero el t&#x00FA;nel redirige el tr&#x00E1;fico al <code>puerto_local</code> de <code>localhost</code>, que es donde est&#x00E1; el servicio</p></list-item>
<list-item><label>&#x25FE;</label> <p>Como en el t&#x00FA;nel local, las opciones <code>-f</code> y <code>-N</code> son aplicables, con el mismo significado</p></list-item>
</list>
<p>Ejemplo: Tengo el servicio de escritorio remoto de <code>pilder01</code> en el puerto 5900 de la direcci&#x00F3;n privada 192.168.1.8</p>
<p>Quiero usar como proxy la m&#x00E1;quina <code>miproxy.gsyc.es</code>, puerto 15900</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>En pilder01 ejecuto:
<preformat>
ssh -R 15900:localhost:5900 <ext-link ext-link-type="uri" xlink:href="mailto:milogin@miproxy.gsyc.es">milogin@miproxy.gsyc.es</ext-link>
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Para acceder a este servicio, el cliente ejecuta
<preformat>
vinagre miproxy.gsyc.es:15900
</preformat></p>
<p>(el servicio est&#x00E1; realmente en el puerto 5900 de pilder01)</p></list-item>
</list>
<p><target target-type="page" id="pges_285"/>Para que el cliente pueda estar en una m&#x00E1;quina distinta a la m&#x00E1;quina proxy, es necesario:</p>
<list list-type="order">
<list-item><p>En el proxy, en en el fichero
<preformat>
/etc/ssh/sshd_config
</preformat></p>
<p>a&#x00F1;adir la entrada</p>
<p><code>GatewayPorts yes</code></p></list-item>
<list-item><p><code>sudo /etc/init.d/ssh restart</code></p>
<p>Por omisi&#x00F3;n, esta opci&#x00F3;n no est&#x00E1; activada (y solo <code>root</code> puede activarla) Por tanto:</p></list-item></list>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Si tenemos una cuenta <code>ssh</code> ordinaria en el proxy, podemos hacer el t&#x00FA;nel inverso, pero el cliente deber&#x00E1; estar en el mismo proxy</p>
<list list-type="bullet">
<list-item><p>Adem&#x00E1;s, el cliente deber&#x00E1; usar el nombre <code>localhost</code> y la direcci&#x00F3;n IP 127.0.0.1</p></list-item>
<list-item><p>En el ejemplo anterior, el cliente solo podr&#x00ED;a estar en <code>proxy.gsyc.es</code> y deber&#x00ED;a ejecutar
<preformat>
vinagre localhost:15900
</preformat></p>
<p>o bien
<preformat>
vinagre 127.0.0.1:15900
</preformat></p>
<p>Pero no podr&#x00ED;a usar el nombre proxy.gsyc.es</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Si tenemos privilegios de adminitrador en el proxy y a&#x00F1;adimos a <code>sshd_config</code> la opci&#x00F3;n <code>GatewayPorts yes</code>, el cliente podr&#x00E1; estar en cualquier lugar</p>
<p>Otro ejemplo de t&#x00FA;nel inverso</p></list-item>
</list>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>mortuno@gsyc:~$ ssh -R 8080:localhost:80 <ext-link ext-link-type="uri" xlink:href="mailto:mortuno@miproxy.gsyc.es">mortuno@miproxy.gsyc.es</ext-link></code></p></list-item>
<list-item><label>&#x25FE;</label> <p>El cliente accede a
<preformat>
<ext-link ext-link-type="uri" xlink:href="http://myproxy.gsyc.es:8080">http://myproxy.gsyc.es:8080</ext-link>
</preformat></p>
<p>donde ver&#x00E1;</p>
<p><ext-link ext-link-type="uri" xlink:href="http://gsyc.es">http://gsyc.es</ext-link></p></list-item>
</list>
</sec>
</sec>
<sec id="c16-s5-s2-5">
<title><target target-type="page" id="pges_286"/><bold>Resumen</bold></title>
<p>Recapitulando, resumimos as&#x00ED; el uso de los t&#x00FA;neles directo e inverso</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>En todos los casos:</p>
<p>El ssh se hace desde la m&#x00E1;quina local hasta el proxy (no hasta la m&#x00E1;quina remota)</p></list-item>
<list-item><label>&#x25FE;</label> <p>T&#x00FA;nel directo:</p></list-item>
</list>
<p>ssh -L puerto_local:maquina_destino:puerto_remoto usuario@proxy</p>
<list list-type="bullet">
<list-item><p>El servicio est&#x00E1; en maquina_destino:puerto_remoto</p>
<p>El cliente est&#x00E1; en la m&#x00E1;quina local, se conecta al puerto local</p></list-item>
<list-item><label>&#x25FE;</label> <p>T&#x00FA;nel inverso:
<preformat>
ssh -R puerto_proxy:localhost:puerto_local usuario@proxy
</preformat></p>
<list list-type="bullet">
<list-item><p>El servicio est&#x00E1; en <code>m&#x00E1;quina local:puerto_local</code></p></list-item>
<list-item><p>El cliente est&#x00E1; en una m&#x00E1;quina remota cualquiera, desconocida. Se conecta a <code>proxy:puerto_proxy</code></p></list-item>
</list></list-item></list>
</sec>
<sec id="c16-s5-s2-6">
<title><bold>scp</bold></title>
<p>scp va sobre ssh, por tanto</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Usa el mismo servidor (sshd), escuchando en el mismo puerto (22)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Si preparamos un t&#x00FA;nel para entrar en el servidor ssh de una m&#x00E1;quina, tambi&#x00E9;n podemos hacer scp a esa m&#x00E1;quina</p></list-item>
<list-item><label>&#x25FE;</label> <p>La &#x00FA;nica diferencia es que, en el cliente, para indicar el puerto</p>
<list list-type="bullet">
<list-item><p>ssh usa <code>-p</code> (min&#x00FA;scula)</p></list-item>
<list-item><p>scp usa <code>-P</code> (may&#x00FA;scula)</p></list-item>
</list></list-item>
</list>
<p><target target-type="page" id="pges_287"/>Ejemplo</p>
<p>Tenemos la m&#x00E1;quina virtual <code>pc01</code>, en el <italic>host</italic> <code>zeta01</code>, conectada a la red a trav&#x00E9;s de NAT</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Establecemos el t&#x00FA;nel (en este caso, remoto)
<preformat>
user@pc01:~$ ssh -R 2222:localhost:22 milogin@zeta01
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Desde el <italic>host</italic>, accedemos al servidor de ssh en <code>pc01</code>
<preformat>
milogin@zeta01:~$ ssh -p 2222 user@localhost # p min&#x00FA;scula
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Desde el <italic>host</italic>, copiamos el fichero <code>holamundo.txt</code> al directorio <code>/tmp1</code> de <code>pc01</code>
<preformat>
milogin@zeta01:~$ scp -P 2222 holamundo.txt user@localhost:/tmp # P may&#x00FA;scula
</preformat></p></list-item>
</list>
</sec>
<sec id="c16-s5-s2-7">
<title><bold>autossh</bold></title>
<p>Una conexi&#x00F3;n ssh en desuso se cortar&#x00E1; autom&#x00E1;ticamente. Podemos evitarlo en el lado del cliente a&#x00F1;adiendo en <code>.ssh/config</code></p>
<preformat>
host *
ServerAliveInterval 60
</preformat>
<p>O mejor a&#x00FA;n, usando autossh</p>
<preformat>
apt update; apt upgrade; apt install -y autossh
</preformat>
<p>No lanzaremos ssh directamente desde el terminal, sino desde autossh. T&#x00ED;picamente con opciones como</p>
<preformat>
-M 0 -o &#x0022;ServerAliveInterval 30&#x0022; -o &#x0022;ServerAliveCountMax 3&#x0022;
</preformat>
<p>Esto hace que cada 30 segundos se compruebe la conexi&#x00F3;n. Y se relance ssh si falla 3 veces seguidas.</p>
<preformat>
#!/bin/bash
PUERTO_PROXY=9999
USUARIO=jperez
PROXY=miproxy
PUERTO_LOCAL=22
autossh -M 0 -o &#x201C;ServerAliveInterval 30&#x201D; -o &#x201C;ServerAliveCountMax 3&#x201D; \
-R ${PUERTO_PROXY}:localhost:${PUERTO_LOCAL} ${USUARIO}@${PROXY}
</preformat>
<p>M&#x00E1;s informaci&#x00F3;n en este enlace:</p>
<p><ext-link ext-link-type="uri" xlink:href="https://www.everythingcli.org/ssh-tunnelling-for-fun-and-profit-autossh">ssh tunnelling for fun and profit autossh</ext-link><target target-type="page" id="pges_288"/></p>
</sec></sec>
</body>
</book-part>
<book-part id="c17" book-part-type="chapter">
<book-part-meta>
<book-part-id book-part-id-type="publisher-id">URJC</book-part-id>
<title-group>
<label>17.</label>
<title><target target-type="page" id="pges_289"/>CONTROL DE INTEGRIDAD</title>
</title-group>
</book-part-meta>
<body>
<sec id="c17-s1">
<label><bold>17.1.</bold></label>
<title><bold>Funci&#x00F3;n hash</bold></title>
<sec id="c17-s1-1">
<title><bold>Funci&#x00F3;n hash</bold></title>
<p>T&#x00E9;cnica b&#x00E1;sica para garantizar integridad de un mensaje</p>
<preformat>
hash:
a mess, jumble, or muddled.
to chop into small pieces; make into hash; mince.
to muddle or mess up.
</preformat>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Funci&#x00F3;n que, a partir de un bloque arbitrario de datos (<italic>message</italic>), genera de forma determinista un <italic>valor hash</italic>, aka <italic>message digest</italic>, aka <italic>digest</italic>.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Este valor hash identifica de forma pr&#x00E1;cticamente un&#x00ED;voca al mensaje, de forma que un cambio en el mensaje, aunque sea peque&#x00F1;o, provoque un cambio en el valor hash</p></list-item>
<list-item><label>&#x25FE;</label> <p>Es posible que dos mensajes distintos generen el mismo valor hash, aunque muy dif&#x00ED;cil</p></list-item>
</list>
<p>Funci&#x00F3;n hash ideal:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>F&#x00E1;cil de generar</p></list-item>
<list-item><label>&#x25FE;</label> <p>Muy dif&#x00ED;cil generar el mensaje a partir del hash</p></list-item>
<list-item><label>&#x25FE;</label> <p>Muy dif&#x00ED;cil modificar el mensaje manteniendo el hash</p></list-item>
<list-item><label>&#x25FE;</label> <p>Muy dif&#x00ED;cil encontrar dos mensajes con el mismo hash</p></list-item>
</list>
<p>Ejemplos de funciones hash: MD2, MD4, MD5, SHA-1, SHA-2</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>En el a&#x00F1;o 1996 aparecieron vulnerabilidades en MD5. En 2005, en SHA- 1</p></list-item>
<list-item><label>&#x25FE;</label> <p>SHA-2 se considera seguro actualmente</p></list-item>
<list-item><label>&#x25FE;</label> <p>Se espera que SHA-3 sea definido a finales de 2012<target target-type="page" id="pges_290"/></p></list-item>
</list>
<preformat>
koji@mazinger:~$ md5sum ubuntu-11.10.iso
c396dd0f97bd122691bdb92d7e68fde5 ubuntu-11.10.iso
koji@mazinger:~$ sha1sum ubuntu-11.10.iso
8492d3daf0c89907c4301cb2c72094fe59037c76 ubuntu-11.10.iso
koji@mazinger:~$ sha224sum ubuntu-11.10.iso
b070d093af7e933cedf6c21cf5a5b71ff7eb29946b9fc2a21d609ca0 ubuntu-11.10.iso
</preformat>
</sec>
</sec>
<sec id="c17-s2">
<label><bold>17.2.</bold></label>
<title><bold>cmp</bold></title>
<sec id="c17-s2-1">
<title><bold>cmp</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Con el <italic>hash</italic> podemos saber si un fichero ha cambiado. Pero no qu&#x00E9; ha cambiado</p></list-item>
<list-item><label>&#x25FE;</label> <p>La orden <italic>cmp</italic> indica el primer byte que ha cambiado en dos ficheros
<preformat>
koji@mazinger:~$ cmp a.pdf b.pdf
a.pdf b.pdf son distintos: byte 63401, l&#x00ED;nea 716
</preformat></p></list-item>
</list>
</sec></sec>
<sec id="c17-s3">
<label><bold>17.3.</bold></label>
<title><bold>diff</bold></title>
<sec id="c17-s3-1">
<title><bold>diff</bold></title>
<p>En caso de que los ficheros est&#x00E9;n en formato de texto plano, <italic>diff</italic> indica cu&#x00E1;les son los cambios, l&#x00ED;nea a l&#x00ED;nea</p>
<p>Supongamos los ficheros <code>a.txt</code> y <code>b.txt</code></p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>a.txt:</code></p></td>
<td valign="top" align="left"><p><code>b:txt</code></p></td>
</tr>
<tr>
<td valign="top" align="left" colspan="2"><p>-----------------------</p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>rojo</code></p></td>
<td valign="top" align="left"><p><code>colorado</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>amarillo</code></p></td>
<td valign="top" align="left"><p><code>&#x00E1;mbar</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>verde</code></p></td>
<td valign="top" align="left"><p><code>verde</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Las l&#x00ED;neas que est&#x00E9;n en <code>a.txt</code>, pero no en <code>b.txt</code> se muestran precedidas del signo de menor</p></list-item>
<list-item><label>&#x25FE;</label> <p>Las l&#x00ED;neas que est&#x00E9;n en <code>b.txt</code> pero no en <code>a.txt</code>, se muestran precedidas del signo de mayor</p></list-item>
<list-item><label>&#x25FE;</label> <p>Las que no cambian, no se muestran</p></list-item>
</list>
<preformat>
koji@mazinger:~$ diff a.txt b.txt
1,2c1,2
<target target-type="page" id="pges_291"/>
&#x003C; rojo
&#x003C; amarillo
---
&#x003E; colorado
&#x003E; &#x00E1;mbar
</preformat>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>1,2c1,2</code> representa el n&#x00FA;mero de l&#x00ED;nea donde sucedi&#x00F3; el cambio, podemos ignorarlo</p></list-item>
<list-item><label>&#x25FE;</label> <p>En entornos de programaci&#x00F3;n, esto sirve para ver cambios en programas y distribuir <italic>parches</italic> (la orden <italic>patch</italic> puede construir <code>b.txt</code> a partir de <code>a.txt</code> y la salida de <code>diff</code>)</p></list-item>
</list>
<p>En administraci&#x00F3;n de sistemas, nos sirve para saber qu&#x00E9; ha cambiado entre dos instantes de tiempo</p>
<p>P.e.</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>En el instante <italic>t</italic><sub>0</sub> ejecutamos <code>ls /directorio &#x003E; t0.txt</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>En el instante <italic>t</italic><sub>1</sub> ejecutamos <code>ls /directorio &#x003E; t1.txt</code></p></list-item>
</list>
<p>Comparamos con <code>diff t0.txt t1.txt</code></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Los ficheros desaparecidos se muestran precedidos del signo de menor</p></list-item>
<list-item><label>&#x25FE;</label> <p>Los ficheros nuevos se muestran precedidos del signo de mayor</p>
<p>Podemos aplicar esto mismo a <code>ps, netstat, who, ...</code></p>
<p>Es &#x00FA;til para</p></list-item>
<list-item><label>&#x25FE;</label> <p>Automatizar mediante scripts</p></list-item>
<list-item><label>&#x25FE;</label> <p>Observar <italic>a ojo</italic> un n&#x00FA;mero elevado de elementos<target target-type="page" id="pges_292"/></p></list-item>
</list>
</sec></sec>
</body>
</book-part>
<book-part id="c18" book-part-type="chapter">
<book-part-meta>
<book-part-id book-part-id-type="publisher-id">URJC</book-part-id>
<title-group>
<label>18.</label>
<title><target target-type="page" id="pges_293"/>BENCHMARK</title>
</title-group>
</book-part-meta>
<body>
<sec id="c17-s3-2">
<title><bold>Benchmark</bold></title>
<p>Resultado de la ejecuci&#x00F3;n de una aplicaci&#x00F3;n que busca estimar el rendimiento de un sistema (inform&#x00E1;tico)</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>En espa&#x00F1;ol podr&#x00ED;amos decir <italic>comparativa</italic>, aunque no resulta tan preciso</p></list-item>
<list-item><label>&#x25FE;</label> <p>Se puede hacer <italic>benchmark</italic> de cpu, disco, tarjeta gr&#x00E1;fica, red, etc</p></list-item>
</list>
</sec>
<sec id="c18-s1">
<label><bold>18.1.</bold></label>
<title><bold>Benchmark de la cpu</bold></title>
<sec id="c18-s1-1">
<title><bold>Benchmark de la cpu</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>MIPS: Millones de instrucciones por segundo. Forma de medir la potencia de un procesador. &#x00DA;til para comparar distintos procesadores, siempre que usen mismo juego de instrucciones, con un benchmark en mismo compilador con mismas optimizaciones.</p></list-item>
<list-item><label>&#x25FE;</label> <p>BogoMIPS: Medida propia de Linux. Estimaci&#x00F3;n aproximada de los MIPS. <italic>Bogus</italic>: Incorrecto, enga&#x00F1;oso
<preformat>
dmesg |grep -i bogo
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>SPECint: Alternativa m&#x00E1;s exacta</p></list-item>
</list>
</sec></sec>
<sec id="c18-s2">
<label><bold>18.2.</bold></label>
<title><bold>Benchmark de red</bold></title>
<sec id="c18-s2-1">
<title><bold>Benchmark de red</bold></title>
<p>Miden la tasa de transferencia m&#x00E1;xima entre un cliente y un servidor (network throughput). Tambi&#x00E9;n se puede traducir por <italic>ancho de banda digital</italic>, rendimiento de red o caudal de datos. <xref ref-type="fn" rid="FN12"><sup>12</sup></xref></p>
<p>Permiten usar diferentes protocolos y tama&#x00F1;os de paquete</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_294"/>iperf. Sencillo, muy popular. Disponible para Windows, Linux, macOs, Android, iOS, FreeBSD</p></list-item>
<list-item><label>&#x25FE;</label> <p>netperf. Sencillo, muy popular. Centrado en Unix/Linux</p></list-item>
<list-item><label>&#x25FE;</label> <p>netio. Sencillo, disponible en Windows, Linux y varios Unix</p></list-item>
<list-item><label>&#x25FE;</label> <p>... entre otros</p></list-item>
</list>
</sec></sec>
<sec id="c18-s2-2">
<title><bold>inxi</bold></title>
<p>inxi es una herramienta muy &#x00FA;til que recopila diversa informaci&#x00F3;n sobre nuestro sistema.</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Instalaci&#x00F3;n:
<preformat>
apt update; apt upgrade; apt install -y inxi
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Basta ejecutar desde un terminal inxi -b para obtener un informe muy completo</p></list-item>
</list>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>System:</code></p></td>
<td valign="top" align="left"><p><code>Host: minisal Kernel: 5.11.0-38-generic x86_64 bits: 64 Console: N/A Distro: Ubuntu 20.04.3 LTS (Focal Fossa)</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>Machine:</code></p></td>
<td valign="top" align="left"><p>Type: Laptop System: Apple product: Macmini7,1 v: 1.0 serial: &#x003C;superuser/root required&#x003E;</p>
<p>Mobo: Apple model: Mac-35C5E08120C7EEAF v: Macmini7,1 serial: &#x003C;superuser/root required&#x003E; UEFI: Apple v: 431.0.0.0.0 date: 02/22/2021</p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>CPU:</code></p></td>
<td valign="top" align="left"><p><code>Dual Core: Intel Core i5-4278U type: MT MCP speed: 800 MHz min/max: 800/3100 MHz</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>Graphics:</code></p></td>
<td valign="top" align="left"><p>Device-1: Intel Haswell-ULT Integrated Graphics driver: i915 v: kernel</p>
<p>Display: server: X.org 1.20.11 driver: i915 tty: 114x60</p>
<p>Message: Advanced graphics data unavailable in console. Try -G --display</p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>Network:</code></p></td>
<td valign="top" align="left"><p>Device-1: Broadcom and subsidiaries BCM4360 802.11ac Wireless Network Adapter driver: wl</p>
<p>Device-2: Broadcom and subsidiaries NetXtreme BCM57766 Gigabit Ethernet PCIe driver: tg3</p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>Drives:</code></p></td>
<td valign="top" align="left"><p><code>Local Storage: total: 931.51 GiB used: 117.52 GiB (12.6%)</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>Info:</code></p></td>
<td valign="top" align="left"><p><code>Processes: 267 Uptime: 3h 56m Memory: 7.65 GiB used: 1.54 GiB (20.2%) Init: systemd runlevel: 5 Shell: bash inxi: 3.0.38</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
</sec>
<sec id="c18-s2-3">
<title><target target-type="page" id="pges_295"/><bold>Instalaci&#x00F3;n de iperf</bold></title>
<p>Paquetizado en Ubuntu, lo instalamos de la forma habitual</p>
<preformat>
apt update
apt upgrade
apt install -y iperf
</preformat>
</sec>
<sec id="c18-s2-4">
<title><bold>Uso de iperf</bold></title>
<p>TCP</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Servidor:
<preformat>
iperf -s
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Cliente:
<preformat>
iperf -c &#x003C;SERVIDOR&#x003E;
</preformat></p></list-item>
</list>
<p>UDP</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Servidor:
<preformat>
iperf -u -s
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Cliente:
<preformat>
iperf -u -c &#x003C;SERVIDOR&#x003E;
</preformat></p></list-item>
</list>
<p>Por omisi&#x00F3;n emplea el puerto TCP/UDP 5000. Se puede cambiar con la opci&#x00F3;n <code>-p</code></p>
</sec>
</body>
<back>
<fn-group>
<fn id="FN12"><p><sup>12</sup> Es com&#x00FA;n denominarlo ancho de banda, sin m&#x00E1;s. Pero este en rigor es el ancho de banda anal&#x00F3;gico, un concepto distinto: un rango de frecuencias<target target-type="page" id="pges_296"/></p></fn>
</fn-group>
</back>
</book-part>
<book-part id="c19" book-part-type="chapter">
<book-part-meta>
<book-part-id book-part-id-type="publisher-id">URJC</book-part-id>
<title-group>
<label>19.</label>
<title><target target-type="page" id="pges_297"/>SESIONES GR&#x00C1;FICAS REMOTAS</title>
</title-group>
</book-part-meta>
<body>
<sec id="c19-s1">
<label><bold>19.1.</bold></label>
<title><bold>Introducci&#x00F3;n</bold></title>
<sec id="c19-s1-1">
<title><bold>Sesiones gr&#x00E1;ficas remotas</bold></title>
<p>Definiciones</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>M&#x00E1;quina local</p>
<p>Equipo en el que trabaja el usuario, donde tiene su pantalla, teclado, y posiblemente, rat&#x00F3;n</p></list-item>
<list-item><label>&#x25FE;</label> <p>M&#x00E1;quina remota</p>
<p>M&#x00E1;quina donde se ejecuta la aplicaci&#x00F3;n a usar. El usuario no tiene acceso a su teclado, pantalla ni rat&#x00F3;n</p></list-item>
</list>
<p>Sesiones de texto / Sesiones remotas</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>El protocolo ssh nos permite trabajar c&#x00F3;modamente en m&#x00E1;quinas Unix remotas, con sesiones de texto</p></list-item>
<list-item><label>&#x25FE;</label> <p>Tambi&#x00E9;n se puede usar ssh en Microsoft Windows, aunque con muchas limitaciones, resulta poco natural</p></list-item>
<list-item><label>&#x25FE;</label> <p>Pero habr&#x00E1; ocasiones en que ser&#x00E1; conveniente o imprescindible usar sesiones gr&#x00E1;ficas en m&#x00E1;quinas remotas</p></list-item>
</list>
</sec></sec>
<sec id="c19-s2">
<label><bold>19.2.</bold></label>
<title><target target-type="page" id="pges_298"/><bold>Protocolos para sesiones gr&#x00E1;ficas remotas (1)</bold></title>
<sec id="c19-s2-1">
<title><bold>Protocolos para sesiones gr&#x00E1;ficas remotas (1)</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Soluciones propietarias como LogMeIn o TeamViewer</p></list-item>
<list-item><label>&#x25FE;</label> <p>RDP. <italic>Remote Desktop Services</italic>, tambi&#x00E9;n conocido como <italic>Terminal Services</italic>.</p>
<p>Nativo en Microsoft Windows, usable desde otras plataformas, p.e.</p>
<p><code>gnome-rdp, vinagre</code> (clientes) o <code>xrdp</code> (servidor)</p>
<p>Puerto por omisi&#x00F3;n: 3389 TCP</p></list-item>
</list>
</sec></sec>
<sec id="c19-s3">
<label><bold>19.3.</bold></label>
<title><bold>Protocolos para sesiones gr&#x00E1;ficas remotas (2)</bold></title>
<sec id="c19-s3-1">
<title><bold>Protocolos para sesiones gr&#x00E1;ficas remotas (2)</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>X11 forwarding. Forma parte de X Window.</p>
<p>Tradicional y nativo en Linux/UNIX, usable en Microsoft Windows.</p>
<p>Normalmente no trabaja sobre un escritorio completo sino con ventanas individuales.</p>
<p>Anticuado auque sigue disponible. Intr&#x00ED;nsecamente poco seguro. Puerto por omisi&#x00F3;n: 6000 TCP</p></list-item>
<list-item><label>&#x25FE;</label> <p>VNC</p>
<p>Protocolo abierto, multiplataforma. Nativo en muchos Linux. Disponible en Microsoft Windows, Unix, *BSD, macOS, Android, iOS, ...</p></list-item>
</list>
</sec></sec>
<sec id="c19-s4">
<label><bold>19.4.</bold></label>
<title><bold>X11 Forwarding</bold></title>
<sec id="c19-s4-1">
<title><bold>X11 Forwarding</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>X Window (a&#x00F1;o 1984) es el protocolo tradicional en Unix para mostrar gr&#x00E1;ficos. En local y tambi&#x00E9;n en remoto</p>
<p>Nada que ver con Microsoft Windows</p></list-item>
<list-item><label>&#x25FE;</label> <p>En 1987 aparece la versi&#x00F3;n 11 de X Window, sigue siendo la versi&#x00F3;n actual. De ah&#x00ED; el nombre X11</p></list-item>
<list-item><label>&#x25FE;</label> <p>X Window es un protocolo cliente-servidor. Aunque la terminolog&#x00ED;a puede ser anti-intuitiva:</p>
<list list-type="bullet">
<list-item><p>La m&#x00E1;quina local es el servidor. En ella est&#x00E1;n los gr&#x00E1;ficos. Normalmente lo llamar&#x00ED;amos <italic>cliente</italic>, pero es el <italic>servidor X11</italic> (ofrece el servicio de representaci&#x00F3;n gr&#x00E1;fica)</p></list-item>
<list-item><p>En la m&#x00E1;quina remota est&#x00E1; el <italic>cliente X Window</italic>. Aunque en esta m&#x00E1;quina est&#x00E1;n los procesos que usan los gr&#x00E1;ficos, esto es, los servicios. (Los servicios son los clientes de los gr&#x00E1;ficos)</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>En la m&#x00E1;quina local puede ser necesario configurar los permisos del servidor
<preformat>
xhost +
</preformat></p>
<p>permite que cualquier cliente X Window desde cualquier m&#x00E1;quina lance ventanas en nuestro servidor X Window local.</p>
<list list-type="bullet">
<list-item><p><target target-type="page" id="pges_299"/>Tambi&#x00E9;n se pueden dar permisos m&#x00E1;s espec&#x00ED;ficos</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Entre dos m&#x00E1;quinas Ubuntu con el mismo usuario en ambas m&#x00E1;quinas, mediante ssh, no es necesario dar permisos adicionales</p></list-item>
</list>
<p>Para lanzar una aplicaci&#x00F3;n gr&#x00E1;fica en una m&#x00E1;quina remota:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Desde la m&#x00E1;quina local hacemos ssh a la m&#x00E1;quina remota con la opci&#x00F3;n</p>
<p><code>-X</code> (may&#x00FA;scula)</p>
<p><code>jperez@gamma12:~$ ssh -X alpha</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>Lanzamos la aplicaci&#x00F3;n en segundo plano</p>
<p>p.e</p>
<p><code>jperez@alpha:~$ xeyes&#x0026;</code></p></list-item>
</list>
</sec></sec>
<sec id="c19-s5">
<label><bold>19.5.</bold></label>
<title><bold>VNC</bold></title>
<sec id="c19-s5-1">
<title><bold>VNC</bold></title>
<p>VNC, <italic>Virtual Network Compute</italic> es un protocolo para abrir sesiones gr&#x00E1;ficas en m&#x00E1;quinas remotas</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Arquitectura cliente-servidor, desarrollado por The Olivetti &#x0026; Oracle Research Lab</p></list-item>
<list-item><label>&#x25FE;</label> <p>La terminolog&#x00ED;a es la habitual, no la de X Window. El cliente est&#x00E1; en la m&#x00E1;quina local, el servidor, en la m&#x00E1;quina remota</p></list-item>
<list-item><label>&#x25FE;</label> <p>Implementaci&#x00F3;n liberada como software libre en 2002</p></list-item>
<list-item><label>&#x25FE;</label> <p>Muy popular. Muchas implementaciones para cualquier plataforma (Microsoft Windows, Linux, Unix, macOS, Android, iOS, Raspberry Pi ...)</p>
<p>Cualquier servidor de cualquier plataforma puede trabajar con cualquier cliente en cualquier otra</p></list-item>
</list>
<p>En Ubuntu</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Tenemos un servidor integrado por omisi&#x00F3;n, llamado vino</p></list-item>
<list-item><label>&#x25FE;</label> <p>Hay un cliente llamado <code>vinagre</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>Tambi&#x00E9;n podemos usar <italic>TightVNC</italic>, entre otras implementaciones</p></list-item>
</list>
<p><target target-type="page" id="pges_300"/>En Windows</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Podemos usar <italic>TightVNC</italic>, entre otras implementaciones</p></list-item>
</list>
<p>En macOS</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Como cliente podemos usar el navegador nativo de macOS, Safari. Basta escribir en la barra de direcciones la direcci&#x00F3;n del servidor:
<preformat>
vnc://maquina:puerto
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Otra opci&#x00F3;n (con cliente y servidor) es RealVNC. Tiene una versi&#x00F3;n comercial y otra libre (RealVNC Open Edition)</p></list-item>
</list>
<p>En Raspbian (Raspberry Pi)</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>El servidor VNC instalado es Real VNC, cuya configuraci&#x00F3;n por omisi&#x00F3;n es incompatible con VNC est&#x00E1;ndar. Hay dos soluciones</p>
<list list-type="order">
<list-item><p>Usar el cliente de Real VNC (vncviewer), disponible en el web de Real VNC</p></list-item>
<list-item><p>Configurar el servidor de Real VNC para que use autenticaci&#x00F3;n VNC, no autenticaci&#x00F3;n UNIX</p></list-item>
</list></list-item>
</list>
</sec>
<sec id="c19-s5-2">
<title><bold>vinagre</bold></title>
<p>El cliente de VNC oficial de Ubuntu es <code>vinagre</code></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Est&#x00E1; desarrollado conjuntamente con <code>vino</code>, algunas opciones avanzadas pueden funcionar mejor con este servidor</p></list-item>
<list-item><label>&#x25FE;</label> <p>Tambi&#x00E9;n tiene soporte para RDP</p>
<p>Uso:
<preformat>
vinagre &#x003C;MAQUINA&#x003E;:&#x003C;PUERTO&#x003E;
</preformat></p></list-item>
</list>
</sec>
<sec id="c19-s5-3">
<title><bold>vino</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>En Ubuntu, el servidor vino est&#x00E1; instalado por omisi&#x00F3;n en las m&#x00E1;quinas con escritorio gr&#x00E1;fico</p></list-item>
<list-item><label>&#x25FE;</label> <p>Por omisi&#x00F3;n trabaja en el puerto 5900 TCP</p></list-item>
<list-item><label>&#x25FE;</label> <p>Para cambiar el puerto del servidor:</p>
<list list-type="order">
<list-item><p><target target-type="page" id="pges_301"/>Instalamos dconf-editor
<preformat>
sudo apt install dconf-editor
</preformat></p></list-item>
<list-item><p>Lanzamos dconf-editor</p>
<p>En
<preformat>
org | gnome | desktop | remote-access
</preformat></p>
<p>Cambiamos <code>alternative-port</code></p>
<p>Activamos <code>use-alternative-port</code></p></list-item>
</list></list-item>
</list>
<p>Normalmente usaremos vino cuando ya tenemos una sesi&#x00F3;n gr&#x00E1;fica abierta en un Ubuntu con escritorio tradicional</p>
<p>Pero no es adecuado para abrir:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Una segunda sesi&#x00F3;n gr&#x00E1;fica en la misma m&#x00E1;quina</p></list-item>
<list-item><label>&#x25FE;</label> <p>Una sesi&#x00F3;n gr&#x00E1;fica en una m&#x00E1;quina remota a la que no tenemos acceso f&#x00ED;sico o en la que no queremos abrir una sesi&#x00F3;n gr&#x00E1;fica tradicional</p></list-item>
<list-item><label>&#x25FE;</label> <p>Una sesi&#x00F3;n en una m&#x00E1;quina donde no queramos un escritorio tan pesado como Unity o Gnome</p></list-item>
</list>
<p>En cualquiera de estos casos</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>En vez de usar <italic>vino</italic>, podremos usar <italic>TightVNC</italic></p></list-item>
</list>
</sec>
<sec id="c19-s5-4">
<title><bold>Servidor de TightVNC en Ubuntu</bold></title>
<p>El servidor de TightVNC es vncserver. Para usarlo necesitaremos, adem&#x00E1;s del propio vncserver, un escritorio. Podr&#x00ED;amos emplear Gnome o Unity, pero son muy pesados. Generalmente ser&#x00E1; m&#x00E1;s adecuado</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>O bien un escritorio ligero como Xfce4</p></list-item>
<list-item><label>&#x25FE;</label> <p>O bien un gestor de ventanas como Openbox</p>
<p>(podemos considerar a Openbox como un escritorio ultra-ligero)</p></list-item>
</list>
<p>Los pasos son:</p>
<list list-type="order">
<list-item><p>Instalaci&#x00F3;n de vncserver</p></list-item>
<list-item><p><target target-type="page" id="pges_302"/>Instalaci&#x00F3;n del escritorio</p></list-item>
<list-item><p>Preparaci&#x00F3;n del escritorio</p></list-item>
<list-item><p>Lanzamiento del servidor</p></list-item>
<list-item><p>Lanzamiento del cliente</p></list-item>
</list>
<sec id="c19-s5-5">
<title><bold>1 Instalaci&#x00F3;n de vncserver</bold></title>
<p>En caso de que TightVNC no est&#x00E9; instalado en nuestro sistema</p>
<list list-type="order">
<list-item><p>Como en cualquier instalaci&#x00F3;n de un paquete nuevo, suele ser recomendable actualizar el sistema
<preformat>
sudo apt update; sudo apt upgrade
</preformat></p></list-item>
<list-item><p>Instalamos el paquete
<preformat>
sudo apt install tightvncserver
</preformat></p></list-item>
</list>
</sec>
<sec id="c19-s5-6">
<title><bold>2 Instalaci&#x00F3;n del escritorio</bold></title>
<p>Si vamos a usar Openbox</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Para saber si est&#x00E1; instalado, intentamos ejecutar
<preformat>
openbox-session
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Para instalarlo
<preformat>
sudo apt install openbox
</preformat></p></list-item>
</list>
<p>Si vamos a usar Xfce4</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Para saber si est&#x00E1; instalado, intentamos ejecutar
<preformat>
xfce4-session
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Para instalarlo
<preformat>
sudo apt install xfce4
</preformat></p></list-item>
</list>
</sec>
<sec id="c19-s5-7">
<title><bold>3 Preparaci&#x00F3;n del escritorio</bold></title>
<list list-type="order">
<list-item><p>En el servidor escribiremos un fichero <code>~/.vnc/xstartup</code> con el siguiente contenido</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Si vamos a usar Xfce4
<preformat>
#!/bin/bash
<target target-type="page" id="pges_303"/>
xrdb ~/.Xresources
startxfce4&#x0026;
xterm&#x0026;
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Si vamos a usar Openbox
<preformat>
#!/bin/bash
xrdb ~/.Xresources
/usr/bin/openbox-session
</preformat></p></list-item>
</list></list-item>
<list-item><p>Como a cualquier script, le damos permiso de ejecuci&#x00F3;n, p.e. <code>chmod 755 ~/.vnc/xstartup</code></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>En Openbox lanzamos aplicaciones desde el men&#x00FA; contextual del bot&#x00F3;n secundario (derecho) del rat&#x00F3;n</p></list-item>
<list-item><label>&#x25FE;</label> <p>Para que funcione puede ser necesario a&#x00F1;adir el siguiente fichero <code>~/.config/openbox/menu.xml</code>
<preformat>
&#x003C;?xml version=&#x0022;1.0&#x0022; encoding=&#x0022;utf-8&#x0022;?&#x003E;
&#x003C;openbox_menu xmlns=&#x0022;http://openbox.org/&#x0022;
&#x00A0;&#x00A0;&#x00A0;xmlns:xsi=&#x0022;http://www.w3.org/2001/XMLSchema-instance&#x0022;
&#x00A0;&#x00A0;&#x00A0;&#x00A0;xsi:schemaLocation=&#x0022;/usr/share/openbox/menu.xsd&#x0022;&#x003E;
&#x00A0;&#x00A0;&#x00A0;&#x003C;menu id=&#x0022;root-menu&#x0022; label=&#x0022;Openbox 3&#x0022;&#x003E;
&#x00A0;&#x00A0;&#x00A0;&#x003C;item label=&#x0022;Run Program&#x0022;&#x003E;
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x003C;action name=&#x0022;Execute&#x0022;&#x003E;
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x003C;execute&#x003E;
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;gmrun
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x003C;/execute&#x003E;
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x003C;/action&#x003E;
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x003C;/item&#x003E;
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x003C;separator/&#x003E;
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x003C;item label=&#x0022;Terminal&#x0022;&#x003E;
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x003C;action name=&#x0022;Execute&#x0022;&#x003E;
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x003C;execute&#x003E;
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;xterm
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x003C;/execute&#x003E;
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x003C;/action&#x003E;
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x003C;/item&#x003E;
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x003C;/menu&#x003E;
&#x003C;/openbox_menu&#x003E;
</preformat></p></list-item>
</list></list-item>
</list>
</sec>
<sec id="c19-s5-8">
<title><target target-type="page" id="pges_304"/><bold>4 Lanzamiento del servidor</bold></title>
<p>Para lanzar el servidor ejecutamos la orden vncserver, indicando el tama&#x00F1;o de la pantalla y la profundidad del color, especificada en n&#x00FA;mero de bits</p>
<p>Ejemplo:</p>
<p><code>vncserver -geometry 1024x768 -depth 16</code></p>
<p>Si vamos a usarlo con frecuencia, puede ser conveniente poner esta orden en un script de shell, p.e.</p>
<p><code>~/bin/mi_vncserver</code></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>La primera vez que lancemos <code>vncserver</code>, nos preguntar&#x00E1; la contrase&#x00F1;a de la sesi&#x00F3;n.</p>
<p>Quedar&#x00E1; almacenada en el fichero</p>
<p><code>~/.vnc/passwd</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>Si necesitamos cambiar la contrase&#x00F1;a, ejecutamos
<preformat>
vncpasswd
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Tambi&#x00E9;n podemos cambiar la contrase&#x00F1;a desde un script.</p>
<p>P.e. la contrase&#x00F1;a <italic>sesamo</italic> ser&#x00ED;a:</p>
<p><code>echo &#x201C;sesamo\nsesamo\n\n&#x201D; | vncpasswd</code></p></list-item>
</list>
<p>Como cualquier demonio, vncserver deber&#x00E1; atarse a un puerto para aceptar peticiones, en su caso a un puerto TCP</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>El puerto por omisi&#x00F3;n es el 5900 TCP</p></list-item>
<list-item><label>&#x25FE;</label> <p>Atenci&#x00F3;n, el servidor de VNC no emplea el concepto <italic>puerto TCP</italic>, sino <italic>display port</italic>, donde</p>
<p><italic>puerto TCP</italic> = 5900 + <italic>display port</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>Sin embargo, en el cliente normalmente s&#x00ED; indicaremos el puerto TCP, no el <italic>display port</italic></p></list-item>
</list>
<p>Si no indicamos otra cosa, el demonio <code>vncserver</code> intentar&#x00E1; usar el <italic>display port</italic> 0, puerto TCP 5900.</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Si est&#x00E1; libre, mostrar&#x00E1; el mensaje
<preformat>
New &#x0027;X&#x0027; desktop is gamma:0
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Si est&#x00E1; ocupado (tal vez por vino), lo intentar&#x00E1; en el <italic>display port</italic> 1, puerto TCP 5901. Si tiene &#x00E9;xito el mensaje ser&#x00E1;
<preformat>
New &#x0027;X&#x0027; desktop is gamma:1
</preformat></p>
<p><target target-type="page" id="pges_305"/>y as&#x00ED; sucesivamente con los <italic>display port</italic> 2, 3, etc (puertos TCP 5902, TCP 5903, etc)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Tambi&#x00E9;n podemos indicar expl&#x00ED;citamente el <italic>display port</italic> que usar&#x00E1; el servidor:</p>
<p>Ejemplo: <italic>display port</italic> 10 (puerto TCP 5910)</p>
<p><code>vncserver :10 -geometry 1024x768 -depth 16</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>La sesi&#x00F3;n permanecer&#x00E1; abierta hasta que la matemos expl&#x00ED;citamente con
<preformat>
vncserver -kill :10
</preformat></p>
<list list-type="bullet">
<list-item><p>Esta orden mata el proceso <code>vncserver</code>, la sesi&#x00F3;n X11 en <code>/tmp/.X11-unix</code> y los logs en <code>~/.vnc/</code></p></list-item>
</list></list-item>
</list>
</sec>
<sec id="c19-s5-9">
<title><bold>5 Lanzamiento del cliente</bold></title>
<p>En la m&#x00E1;quina local ejecutamos</p>
<p><code>vinagre &#x003C;SERVIDOR&#x003E;:&#x003C;PUERTO</code></p>
<p>p.e.</p>
<p><code>vinagre gamma:59012</code></p>
<p>Observa que en este caso indicamos el puerto TCP, no el <italic>display port</italic></p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Recuerda que para cerrar la sesi&#x00F3;n hay que matar el servidor, no basta con cerrar el cliente</p></list-item>
</list>
</sec></sec>
<sec id="c19-s5-10">
<title><bold>Uso de VNC en Docker</bold></title>
<p>VNC es una forma conveniente de abrir sesiones gr&#x00E1;ficas dentro de un contenedor.</p>
<p>Configuraci&#x00F3;n de ejemplo para el <italic>display port</italic> 0, contrase&#x00F1;a <italic>sesamo</italic></p>
<p><code>entrypoint.sh:</code></p>
<preformat>
#!/bin/bash
vncserver :0 -geometry 1024x768 -depth 16
/usr/bin/xterm&#x0026;
/bin/bash
</preformat>
<p>Dockerfile:</p>
<preformat>
FROM ubuntu:16.04
ENV USER root
<target target-type="page" id="pges_306"/>
RUN apt-get update &#x0026;&#x0026; DEBIAN_FRONTEND=noninteractive &#x0026;&#x0026; \
&#x00A0;&#x00A0;&#x00A0;apt-get install -y \
&#x00A0;&#x00A0;&#x00A0;tightvncserver openbox \
&#x00A0;&#x00A0;&#x00A0;xterm

# Expose VNC port
EXPOSE 5900

#set password for vnc
RUN echo &#x201C;sesamo\nsesamo\n\n&#x201D; | vncpasswd

COPY entrypoint.sh /
ENTRYPOINT [&#x0022;/entrypoint.sh&#x0022;]
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;<ext-link ext-link-type="uri" xlink:href="http://ortuno.es/Dockerfile_vnc.txt">http://ortuno.es/Dockerfile_vnc.txt</ext-link>
</preformat>
<p>Lanzamiento del contendor:</p>
<preformat>
&#x00A0;&#x00A0;&#x00A0;#!/bin/bash
&#x00A0;&#x00A0;&#x00A0;DISPLAY_NUMBER=0
&#x00A0;&#x00A0;&#x00A0;PORT=$((DISPLAY_NUMBER+5900))
&#x00A0;&#x00A0;&#x00A0;IMAGEN=vnc
&#x00A0;&#x00A0;&#x00A0;NOMBRE=jper${IMAGEN}01
&#x00A0;&#x00A0;&#x00A0;USUARIO=jperez

docker run -it --rm -h ${NOMBRE} --name ${NOMBRE} -p ${PORT}:${PORT} \
&#x00A0;&#x00A0;&#x00A0;-e DISPLAY=:${DISPLAY_NUMBER} ${USUARIO}/${IMAGEN}
&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;&#x00A0;<ext-link ext-link-type="uri" xlink:href="http://ortuno.es/lanza_vnc.txt">http://ortuno.es/lanza_vnc.txt</ext-link>
</preformat>
</sec>
</sec>
</body>
</book-part>
<book-part id="c20" book-part-type="chapter">
<book-part-meta>
<book-part-id book-part-id-type="publisher-id">URJC</book-part-id>
<title-group>
<label>20.</label>
<title><target target-type="page" id="pges_307"/>NETSTAT</title>
</title-group>
</book-part-meta>
<body>
<sec id="c19-s5-11">
<title><bold>netstat</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>netstat es una herramienta b&#x00E1;sica en cualquier SSOO (Unix, Linux, macOS, Windows...)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Muestra informaci&#x00F3;n sobre conexiones de red, tablas de encaminamiento, estad&#x00ED;sticas de los interfaces, NAT y multicast</p></list-item>
</list>
<p>Nos permitir&#x00E1; comprobar qu&#x00E9; demonios est&#x00E1;n funcionando en nuestra m&#x00E1;quina</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Es una herramienta b&#x00E1;sica para depurar cualquier servicio</p></list-item>
<list-item><label>&#x25FE;</label> <p>Un principio b&#x00E1;sico de seguridad en cualquier sistema es tener activos solo los servicios necesarios, cualquier nuevo servicio siempre implica un cierto riesgo</p></list-item>
</list>
</sec>
<sec id="c19-s5-12">
<title><bold>Informaci&#x00F3;n sobre conexiones de red, Linux</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><code>-tu</code></p>
<p>Muestra informaci&#x00F3;n sobre TCP y UDP (y no sobre los <italic>Unix domain sockets</italic>)</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>-p</code></p>
<p>Indica el programa a quien pertenece el socket</p>
<p>Si lo ejecuta un usuario ordinario, solo muestra algunos nombres</p>
<p>Si lo ejecuta root, muestra todos los nombres</p></list-item>
<list-item><label>&#x25FE;</label> <p><code>-a</code></p>
<p>Muestra todos los sockets, no solamente las conexiones establecidas sino tambi&#x00E9;n los sockets que est&#x00E1;n <italic>escuchando</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p><code>-n</code></p>
<p>Muestra direcciones IP y no nombres de m&#x00E1;quina.</p>
<p>Muestra n&#x00FA;meros de puerto, no nombre de servicio asociado (en los <italic>well know ports</italic>). No intenta resolver nombres de m&#x00E1;quina.<target target-type="page" id="pges_308"/></p></list-item>
</list>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" colspan="6"><p><code>koji@afrodita:~$ sudo netstat -tupan</code></p></td>
</tr>
<tr>
<td valign="top" align="left" colspan="6"><p><code>Conexiones activas de Internet (servidores y establecidos)</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>Prot</code></p></td>
<td valign="top" align="left"><p><code>Recv-Q</code></p></td>
<td valign="top" align="left"><p><code>Send-Q Direcci&#x00F3;n_Local</code></p></td>
<td valign="top" align="left"><p><code>Direcci&#x00F3;n_Externa</code></p></td>
<td valign="top" align="left"><p><code>Estado PID/Program name</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>tcp</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
<td valign="top" align="left"><p><code>0 0.0.0.0:22</code></p></td>
<td valign="top" align="left"><p><code>0.0.0.0:*</code></p></td>
<td valign="top" align="left"><p><code>ESCUCHAR</code></p></td>
<td valign="top" align="left"><p><code>27488/sshd</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>tcp</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
<td valign="top" align="left"><p><code>0 127.0.0.1:631</code></p></td>
<td valign="top" align="left"><p><code>0.0.0.0:*</code></p></td>
<td valign="top" align="left"><p><code>ESCUCHAR</code></p></td>
<td valign="top" align="left"><p><code>1320/cupsd</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>tcp</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
<td valign="top" align="left"><p><code>0 193.147.71.120:22</code></p></td>
<td valign="top" align="left"><p><code>193.147.71.62:56881</code></p></td>
<td valign="top" align="left"><p><code>ESTABLECIDO</code></p></td>
<td valign="top" align="left"><p><code>26653/sshd: koji</code></p></td>
</tr>
<tr><td valign="top" align="left"><p><code>tcp6</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
<td valign="top" align="left"><p><code>0 :::79</code></p></td>
<td valign="top" align="left"><p><code>:::*</code></p></td>
<td valign="top" align="left"><p><code>ESCUCHAR</code></p></td>
<td valign="top" align="left"><p><code>19514/xinetd</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>tcp6</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
<td valign="top" align="left"><p><code>0 :::22</code></p></td>
<td valign="top" align="left"><p><code>:::*</code></p></td>
<td valign="top" align="left"><p><code>ESCUCHAR</code></p></td>
<td valign="top" align="left"><p><code>27488/sshd</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>tcp6</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
<td valign="top" align="left"><p><code>0 ::1:631</code></p></td>
<td valign="top" align="left"><p><code>:::*</code></p></td>
<td valign="top" align="left"><p><code>ESCUCHAR</code></p></td>
<td valign="top" align="left"><p><code>1320/cupsd</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>udp</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
<td valign="top" align="left"><p><code>0 0.0.0.0:49573</code></p></td>
<td valign="top" align="left"><p><code>0.0.0.0:*</code></p></td>
<td valign="top" align="left"></td>
<td valign="top" align="left"><p><code>32398/avahi-daemon:</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>udp</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
<td valign="top" align="left"><p><code>0 0.0.0.0:5353</code></p></td>
<td valign="top" align="left"><p><code>0.0.0.0:*</code></p></td>
<td valign="top" align="left"></td>
<td valign="top" align="left"><p><code>32398/avahi-daemon:</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" colspan="6"><p><code>koji@afrodita:~$ sudo netstat -tupa</code></p></td>
</tr>
<tr>
<td valign="top" align="left" colspan="6"><p><code>Conexiones activas de Internet (servidores y establecidos)</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>Prot</code></p></td>
<td valign="top" align="left"><p><code>Recv-Q</code></p></td>
<td valign="top" align="left"><p><code>Send-Q Direcci&#x00F3;n_Local</code></p></td>
<td valign="top" align="left"><p><code>Direcci&#x00F3;n_Externa</code></p></td>
<td valign="top" align="left"><p><code>Estado</code></p></td>
<td valign="top" align="left"><p><code>PID/Program name</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>tcp</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
<td valign="top" align="left"><p><code>0 *:ssh</code></p></td>
<td valign="top" align="left"><p><code>*:*</code></p></td>
<td valign="top" align="left"><p><code>ESCUCHAR</code></p></td>
<td valign="top" align="left"><p><code>27488/sshd</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>tcp</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
<td valign="top" align="left"><p><code>0 localhost:ipp</code></p></td>
<td valign="top" align="left"><p><code>*:*</code></p></td>
<td valign="top" align="left"><p><code>ESCUCHAR</code></p></td>
<td valign="top" align="left"><p><code>1320/cupsd</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>tcp</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
<td valign="top" align="left"><p><code>0 afrodita:ssh</code></p></td>
<td valign="top" align="left"><p><code>doublas:56881</code></p></td>
<td valign="top" align="left"><p><code>ESTABLECIDO</code></p></td>
<td valign="top" align="left"><p><code>26653/sshd: koji</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>tcp6</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
<td valign="top" align="left"><p><code>0 [::]:finger</code></p></td>
<td valign="top" align="left"><p><code>[::]:*</code></p></td>
<td valign="top" align="left"><p><code>ESCUCHAR</code></p></td>
<td valign="top" align="left"><p><code>19514/xinetd</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>tcp6</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
<td valign="top" align="left"><p><code>0 [::]:ssh</code></p></td>
<td valign="top" align="left"><p><code>[::]:*</code></p></td>
<td valign="top" align="left"><p><code>ESCUCHAR</code></p></td>
<td valign="top" align="left"><p><code>27488/sshd</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>tcp6</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
<td valign="top" align="left"><p><code>0 ip6-localhost:ipp</code></p></td>
<td valign="top" align="left"><p><code>[::]:*</code></p></td>
<td valign="top" align="left"><p><code>ESCUCHAR</code></p></td>
<td valign="top" align="left"><p><code>1320/cupsd</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>udp</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
<td valign="top" align="left"><p><code>0 *:49573</code></p></td>
<td valign="top" align="left"><p><code>*:*</code></p></td>
<td valign="top" align="left"></td>
<td valign="top" align="left"><p><code>32398/avahi-daemon:</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>udp</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
<td valign="top" align="left"><p><code>0 *:mdns</code></p></td>
<td valign="top" align="left"><p><code>*:*</code></p></td>
<td valign="top" align="left"></td>
<td valign="top" align="left"><p><code>32398/avahi-daemon:</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<p>Problema:</p>
<p>Netstat no comprueba que el demonio que est&#x00E1; atado a un puerto sea el demonio <italic>habitual</italic> en ese puerto.</p>
<p>Ejemplo: si atamos un servicio cualquiera al puerto 80, netstat lo tomar&#x00E1; por un servidor web</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Un demonio puede escuchar en un puerto (p.e. el 22) de cualquier direcci&#x00F3;n de la m&#x00E1;quina, por tanto, en cualquiera de los interfaces de la m&#x00E1;quina</p>
<p><table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>tcp</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
<td valign="top" align="left"><p><code>0.0.0.0:22</code></p></td>
<td valign="top" align="left"><p><code>0.0.0.0:*</code></p></td>
<td valign="top" align="left"><p><code>ESCUCHAR</code></p></td>
<td valign="top" align="left"><p><code>27488/sshd</code></p></td>
</tr>
</tbody>
</table>
</table-wrap></p></list-item>
<list-item><label>&#x25FE;</label> <p>O bien puede escuchar en un puerto (p.e. el 631), pero no de todas las direcciones/todos los interfaces, sino de una en concreto</p>
<p><table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>tcp</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
<td valign="top" align="left"><p><code>127.0.0.1:631</code></p></td>
<td valign="top" align="left"><p><code>0.0.0.0:*</code></p></td>
<td valign="top" align="left"><p><code>ESCUCHAR</code></p></td>
<td valign="top" align="left"><p><code>1320/cupsd</code></p></td>
</tr>
</tbody>
</table>
</table-wrap></p>
<p><target target-type="page" id="pges_309"/>Este demonio est&#x00E1; en la m&#x00E1;quina afrodita, pero no atiende peticiones hechas a la direcci&#x00F3;n p&#x00FA;blica de afrodita, solamente a <italic>localhost</italic> /127.0.0.1</p></list-item>
<list-item><label>&#x25FE;</label> <p>Podemos usar <code>grep</code> para filtrar la salida de netstat
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left" colspan="6"><p>koji@afrodita:~$ netstat -tupan |grep 22</p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>tcp</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
<td valign="top" align="left"><p><code>0 0.0.0.0:22</code></p></td>
<td valign="top" align="left"><p><code>0.0.0.0:*</code></p></td>
<td valign="top" align="left"><p><code>ESCUCHAR</code></p></td>
<td valign="top" align="left"><p><code>-</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>tcp</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
<td valign="top" align="left"><p><code>0 193.147.71.120:22</code></p></td>
<td valign="top" align="left"><p><code>193.147.71.62:34285</code></p></td>
<td valign="top" align="left"><p><code>ESTABLECIDO</code></p></td>
<td valign="top" align="left"><p><code>-</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>tcp6</code></p></td>
<td valign="top" align="left"><p><code>0</code></p></td>
<td valign="top" align="left"><p><code>0 :::22</code></p></td>
<td valign="top" align="left"><p><code>:::*</code></p></td>
<td valign="top" align="left"><p><code>ESCUCHAR</code></p></td>
<td valign="top" align="left"><p><code>-</code></p></td>
</tr>
</tbody>
</table>
</table-wrap></p>
</list-item>
</list></sec>
<sec id="c19-s5-13">
<title><bold>Netsat en macOS</bold></title>
<p>Uso t&#x00ED;pico:</p>
<p>netstat -f inet -an</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Para ver solo las conexiones tcp y udp, la opci&#x00F3;n no es -tu sino <code>-f inet</code></p></list-item>
<list-item><label>&#x25FE;</label> <p><code>-a</code> y <code>-n</code> se comportan como en Linux</p></list-item>
<list-item><label>&#x25FE;</label> <p>Para saber qu&#x00E9; proceso escucha en cada puerto, no hay un equivalente a <code>-p</code></p>
<p>Pero podemos usar
<preformat>
sudo lsof -iTCP:12345 -sTCP:LISTEN
</preformat></p>
<p>(Donde 12345 ser&#x00ED;a el puerto a consultar) o bien
<preformat>
sudo lsof -i -n -P |grep UDP
</preformat></p></list-item>
</list>
</sec>
</body>
</book-part>
<book-part id="c21" book-part-type="chapter">
<book-part-meta>
<book-part-id book-part-id-type="publisher-id">URJC</book-part-id>
<title-group>
<label>21.</label>
<title><target target-type="page" id="pges_310"/><target target-type="page" id="pges_311"/>EDITORES DE TEXTO</title>
</title-group>
</book-part-meta>
<body>
<sec id="c21-s1">
<label><bold>21.1.</bold></label>
<title><bold>Introducci&#x00F3;n</bold></title>
<sec id="c21-s1-1">
<title><bold>Introducci&#x00F3;n</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Los editores de texto crean y modifican ficheros de texto <italic>plano</italic></p>
<p>Se emplea en programaci&#x00F3;n y en configuraci&#x00F3;n de sistemas</p></list-item>
<list-item><label>&#x25FE;</label> <p>Los procesadores de texto crean y modifican ficheros de texto con formato de fuente (negritas, cursivas, tipos de letra,etc), de p&#x00E1;gina (interlineado, m&#x00E1;rgenes, etc) e im&#x00E1;genes</p></list-item>
</list>
<p>En cualquier Linux hay disponibles muchos editores &#x00BF;Cu&#x00E1;l es mejor?</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Depende en buena parte de gustos personales</p></list-item>
<list-item><label>&#x25FE;</label> <p>Depende de d&#x00F3;nde vayamos a usarlos</p></list-item>
<list-item><label>&#x25FE;</label> <p>Este es un asunto t&#x00ED;pico para <italic>guerras de religi&#x00F3;n</italic></p></list-item>
</list>
<fig><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-33.jpg"/>
<attrib>Bilo y Nano. Javier Malonda. (CC BY-NC-ND 3.0)</attrib></fig>
</sec>
<sec id="c21-s1-2">
<title><target target-type="page" id="pges_312"/><bold>Tipos de editor de texto</bold></title>
<list list-type="order">
<list-item><p>Editores en modo gr&#x00E1;fico</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Su curva de aprendizaje suele ser m&#x00E1;s suave</p></list-item>
<list-item><label>&#x25FE;</label> <p>Adecuados para trabajar como programador en un ordenador <italic>est&#x00E1;ndar</italic>, local y con gr&#x00E1;ficos</p></list-item>
</list></list-item>
<list-item><p>Editores en modo texto (editores de consola)</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Curva de aprendizaje m&#x00E1;s dura (excepto algunos muy sencillos/simplones)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Permiten trabajar en remoto con la misma facilidad que en local</p>
<list list-type="bullet">
<list-item><p>Podemos administrar sin problemas nuestra m&#x00E1;quina Linux</p>
<p>p.e. desde un Windows prestado y con mala conexi&#x00F3;n. O incluso una PDA y un tel&#x00E9;fono m&#x00F3;vil</p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Son los &#x00FA;nicos disponibles en sistemas empotrados, como routers</p></list-item>
<list-item><label>&#x25FE;</label> <p>Suelen ser los &#x00FA;nicos disponibles en ordenadores a medio instalar, averiados, herramientas de rescate, etc</p></list-item>
</list></list-item>
</list>
</sec></sec>
<sec id="c21-s2">
<label><bold>21.2.</bold></label>
<title><bold>vi</bold></title>
<sec id="c21-s2-1">
<title><bold>vi</bold></title>
<p>El editor <italic>est&#x00E1;ndar</italic> en Unix. Desarrollado por Bill Joy (Co-fundador de Sun Microsystems) en el a&#x00F1;o 1976.</p>
<p>Hoy usamos clones, especialmente vim o m&#x00E1;s recientemente, neovim</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Si no nos gusta vi/vim/neovim, casi siempre podremos instalar otro</p></list-item>
<list-item><label>&#x25FE;</label> <p>Pero para poder instalar otro, suele ser imprescindible manejar al menos las &#x00F3;rdenes elementales de vi</p></list-item>
</list>
</sec></sec>
<sec id="c21-s2-2">
<title><bold>Ventajas</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Normalmente estar&#x00E1; disponible y funcionando en cualquier m&#x00E1;quina Unix</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_313"/>Hay versiones para la mayor&#x00ED;a de los SSOO (Windows, macOS...)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Es muy flexible y potente, conoci&#x00E9;ndolo bien se puede trabajar a gran velocidad</p></list-item>
<list-item><label>&#x25FE;</label> <p>Pensado para sesiones remotas con malas conexiones en un <italic>terminal tonto</italic> de los a&#x00F1;os 70, el ADM-3A</p></list-item>
<list-item><label>&#x25FE;</label> <p>Si trabajamos en una m&#x00E1;quina con gr&#x00E1;ficos, puede ser conveniente usar un vim en m&#x00F3;do gr&#x00E1;fico, mejor integrado con el escritorio. Permitir&#x00E1; usar el rat&#x00F3;n, funcionar&#x00E1; el portapapeles del escritorio y podr&#x00E1; tener men&#x00FA;s, de utilidad para ordenes que a&#x00FA;n no hemos memorizado</p>
<list list-type="bullet">
<list-item><p>En Windows, gvim</p></list-item>
<list-item><p>En Linux, gvim <xref ref-type="fn" rid="FN13"><sup>13</sup></xref></p></list-item>
<list-item><p>En OS X, MacVim (mvim)</p></list-item>
</list></list-item>
</list>
<fig><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-34.jpg"/></fig>
</sec>
<sec id="c21-s2-3">
<title><bold>Inconvenientes</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Interfaz de usuario muy anticuado, el usuario debe memorizar &#x00F3;rdenes &#x00A1;donde hasta las may&#x00FA;sculas son significativas!</p></list-item>
</list>
</sec>
<sec id="c21-s2-s1">
<label><bold>21.2.1.</bold></label>
<title><target target-type="page" id="pges_314"/><bold>Modos de vi</bold></title>
<sec id="c21-s2-s1-1">
<title><bold>Modos de vi</bold></title>
<list list-type="order">
<list-item><p>Modo orden (tambi&#x00E9;n llamado modo comando, modo normal)</p></list-item>
<list-item><p>En este modo guardamos el fichero, leemos otro, salimos, copiamos, pegamos, etc</p></list-item>
<list-item><p>Modo insertar (tambi&#x00E9;n llamado modo texto o modo entrada)</p>
<p>En este modo insertamos texto</p></list-item>
<list-item><p>Modo reemplazar (tambi&#x00E9;n llamado modo texto o modo entrada, sin distinguirlo del modo insertar)</p>
<p>En este modo reemplazamos texto</p></list-item>
</list>
<fig><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-35.jpg"/></fig>
</sec></sec>
<sec id="c21-s2-s2">
<label><bold>21.2.2.</bold></label>
<title><bold>&#x00D3;rdenes imprescindibles</bold></title>
<sec id="c21-s2-s2-1">
<title><bold>&#x00D3;rdenes imprescindibles</bold></title>
<p>Desde la shell</p>
<preformat>
koji@mazinger:~$ vi nombre_fichero.txt
</preformat>
<p>(Edita el fichero del nombre indicado. Si no existe, lo crea)</p>
<p>Desde vi</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>a</code></p></td>
<td valign="top" align="left"><p><code>Pasar de modo orden a modo insertar</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>R</code></p></td>
<td valign="top" align="left"><p><code>Pasar de modo orden a modo reemplazar</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>Esc</code></p></td>
<td valign="top" align="left"><p><code>Volver a modo orden</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><target target-type="page" id="pges_315"/><code>x</code></p></td>
<td valign="top" align="left"><p><code>Borrar un car&#x00E1;cter</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>J</code></p></td>
<td valign="top" align="left"><p><code>Unir la l&#x00ED;nea actual con la l&#x00ED;nea siguiente</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>:wq</code></p></td>
<td valign="top" align="left"><p><code>Escribir el fichero y salir</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>:q!</code></p></td>
<td valign="top" align="left"><p><code>Salir sin guardar el fichero</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<p>Este conjunto de &#x00F3;rdenes es suficiente para editar cualquier fichero</p>
</sec></sec>
<sec id="c21-s2-s3">
<label><bold>21.2.3.</bold></label>
<title><bold>&#x00D3;rdenes b&#x00E1;sicas</bold></title>
<sec id="c21-s2-s3-1">
<title><bold>&#x00D3;rdenes b&#x00E1;sicas</bold></title>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>:r nombre</code></p></td>
<td valign="top" align="left"><p><code>leer un fichero</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>:w nombre</code></p></td>
<td valign="top" align="left"><p><code>escribir fichero</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>u</code></p></td>
<td valign="top" align="left"><p><code>Deshacer &#x00FA;ltimo cambio</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>ctrl r</code></p></td>
<td valign="top" align="left"><p><code>Rehacer lo &#x00FA;ltimo deshecho</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>D</code></p></td>
<td valign="top" align="left"><p><code>Borrar hasta final de l&#x00ED;nea</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>dd</code></p></td>
<td valign="top" align="left"><p><code>Borrar l&#x00ED;nea actual</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>yy</code></p></td>
<td valign="top" align="left"><p><code>copiar (yanc) linea</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>p</code></p></td>
<td valign="top" align="left"><p><code>pegar lo ultimo copiado o borrado</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>.</code></p></td>
<td valign="top" align="left"><p><code>Repetir la &#x00FA;ltima orden</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>/patron</code></p></td>
<td valign="top" align="left"><p><code>Busca un patr&#x00F3;n (hacia adelante)</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>n</code></p></td>
<td valign="top" align="left"><p><code>Repetir b&#x00FA;squeda</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>N</code></p></td>
<td valign="top" align="left"><p><code>Buscar en direcci&#x00F3;n inversa a anterior</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>G</code></p></td>
<td valign="top" align="left"><p><code>Ir a Final del archivo</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>5G</code></p></td>
<td valign="top" align="left"><p><code>Ir a l&#x00ED;nea 5</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>%</code></p></td>
<td valign="top" align="left"><p><code>Salta al par&#x00E9;ntesis que se corresponda con el par&#x00E9;ntesis actual (o llave, corchete...)</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<p>Casi todas las &#x00F3;rdenes permiten anteponer un n&#x00FA;mero, que indica c&#x00FA;antas veces se repetir&#x00E1;</p>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>dd</code></p></td>
<td valign="top" align="left"><p><code>Borrar l&#x00ED;nea actual</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>10dd</code></p></td>
<td valign="top" align="left"><p><code>Borrar 10 l&#x00ED;neas</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>u</code></p></td>
<td valign="top" align="left"><p><code>Deshacer un cambio</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>3u</code></p></td>
<td valign="top" align="left"><p><code>Deshacer &#x00FA;ltimos 3 cambios</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>cw</code></p></td>
<td valign="top" align="left"><p><code>Cambiar una palabra</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>5cw</code></p></td>
<td valign="top" align="left"><p><code>Cambiar 5 palabras</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
</sec></sec>
<sec id="c21-s2-s4">
<label><bold>21.2.4.</bold></label>
<title><target target-type="page" id="pges_316"/><bold>Otras &#x00F3;rdenes</bold></title>
<sec id="c21-s2-s4-1">
<title><bold>Otras &#x00F3;rdenes</bold></title>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>0</code></p></td>
<td valign="top" align="left"><p><code>ir a principio l&#x00ED;nea</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>$</code></p></td>
<td valign="top" align="left"><p><code>ir a fin linea</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>w</code></p></td>
<td valign="top" align="left"><p><code>ir a siguiente palabra</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>b</code></p></td>
<td valign="top" align="left"><p><code>ir a palabra anterior</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>r</code></p></td>
<td valign="top" align="left"><p><code>Sustituir 1 car&#x00E1;cter</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>cw</code></p></td>
<td valign="top" align="left"><p><code>Cambiar palabra (change word)</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>dw</code></p></td>
<td valign="top" align="left"><p><code>Borrar hasta fin palabra (delete word)</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>yw</code></p></td>
<td valign="top" align="left"><p><code>Copiar palabra</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>*</code></p></td>
<td valign="top" align="left"><p><code>Buscar palabra igual a la palabra sobre la que est&#x00E1; el cursor</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>ma</code></p></td>
<td valign="top" align="left"><p><code>Poner marca de texto a</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>mb</code></p></td>
<td valign="top" align="left"><p><code>Poner marca de texto b</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>&#x0027;a</code></p></td>
<td valign="top" align="left"><p><code>ir a marca a</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>&#x0027;b</code></p></td>
<td valign="top" align="left"><p><code>ir a marca b</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>Ctrl G</code></p></td>
<td valign="top" align="left"><p><code>Indicar linea actual</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>~</code></p></td>
<td valign="top" align="left"><p><code>Pasar de may. a minusc. o al rev&#x00E9;s</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<table-wrap>
<table frame="hsides" rules="groups">
<tbody>
<tr>
<td valign="top" align="left"><p><code>:49,53 w! fichero</code></p></td>
<td valign="top" align="left"><p><code>Escribir en fichero lineas de 49 a 53</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>:.,53 w! fichero</code></p></td>
<td valign="top" align="left"><p><code>Escribir en fichero desde linea actual hasta l&#x00ED;nea 53</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>:1,$ s/digo/diego/g</code></p></td>
<td valign="top" align="left"><p><code>Buscar todas las cadenas &#x201C;digo&#x201D; desde la l&#x00ED;nea</code></p>
<p><code>1 hasta el final, y reemplazarlas por &#x201C;diego&#x201D;</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>:set nu</code></p></td>
<td valign="top" align="left"><p><code>Indicar el n&#x00B0; de linea</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>:set nonu</code></p></td>
<td valign="top" align="left"><p><code>Desactivar n&#x00B0; de linea</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>:set ic</code></p></td>
<td valign="top" align="left"><p><code>Ignore case (Insensible a mayus/min)</code></p></td>
</tr>
<tr>
<td valign="top" align="left"><p><code>:set noic</code></p></td>
<td valign="top" align="left"><p><code>Desactiva ic</code></p></td>
</tr>
</tbody>
</table>
</table-wrap>
<p>Podemos configurar vim de forma persistente creando un fichero de configuraci&#x00F3;n</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>En Unix/Linux
<preformat>
~/.vimrc
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>En Windows
<preformat>
c:\Archivos de programa\vim\_vimrc (XP/Vista)
</preformat></p>
<p><code>c:\Program File (x86)\vim\_vimrc</code> (Windows 7)</p></list-item>
</list>
<p><target target-type="page" id="pges_317"/>Por ejemplo, el fichero de configuraci&#x00F3;n puede contener:</p>
<preformat>
set vb
set ic
set tabstop=4
syntax on
</preformat>
<p>Esto activa la <italic>visual bell</italic> (que elimina los molestos pitidos del terminal), ignora may&#x00FA;sculas/min&#x00FA;sculas, fija el tabulador en 4 espacios y colorea el texto si reconoce la sintaxis</p>
<p>En Windows podemos a&#x00F1;adir</p>
<preformat>
set enc=utf-8
</preformat>
<p>De esta forma, emplear&#x00E1; por omisi&#x00F3;n la misma codificaci&#x00F3;n que en Unix/Linux Para m&#x00E1;s informaci&#x00F3;n sobre vi, consulta la p&#x00E1;gina web <italic>vi lovers home</italic> <italic>page</italic></p>
</sec></sec>
<sec id="c21-s3">
<label><bold>21.3.</bold></label>
<title><bold>Editores ligeros</bold></title>
<sec id="c21-s3-1">
<title><bold>Editores ligeros</bold></title>
<p>Hemos visto que vi tiene muchas ventajas. Pero si nos <italic>asusta</italic> su interfaz de usuario y necesitamos un editor en modo texto, disponemos de editores ligeros como</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>mcedit (editor del mc, midnight commander)</p></list-item>
<list-item><label>&#x25FE;</label> <p>nano (clon de pico)</p></list-item>
<list-item><label>&#x25FE;</label> <p>joe</p></list-item>
</list>
</sec></sec>
<sec id="c21-s4">
<label><bold>21.4.</bold></label>
<title><bold>Emacs / XEmacs</bold></title>
<sec id="c21-s4-1">
<title><bold>Emacs / XEmacs</bold></title>
<p>Editor cl&#x00E1;sico en Unix. Uno de los m&#x00E1;s conocidos, se populariza a mediados de los 80</p>
<p>Emacs trabaja en modo texto, XEmacs en modo gr&#x00E1;fico</p>
<p><target target-type="page" id="pges_318"/>Ventajas</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Complet&#x00ED;simo, es mucho m&#x00E1;s que un editor. Permite leer correo, news, se integra con gran cantidad de herramientas...</p></list-item>
<list-item><label>&#x25FE;</label> <p>M&#x00F3;dulos para muchos lenguajes de programaci&#x00F3;n</p></list-item>
<list-item><label>&#x25FE;</label> <p>Da formato y color al fuente, con mucha calidad.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Completamente personalizable (en lisp)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Puede emular a vi</p></list-item>
</list>
<p>Inconvenientes</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Muy grande y pesado, consume muchos recursos.</p></list-item>
<list-item><label>&#x25FE;</label> <p>Su uso resulta complicado</p></list-item>
<list-item><label>&#x25FE;</label> <p>A&#x00FA;n para las tareas sencillas, tiene alguna peculiaridad que lo hace poco intuitivo al usuario actual</p></list-item>
</list>
</sec>
<sec id="c21-s4-2">
<title><bold>Usando emacs</bold></title>
<fig><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-36.jpg"/></fig>
</sec>
<sec id="c21-s4-3">
<title><bold>emacs &#x2260; xemacs</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>menu</p></list-item>
<list-item><label>&#x25FE;</label> <p>pantalla edici&#x00F3;n</p></list-item>
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_319"/>l&#x00ED;nea de modo</p></list-item>
<list-item><label>&#x25FE;</label> <p>l&#x00ED;nea comandos</p></list-item>
</list>
<fig><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-37.jpg"/></fig>
</sec>
<sec id="c21-s4-4">
<title><bold>Atajos de teclado</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>CTRL-K borrar linea</p></list-item>
<list-item><label>&#x25FE;</label> <p>ESC-X query-replace, ESC-X replace</p></list-item>
<list-item><label>&#x25FE;</label> <p>ESC-X goto-line</p></list-item>
<list-item><label>&#x25FE;</label> <p>CTRL-X-S salvar</p></list-item>
<list-item><label>&#x25FE;</label> <p>CTRL-X-F encontrar fichero</p></list-item>
<list-item><label>&#x25FE;</label> <p>CTRL-W=cortar, CTRL-Y=pegar</p></list-item>
<list-item><label>&#x25FE;</label> <p>CTRL-@=marca</p></list-item>
</list>
</sec>
<sec id="c21-s4-5">
<title><bold>Enlaces sobre Emacs/XEmacs</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Emacs <ext-link ext-link-type="uri" xlink:href="http://www.gnu.org/software/emacs">http://www.gnu.org/software/emacs</ext-link></p></list-item>
<list-item><label>&#x25FE;</label> <p>XEmacs <ext-link ext-link-type="uri" xlink:href="http://www.xemacs.org">http://www.xemacs.org</ext-link></p></list-item>
</list>
</sec></sec>
<sec id="c21-s5">
<label><bold>21.5.</bold></label>
<title><target target-type="page" id="pges_320"/><bold>Otros editores</bold></title>
<sec id="c21-s5-s1">
<label><bold>21.5.1.</bold></label>
<title><bold>Atom</bold></title>
<sec id="c21-s5-s1-1">
<title><bold>Atom</bold></title>
<fig><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-38.jpg"/></fig>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Editor de texto, libre y gratuito, disponible para Windows, Linux y macOS</p></list-item>
</list>
<p>Ventajas</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>M&#x00E1;s que un editor, es un IDE (Integrated development environment) con mucha funcionalidad: da formato, color, autocompleta, se integra con el compilador, con git, incluye colaboraci&#x00F3;n en tiempo real (teletype)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Ampliable mediante paquetes, que se pueden instalar desde el terminal (apm)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Desarrollado por GitHub</p></list-item>
<list-item><label>&#x25FE;</label> <p>Moderno: la primera versi&#x00F3;n es de 2014, se ha vuelto muy popular</p></list-item>
</list>
<p>Inconvenientes</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Exige una sesi&#x00F3;n gr&#x00E1;fica</p></list-item>
</list>
<p>enlaces</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><ext-link ext-link-type="uri" xlink:href="https://atom.io/">https://atom.io/</ext-link></p></list-item>
</list>
</sec></sec>
<sec id="c21-s5-s2">
<label><bold>21.5.2.</bold></label>
<title><target target-type="page" id="pges_321"/><bold>gedit</bold></title>
<sec id="c21-s5-s2-1">
<title><bold>gedit</bold></title>
<fig><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-39.jpg"/></fig>
<p>Inconvenientes</p>
<p>Editor de texto de prop&#x00F3;sito general, es el <italic>block de notas</italic> de gnome</p>
<p>Ventajas</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Muy sencillo y f&#x00E1;cil de manejar</p></list-item>
<list-item><label>&#x25FE;</label> <p>Exige una sesi&#x00F3;n gr&#x00E1;fica</p></list-item>
<list-item><label>&#x25FE;</label> <p>Ha mejorado mucho, pero sigue teniendo poca funcionalidad</p></list-item>
<list-item><label>&#x25FE;</label> <p>Tal vez no sea la mejor opci&#x00F3;n si tenemos disponible editores como atom, scite...</p></list-item>
</list>
</sec>
<sec id="c21-s5-s3">
<label><bold>21.5.3.</bold></label>
<title><bold>SciTE</bold></title>
<sec id="c21-s5-s3-1">
<title><bold>SciTE</bold></title>
<fig><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-40.jpg"/></fig>
<p><target target-type="page" id="pges_322"/>Editor de texto multiplataforma Ventajas</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Muy completo: Da formato, color, se integra con el compilador...</p></list-item>
<list-item><label>&#x25FE;</label> <p>Versiones para Win32 y X Window</p></list-item>
<list-item><label>&#x25FE;</label> <p>Muy f&#x00E1;cil de manejar</p></list-item>
<list-item><label>&#x25FE;</label> <p>Es el editor de <italic>anjuta</italic>, el IDE de gnome</p></list-item>
</list>
<p>Inconvenientes</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Exige una sesi&#x00F3;n gr&#x00E1;fica</p></list-item>
<list-item><label>&#x25FE;</label> <p>No muy extendido</p></list-item>
<list-item><label>&#x25FE;</label> <p>Hay editores m&#x00E1;s avanzados</p></list-item>
</list>
<p>enlaces</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><ext-link ext-link-type="uri" xlink:href="http://www.scintilla.org/SciTE.html">http://www.scintilla.org/SciTE.html</ext-link></p></list-item>
</list>
</sec></sec>
<sec id="c21-s5-s4">
<label><bold>21.5.4.</bold></label>
<title><bold>Kate</bold></title>
<sec id="c21-s5-s4-1">
<title><bold>Kate</bold></title>
<fig><graphic xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="MD-2-URJC_fig-41.jpg"/></fig>
<p>Es el editor del escritorio KDE</p>
<p>Ventajas</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Muy completo: Da formato, color, se integra con el compilador...</p></list-item>
<list-item><label>&#x25FE;</label> <p>Muy buen <italic>pretty printing</italic></p></list-item>
<list-item><label>&#x25FE;</label> <p>Muy f&#x00E1;cil de manejar</p></list-item>
</list>
<p><target target-type="page" id="pges_323"/>Inconvenientes</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Exige una sesi&#x00F3;n gr&#x00E1;fica</p></list-item>
<list-item><label>&#x25FE;</label> <p>No muy extendido</p></list-item>
<list-item><label>&#x25FE;</label> <p>Hay cosas editores m&#x00E1;s avanzados hacen mejor</p></list-item>
<list-item><label>&#x25FE;</label> <p>Es necesario tener instalado KDE (o al menos buena parte)</p></list-item>
<list-item><label>&#x25FE;</label> <p>No disponible en otras plataformas</p></list-item>
</list>
<p>Enlaces</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><ext-link ext-link-type="uri" xlink:href="http://kate-editor.org">http://kate-editor.org</ext-link><target target-type="page" id="pges_324"/></p></list-item>
</list>
</sec></sec></sec></sec>
</body>
<back>
<fn-group>
<fn id="FN13"><p><sup>13</sup> el nombre del paquete es vim-gtk</p></fn>
</fn-group>
</back>
</book-part>
<book-part id="c22" book-part-type="chapter">
<book-part-meta>
<book-part-id book-part-id-type="publisher-id">URJC</book-part-id>
<title-group>
<label>22.</label>
<title><target target-type="page" id="pges_325"/>MARKDOWN</title>
</title-group>
</book-part-meta>
<body>
<sec id="c21-s5-s4-2">
<title><bold>Introducci&#x00F3;n</bold></title>
<sec id="c22-s1">
<label><bold>22.1.</bold></label>
<title><bold>Introducci&#x00F3;n</bold></title>
<p>Markdown es un lenguaje de marcado ligero</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Creado por John Gruber y Aaron Swartz en 2004</p></list-item>
<list-item><label>&#x25FE;</label> <p>Sintaxis muy sencilla, f&#x00E1;cil de escribir a mano con un editor de texto plano</p></list-item>
<list-item><label>&#x25FE;</label> <p>No intenta reemplazar a lenguajes de marcado m&#x00E1;s potentes como HTML, Latex, PostScript, DocBook, etc</p></list-item>
<list-item><label>&#x25FE;</label> <p>Muy popular en la actualidad para entornos donde se desea una cierta <italic>maquetaci&#x00F3;n</italic> del texto, no muy exigente: foros, blogs, mensajer&#x00ED;a instant&#x00E1;nea, documentaci&#x00F3;n de c&#x00F3;digo fuente, ficheros <italic>readme</italic>, documentos internos, manuales de usuario <italic>online</italic>, etc</p></list-item>
</list>
</sec></sec>
<sec id="c22-s1-1">
<title><bold>Herramientas para Markdown</bold></title>
<sec id="c22-s2">
<label><bold>22.2.</bold></label>
<title><bold>Herramientas para Markdown</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>El texto en Markdown se puede leer <italic>tal cual</italic> o se puede usar alguna herramienta para generar HTML o PDF a partir de &#x00E9;l, como:</p>
<list list-type="bullet">
<list-item><p>Grip, una herramienta muy sencilla para generar html desde markdown</p>
<p><ext-link ext-link-type="uri" xlink:href="https://github.com/joeyespo/grip">https://github.com/joeyespo/grip</ext-link></p></list-item>
<list-item><p>Pandoc, una herramienta muy potente que convierte documentos desde y hasta m&#x00FA;ltiples formatos (Markdown, Html, epub, docbook, LaTex, RTF, ODT, ...)</p>
<p><ext-link ext-link-type="uri" xlink:href="https://pandoc.org/">https://pandoc.org/</ext-link></p></list-item>
</list></list-item>
<list-item><label>&#x25FE;</label> <p>Tambi&#x00E9;n hay herramientas que facilitan su edici&#x00F3;n</p>
<list list-type="bullet">
<list-item><p>Aplicaciones completas de escritorio como Abricotine, Remarkable o ReText</p></list-item>
<list-item><p><target target-type="page" id="pges_326"/>Plugins para editores como Atom, Vim o Visual Studio Code</p></list-item>
<list-item><p>Editores <italic>online</italic> como GitBook</p></list-item>
</list></list-item>
</list>
<p>Aqu&#x00ED; recomendaremos Pandoc o Abricotine</p>
</sec>
<sec id="c22-s2-1">
<title><bold>Variantes de Markdown</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>La definici&#x00F3;n inicial del lenguaje era bastante informal, con muchas imprecisiones. Herramientas muy variadas lo fueron incorporando, cada una con interpretaciones y extensiones ligeramente distintas en los aspectos no b&#x00E1;sicos</p></list-item>
<list-item><label>&#x25FE;</label> <p>Hay por tanto muchas variantes de Markdown. Tal vez el intento de normalizaci&#x00F3;n m&#x00E1;s extendido es GFM (<italic>GitHub Flavored Markdown</italic>, a&#x00F1;o 2017)</p></list-item>
</list>
</sec></sec>
<sec id="c22-s2-2">
<title><bold>P&#x00E1;rrafos</bold></title>
<sec id="c22-s3">
<label><bold>22.3.</bold></label>
<title><bold>P&#x00E1;rrafos</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Los saltos de l&#x00ED;nea <italic>ordinarios</italic> se ignoran. En el momento del <italic>rendering</italic>, la l&#x00ED;nea se rompe por donde donde corresponda</p></list-item>
<list-item><label>&#x25FE;</label> <p>Para hacer un p&#x00E1;rrafo (punto y aparte) es necesario escribir al menos una l&#x00ED;nea en blanco</p></list-item>
<list-item><label>&#x25FE;</label> <p>Para forzar un salto de l&#x00ED;nea, se pueden escribir dos espacios antes del salto de l&#x00ED;nea</p>
<list list-type="bullet">
<list-item><p>Esto es problem&#x00E1;tico porque es invisible. Muchas herramientas admiten escribirlo en html: <code>&#x003C;br&#x003E;</code></p></list-item>
</list></list-item>
</list>
</sec></sec>
<sec id="c22-s3-1">
<title><bold>Secciones en el texto</bold></title>
<sec id="c22-s4">
<label><bold>22.4.</bold></label>
<title><bold>Secciones en el texto</bold></title>
<preformat>
# Encabezado nivel 1 (Secci&#x00F3;n)
## Encabezado nivel 2 (Subsecci&#x00F3;n)
(...)
###### Encabezado nivel 6 (Sub-sub-sub-sub-sub-secci&#x00F3;n)
</preformat>
<p>Es necesario dejar un espacio entre la almohadilla y el t&#x00ED;tulo</p>
</sec></sec>
<sec id="c22-s4-1">
<title><target target-type="page" id="pges_327"/><bold>Cursiva, negrita, enlaces</bold></title>
<sec id="c22-s5">
<label><bold>22.5.</bold></label>
<title><bold>Cursiva, negrita, enlaces</bold></title>
<p>Cursiva</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Una barra baja al comienzo y final del texto a resaltar <code>_ejemplo cursiva_</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>O bien un asterisco al comienzo y al final del texto a resaltar <code>*ejemplo cursiva*</code></p></list-item>
</list>
<p>Negrita</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Dos barras bajas al comienzo y final del texto a resaltar <code>_ _ejemplo negrita_ _</code></p></list-item>
<list-item><label>&#x25FE;</label> <p>O bien dos astericos al comienzo y al final del texto a resaltar <code>**ejemplo negrita**</code></p></list-item>
</list>
<p>Enlaces</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Descripci&#x00F3;n del enlace, entre corchetes, seguido de la URL, entre par&#x00E9;ntesis
<preformat>
[Universidad Rey Juan Carlos](<ext-link ext-link-type="uri" xlink:href="https://urjc.es">https://urjc.es</ext-link>)
</preformat></p></list-item>
</list>
</sec></sec>
<sec id="c22-s6">
<label><bold>22.6.</bold></label>
<title><bold>Im&#x00E1;genes</bold></title>
<sec id="c22-s6-1">
<title><bold>Im&#x00E1;genes</bold></title>
<p>Se pueden a&#x00F1;adir im&#x00E1;genes con la misma sintaxis que los enlaces, pero a&#x00F1;adiendo una admiraci&#x00F3;n. Esta es una extensi&#x00F3;n GFM que no soportan todas las herramientas</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Fichero en una direcci&#x00F3;n web
<preformat>
![Logo de la URJC](<ext-link ext-link-type="uri" xlink:href="https://gsyc.urjc.es/~mortuno/urjc.gif">https://gsyc.urjc.es/~mortuno/urjc.gif</ext-link>)
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Fichero en un trayecto absoluto de mi ordenador
<preformat>
![Pantallazo 1](/home/jperez/fotos/pantallazo01.png)
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Fichero en el mismo directorio que el documento actual
<preformat>
![Pantallazo 2](pantallazo02.png)
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Fichero en un subdirectorio del directorio actual
<preformat>
![Pantallazo 3](images/pantallazo03.png)
</preformat></p></list-item>
</list>
<p><target target-type="page" id="pges_328"/>Posiblemente esto es lo m&#x00E1;s recomendable: escribir el fichero en un subdirectorio llamado <italic>images</italic> dentro del directorio donde est&#x00E1; el documento. Observa que NO es</p>
<preformat>
![Pantallazo 3](/images/pantallazo01.png)
</preformat>
<p>La barra antes del directorio indicar&#x00ED;a un trayecto absoluto</p>
</sec></sec>
<sec id="c22-s7">
<label><bold>22.7.</bold></label>
<title><bold>Listas</bold></title>
<sec id="c22-s7-1">
<title><bold>Listas sin ordenar</bold></title>
<p>Se crean con un gui&#x00F3;n seguido de un espacio</p>
<preformat>
- Sota
&#x00A0;&#x00A0;&#x00A0;- Sota de espadas
&#x00A0;&#x00A0;&#x00A0;- Sota de oros
- Caballo
La figura del caballero, generalmente llamado
*caballo* es una peculiaridad de la baraja espa&#x00F1;ola
que sustituye a la figura de la reina que aparece
en la mayor&#x00ED;a de las restantes barajas. Son cuatro:
- Caballo de oros
- ...
</preformat>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Para hacer sublistas, a&#x00F1;adimos indentaci&#x00F3;n. Basta con hacerlo en la primera l&#x00ED;nea del p&#x00E1;rrafo. Tambi&#x00E9;n usamos indentaci&#x00F3;n para que el p&#x00E1;rrafo pertenezca a un elemento de la lista y no sea un p&#x00E1;rrafo distinto. Es necesario escribir el mismo n&#x00FA;mero de espacios que en la l&#x00ED;nea que abri&#x00F3; ese nivel</p></list-item>
<list-item><label>&#x25FE;</label> <p>En vez de guiones se pueden usar asteriscos</p></list-item>
</list>
</sec>
<sec id="c22-s7-2">
<title><bold>Listas ordenadas</bold></title>
<p>Se crean como las listas no ordenadas, pero con un n&#x00FA;mero, un punto y un espacio</p>
<preformat>
1. An&#x00E1;lisis
1. Dise&#x00F1;o
1. Codificaci&#x00F3;n
1. Prueba
</preformat>
<p>El n&#x00FA;mero que escribamos es irrelevante. Puede ser una secuencia correcta como 1,2,3, todo unos, n&#x00FA;meros fuera de secuencia...</p>
</sec></sec>
<sec id="c22-s8">
<label><bold>22.8.</bold></label>
<title><target target-type="page" id="pges_329"/><bold>C&#x00F3;digo</bold></title>
<sec id="c22-s8-1">
<title><bold>C&#x00F3;digo</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>C&#x00F3;digo dentro de una l&#x00ED;nea</p>
<p>Se escribe entre comillas invertidas. (la comilla a la derecha de la letra P). Ejemplo:
<preformat>
Ejecuta la orden &#x0060;ls -l&#x0060;
</preformat></p></list-item>
<list-item><label>&#x25FE;</label> <p>Bloques de c&#x00F3;digo</p>
<list list-type="bullet">
<list-item><p>Apertura:Tres comillas invertidas. Se puede a&#x00F1;adir el nombre del lenguaje</p></list-item>
<list-item><p>Cierre: Tres comillas invertidas</p></list-item>
</list>
<p>Propio de Github Markdown, no siempre disponible
<preformat>
&#x0060;&#x0060;&#x0060;python
#!/usr/bin/env python3
</preformat>
<preformat>
def main():
return
</preformat>
<preformat>
if _ _name_ _ == &#x0022; _ _main_ _ &#x0022;:
main()
&#x0060;&#x0060;&#x0060;
</preformat></p></list-item>
</list>
</sec></sec>
<sec id="c22-s9">
<label><bold>22.9.</bold></label>
<title><bold>Tablas</bold></title>
<sec id="c22-s9-1">
<title><bold>Tablas</bold></title>
<p>En GFM tambi&#x00E9;n se pueden crear tablas</p>
<preformat>
Hora | Lunes | Martes | Mi&#x00E9;rcoles
---|-------|--------|------
9:00 | Tal | Tal | Cual
11:00| Esto | Lo otro | M&#x00E1;s all&#x00E1;
</preformat>
</sec></sec>
<sec id="c22-s10">
<label><bold>22.10.</bold></label>
<title><bold>Generaci&#x00F3;n de HTML</bold></title>
<sec id="c22-s10-1">
<title><bold>Generaci&#x00F3;n de HTML</bold></title>
<p>Fichero Markdown de ejemplo</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><target target-type="page" id="pges_330"/><ext-link ext-link-type="uri" xlink:href="https://gsyc.urjc.es/ mortuno/md/ejemplo.md">https://gsyc.urjc.es/ mortuno/md/ejemplo.md</ext-link></p></list-item>
<list-item><label>&#x25FE;</label> <p>Conversi&#x00F3;n a HTML con pandoc, sin plantilla de estilo
<preformat>
pandoc -s ejemplo.md -o ejemplo_no_css.html
</preformat></p></list-item>
</list>
<p>Resultado:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><ext-link ext-link-type="uri" xlink:href="https://gsyc.urjc.es/ mortuno/md/ejemplo no css.html">https://gsyc.urjc.es/ mortuno/md/ejemplo_no_css.html</ext-link></p></list-item>
</list>
<p>Es usable pero bastante <italic>feo</italic>. Mejora mucho con la hoja de estilos de pandoc</p>
<preformat>
pandoc -s -c pandoc.css ejemplo.md -o ejemplo_css_01.html
</preformat>
<p>Resultado:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><ext-link ext-link-type="uri" xlink:href="https://gsyc.urjc.es/ mortuno/md/ejemplo css 01.html">https://gsyc.urjc.es/ mortuno/md/ejemplo_css_01.html</ext-link></p></list-item>
</list>
<p>En Internet encontraremos muchas otras hojas de estilo css. Por ejemplo esta:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><ext-link ext-link-type="uri" xlink:href="https://gist.github.com/killercup/5917178">https://gist.github.com/killercup/5917178</ext-link></p></list-item>
</list>
<p>Resultado:</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p><ext-link ext-link-type="uri" xlink:href="https://gsyc.urjc.es/ mortuno/md/ejemplo css 02.html">https://gsyc.urjc.es/ mortuno/md/ejemplo_css_02.html</ext-link></p></list-item>
</list>
<p>Lo m&#x00E1;s sencillo es dejar los ficheros css en el mismo directorio que el fichero md. Puedes obtenerlos desde estos enlaces: <ext-link ext-link-type="uri" xlink:href="https://gsyc.urjc.es/%7Emortuno/md.css">css de pandoc</ext-link>, <ext-link ext-link-type="uri" xlink:href="https://gsyc.urjc.es/%7Emortuno/md.css">css de killercup</ext-link></p>
</sec>
<sec id="c22-s10-2">
<title><bold>Otras opciones de pandoc</bold></title>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>A&#x00F1;adiendo a pandoc la opci&#x00F3;n <code>--toc</code> se crea una tabla de contenidos al principio el documento (un &#x00ED;ndice)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Pandoc dar&#x00E1; un <italic>warning</italic> porque nuestro documento no tiene t&#x00ED;tulo.</p>
<p>En Markdown no hay forma est&#x00E1;ndar de incluir metainformaci&#x00F3;n al documento, pero muchas herramientas soportan el formato de YAML para metadatos, justo al pricipio del documento
<preformat>
---
title: Introducci&#x00F3;n al formato Markdown
---
</preformat></p></list-item>
</list>
<p>Ejemplo:</p>
<p><ext-link ext-link-type="uri" xlink:href="https://gsyc.urjc.es/%7Emortuno/md/fpi_practica_08.md.html">fpi_practica_08.md</ext-link></p>
<p><ext-link ext-link-type="uri" xlink:href="https://gsyc.urjc.es/%7Emortuno/md/fpi_practica_08_css_01.html">fpi_practica_08_css_01.html</ext-link></p>
<p><ext-link ext-link-type="uri" xlink:href="https://gsyc.urjc.es/%7Emortuno/md/fpi_practica_08_css_01.html">fpi_practica_08_css_02.html</ext-link></p>
<p><target target-type="page" id="pges_331"/>Este script facilita el uso de pandoc</p>
<preformat>
<italic>#!/bin/bash</italic>
<italic># Leave uncomented the style sheet that you prefer</italic>
<italic>#TEMPLATE=md.css # <ext-link ext-link-type="uri" xlink:href="https://gist.github.com/killercup/5917178">https://gist.github.com/killercup/5917178</ext-link></italic>
TEMPLATE=pandoc.css
</preformat>
<preformat>
if test $# -eq 0 || test $# -gt 1
then
&#x00A0;&#x00A0;&#x00A0;echo &#x201C;Es necesario indicar un argumento (y solo uno)&#x201D; &#x003E;&#x0026;2
&#x00A0;&#x00A0;&#x00A0;exit
fi
<target target-type="page" id="pges_332"/>
filename=$(basename &#x201D;$1&#x201D;)
extension=&#x201D;${filename##*.}&#x201D;
filename=&#x201D;${filename%.*}&#x201D;
</preformat>
<preformat>
pandoc -s --toc -c ${TEMPLATE} $1 -o ${filename}.html
</preformat>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>Recibe como argumento un fichero .<italic>md</italic> (o .<italic>tex</italic>, entre otros formatos)</p></list-item>
<list-item><label>&#x25FE;</label> <p>Genera una versi&#x00F3;n del documento en HTML</p></list-item>
<list-item><label>&#x25FE;</label> <p>Puedes descargarlo aqu&#x00ED;: <ext-link ext-link-type="uri" xlink:href="http://gsyc.urjc.es/ mortuno/mypandoc">http://gsyc.urjc.es/ mortuno/mypandoc</ext-link></p></list-item>
<list-item><label>&#x25FE;</label> <p>No olvides dejar el .css en el mismo directorio que el .md</p></list-item>
</list>
<p>Este script usa pandoc para convertir un fichero desde markdown hasta markdown. El formato final no cambia, pero resulta muy &#x00FA;til, porque <italic>limpia</italic> el fichero: l&#x00ED;neas homog&#x00E9;neas, estilo homog&#x00E9;neo, etc</p>
<preformat>
<italic>#!/bin/bash</italic>
<italic># -s standalone document</italic>
<italic># -S typographically smart</italic>
if test $# -eq 0 || test $# -gt 1
then
&#x00A0;&#x00A0;&#x00A0;echo &#x201C;Es necesario indicar un argumento (y solo uno)&#x201D; &#x003E;&#x0026;2
&#x00A0;&#x00A0;&#x00A0;exit
fi
filename=$(basename &#x201D;$1&#x201D;)
extension=&#x201D;${filename##*.}&#x201D;
filename=&#x201D;${filename%.*}&#x201D;
if test $extension != md then
&#x00A0;&#x00A0;&#x00A0;echo &#x201C;Es necesario que la extensi&#x00F3;n sea .md&#x201D; &#x003E;&#x0026;2
&#x00A0;&#x00A0;&#x00A0;exit
fi

tmp_name=/tmp/${filename}.$$.md pandoc -s $1 -o ${tmp_name}
mv ${tmp_name} $1
</preformat>
<p>Puedes descargarlo aqu&#x00ED;: <ext-link ext-link-type="uri" xlink:href="http://gsyc.urjc.es/%7Emortuno/clean_md">http://gsyc.urjc.es/ mortuno/clean_md</ext-link></p>
</sec>
<sec id="c22-s10-3">
<title><bold>grip</bold></title>
<p>Una herramienta alternativa a pandoc para convertir ficheros markdown en ficheros HTML es grip</p>
<list list-type="simple">
<list-item><label>&#x25FE;</label> <p>En ocasiones genera HTML de mayor calidad</p></list-item>
<list-item><label>&#x25FE;</label> <p>Actualmente no permite insertar css, pero podemos a&#x00F1;adirlo nosotros f&#x00E1;cilmente, basta a&#x00F1;adir un elemento <code>&#x003C;style&#x003E;</code> o mejor un <code>&#x003C;link&#x003E;</code></p></list-item>
</list>
<p>Para instalar grip, ejecutamos (con privilegios de administrador)</p>
<preformat>
sudo apt install grip
</preformat>
<p>Para usarlo:</p>
<preformat>
grip FICHERO_ENTRADA.md --export FICHERO_SALIDA.html
</preformat>
</sec>
</sec>
</body>
<back>
<ref-list id="b2">
<title><bold>Referencias</bold></title>
<ref id="CIT007"><mixed-citation publication-type="webpage"><ext-link ext-link-type="uri" xlink:href="https://www.markdownguide.org/basic-syntax/">https://guides.github.com/features/mastering-markdown/</ext-link></mixed-citation></ref>
<ref id="CIT008"><mixed-citation publication-type="webpage"><ext-link ext-link-type="uri" xlink:href="https://www.markdownguide.org/basic-syntax/">https://www.markdownguide.org/basic-syntax/</ext-link></mixed-citation></ref>
</ref-list>
</back>
</book-part>
</book-body>
</book>
