Prefacio
Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y
comentarios: translation-feedback@[Link]
Este libro llevará tus habilidades con la línea de comandos de Linux al
siguiente nivel, para que puedas trabajar de forma más rápida, inteligente
y eficaz.
Si eres como la mayoría de los usuarios de Linux, aprendiste tus primeras
habilidades con la línea de comandos en el trabajo, o leyendo un libro de
introducción, o instalando Linux en casa y simplemente probando cosas.
He escrito este libro para ayudarte a dar el siguiente paso: desarrollar
habilidades intermedias y avanzadas en la línea de comandos de Linux.
Está lleno de técnicas y conceptos que espero que transformen tu forma
de interactuar con Linux y aumenten tu productividad. Piensa en él como
un segundo libro sobre el uso de Linux que te lleva más allá de lo básico.
La línea de comandos es la más sencilla de las interfaces, pero también la
más difícil. Es sencilla porque no se te presenta nada más que un
prompt, que espera a que ejecutes cualquier comando que conozcas:1
Es un reto porque todo lo que hay más allá de la indicación es
responsabilidad tuya. No hay iconos, botones ni menús amigables que te
guíen. En su lugar, cada comando que escribes es un acto creativo. Esto es
cierto para los comandos básicos, como listar tus archivos:
$ ls
y comandos más elaborados como éste:
$ paste <(echo {1..10}.jpg | sed 's/ /\n/g') \
<(echo {0..9}.jpg | sed 's/ /\n/g') \
| sed 's/^/mv /' \
| bash
Si te quedas mirando el comando anterior y piensas: "¿Qué demonios
es eso?" o "Nunca necesitaría un comando tan complejo", este libro es
para ti.2
Lo que aprenderás
Este libro te hará más rápido y eficaz en tres habilidades esenciales:
Elegir o construir comandos para resolver un problema
empresarial planteado
Ejecutar esos comandos con eficacia
Navegar por el sistema de archivos de Linux con facilidad
Al final, entenderás lo que ocurre entre bastidores cuando ejecutas un
comando, para poder predecir mejor los resultados (y no desarrollar
supersticiones). Verás una docena de métodos diferentes para lanzar
comandos y aprenderás cuándo utilizar cada uno de ellos para sacar el
máximo partido. También aprenderás trucos y consejos prácticos para ser
más productivo, como:
Construir comandos complejos a partir de otros más sencillos,
paso a paso, para resolver problemas del mundo real, como
gestionar contraseñas o generar diez mil archivos de prueba.
Ahorrar tiempo organizando tu directorio personal de forma
inteligente para que no tengas que buscar archivos
Transformar archivos de texto y consultarlos como bases de
datos para alcanzar objetivos empresariales
Controlar las funciones de apuntar y hacer clic de Linux desde la
línea de comandos, como copiar y pegar con el portapapeles, y
recuperar y procesar datos web, sin levantar las manos del
teclado.
Sobre todo, aprenderás buenas prácticas generales para que,
independientemente de los comandos que ejecutes, puedas tener más
éxito en el uso cotidiano de Linux y ser más competitivo en el mercado
laboral. Este es el libro que desearía haber tenido cuando aprendí Linux.
Lo que este libro no es
Este libro no optimiza tu ordenador Linux para que funcione de forma
más eficiente. Te hace más eficiente a la hora de interactuar con Linux.
Este libro tampoco es una referencia exhaustiva de la línea de comandos:
hay cientos de comandos y funciones que no menciono. Este libro trata de
la pericia. Enseña un conjunto cuidadosamente seleccionado de
conocimientos sobre la línea de comandos en un orden práctico para
desarrollar tus habilidades. Si quieres una guía de referencia, prueba mi
otro libro, Linux Pocket Guide (O'Reilly).
Público y requisitos previos
Este libro asume que tienes experiencia con Linux; no es una
introducción. Es para usuarios que quieran mejorar sus habilidades con
la línea de comandos, como estudiantes, administradores de sistemas,
desarrolladores de software, ingenieros de fiabilidad de sitios, ingenieros
de pruebas y entusiastas de Linux en general. Los usuarios avanzados de
Linux también pueden encontrar material útil, sobre todo si aprendieron
a ejecutar comandos por ensayo y error y quieren reforzar su
comprensión conceptual.
Para sacar el máximo provecho de este libro, ya deberías estar
familiarizado con los siguientes temas (si no lo estás, consulta el Apéndice
A para un rápido repaso):
Crear y editar archivos de texto con un editor de texto
como vim(vi), emacs, nano, o pico
Comandos básicos de gestión de archivos,
como cp (copiar), mv (mover o renombrar),rm (eliminar o borrar),
y chmod (cambiar permisos de archivos).
Comandos básicos de visualización de archivos como cat (ver un
archivo entero) y less (ver una página cada vez).
Comandos básicos de directorio como cd (cambiar de
directorio), ls (listar los archivos de un directorio), mkdir (crear
directorio), rmdir (eliminar directorio) y pwd (mostrar el nombre
del directorio actual).
Los fundamentos de los shell scripts: almacenar comandos
Linux en un archivo, hacer ejecutable el archivo (con chmod
755 o chmod +x), y ejecutar el archivo
Visualización de la documentación incorporada de Linux,
conocida como páginas de manual, con el
comandoman (ejemplo: man cat muestra documentación sobre el
comando cat)
Convertirte en superusuario con el comando sudo para tener
acceso completo a tu sistema Linux (ejemplo: sudo nano
/etc/hosts edita el archivo del sistema/etc/hosts, que está
protegido de los usuarios normales).
Si también conoces las funciones habituales de la línea de comandos,
como la concordancia de patrones para nombres de archivo (con los
símbolos * y ? ), la redirección de entrada/salida (< y >) y las tuberías (|),
habrás empezado con buen pie.
Tu caparazón
Supongo que tu shell de Linux es bash, que es el shell por defecto de la
mayoría de las distribuciones de Linux. Siempre que escribo "el intérprete
de comandos", me refiero abash. La mayoría de las ideas que presento se
aplican también a otros shells, comozsh o dash ; consulta el Apéndice
B para ayudarte a traducir los ejemplos de este libro para otros shells.
Gran parte del material funcionará sin cambios también en el Terminal
Apple Mac, que ejecuta zsh por defecto pero también puede ejecutar bash.3
Convenciones utilizadas en este libro
En este libro se utilizan las siguientes convenciones tipográficas:
Cursiva
Indica nuevos términos, URL, direcciones de correo electrónico,
nombres de archivo y extensiones de archivo.
Constant width
Se utiliza en los listados de programas, así como dentro de los
párrafos para referirse a elementos del programa como nombres de
variables o funciones, bases de datos, tipos de datos, variables de
entorno, sentencias y palabras clave.
Constant width bold
Muestra comandos u otro texto que debe ser tecleado literalmente
por el usuario. También se utiliza ocasionalmente en la salida de
comandos para resaltar texto de interés.
Constant width italic
Muestra el texto que debe sustituirse por valores proporcionados
por el usuario o por valores determinados por el contexto. También
se utiliza para notas breves a la derecha de los listados de código.
Constant width highlighted
Se utiliza en listados de programas complejos para llamar la
atención sobre un texto concreto.
CONSEJO
Este elemento significa un consejo o sugerencia.
NOTA
Este elemento significa una nota general.
ADVERTENCIA
Este elemento indica una advertencia o precaución.
Utilizar ejemplos de código
El material complementario (ejemplos de código, ejercicios, etc.) se puede
descargar en [Link]
Si tienes una pregunta técnica o un problema al utilizar los ejemplos de
código, envía un correo electrónico a bookquestions@[Link].
Este libro está aquí para ayudarte a hacer tu trabajo. En general, si se
ofrece código de ejemplo con este libro, puedes utilizarlo en tus
programas y documentación. No es necesario que te pongas en contacto
con nosotros para pedirnos permiso, a menos que estés reproduciendo
una parte importante del código. Por ejemplo, escribir un programa que
utilice varios trozos de código de este libro no requiere permiso. Vender o
distribuir ejemplos de los libros de O'Reilly sí requiere permiso.
Responder a una pregunta citando este libro y el código de ejemplo no
requiere permiso. Incorporar una cantidad significativa de código de
ejemplo de este libro en la documentación de tu producto sí
requierepermiso.
Agradecemos la atribución, pero en general no la exigimos. Una
atribución suele incluir el título, el autor, la editorial y el ISBN. Por
ejemplo "Linux eficiente en la línea de comandos por Daniel J. Barrett
(O'Reilly). Copyright 2022 Daniel Barrett, 978-1-098-11340-7".
Si crees que el uso que haces de los ejemplos de código no se ajusta al uso
legítimo o al permiso concedido anteriormente, no dudes en ponerte en
contacto con nosotros en permissions@[Link].
Aprendizaje en línea O'Reilly
NOTA
Durante más de 40 años, O'Reilly Media ha proporcionado formación
tecnológica y empresarial, conocimientos y perspectivas para ayudar a las
empresas a alcanzar el éxito.
Nuestra red única de expertos e innovadores comparten sus
conocimientos y experiencia a través de libros, artículos y nuestra
plataforma de aprendizaje online. La plataforma de aprendizaje en línea
de O'Reilly te ofrece acceso bajo demanda a cursos de formación en
directo, rutas de aprendizaje en profundidad, entornos de codificación
interactivos y una amplia colección de textos y vídeos de O'Reilly y de más
de 200 editoriales. Para más información, visita [Link]
Cómo contactar con nosotros
Dirige tus comentarios y preguntas sobre este libro a la editorial:
O'Reilly Media, Inc.
1005 Gravenstein Highway Norte
Sebastopol, CA 95472
800-998-9938 (en Estados Unidos o Canadá)
707-829-0515 (internacional o local)
707-829-0104 (fax)
Tenemos una página web para este libro, donde se enumeran erratas,
ejemplos y cualquier información adicional. Puedes acceder a esta página
en [Link]
Envía un correo electrónico a bookquestions@[Link] para
comentar o hacer preguntas técnicas sobre este libro.
Para obtener noticias e información sobre nuestros libros y cursos,
visita [Link]
Encuéntranos en Facebook: [Link]
Síguenos en Twitter: [Link]
Míranos en YouTube: [Link]
Agradecimientos
Ha sido un placer escribir este libro. Gracias a la increíble gente de
O'Reilly, especialmente a los editores Virginia Wilson y John Devins, a los
editores de producción Caitlin Ghegan y Gregory Hyman, a la gestora de
contenidos Kristen Brown, a la correctora Kim Wimpsett, a la editora del
índice Sue Klefstad y al siempre útil equipo de herramientas. También
estoy muy agradecido a los revisores técnicos del libro, Paul Bayer, John
Bonesio, Dan Ritter y Carla Schroder, por muchos comentarios y críticas
perspicaces. Gracias también al Grupo de Usuarios Linux de Boston por
sus sugerencias sobre el título. Un agradecimiento especial a Maggie
Johnson de Google por su amable permiso para escribir el libro.
Me gustaría ofrecer mi más profundo agradecimiento a Chip Andrews,
Matthew Diaz y Robert Strandh, que fueron compañeros de estudios en la
Universidad Johns Hopkins hace 35 años. Se dieron cuenta de mi nuevo y
creciente interés por Unix y, para mi total sorpresa, me recomendaron
que el Departamento de Informática me contratara como su próximo
administrador de sistemas. Su pequeño acto de fe cambió la trayectoria de
mi vida. (Gracias también a los creadores y mantenedores de Linux, GNU
Emacs, Git, AsciiDoc y muchas otrasherramientas de código abierto:
sin estas personas inteligentes y generosas, mi carrera habría sido
realmente muy distinta.
Como siempre, gracias a mi maravillosa familia, Lisa y Sophia, por su
amor y paciencia.
1 Este libro muestra el prompt de Linux como un signo de dólar. Tu
indicador puede ser diferente.
2 Conocerás la finalidad de este mandato misterioso en el capítulo 8.
3 La versión de bash en macOS es antigua y le faltan funciones
importantes. Para actualizar bash, consulta el artículo "Actualizar Bash en
macOS" de Daniel Weibel.
Parte I. Conceptos básicos
Los cuatro primeros capítulos pretenden aumentar tu eficacia
rápidamente, abarcando conceptos y técnicas que te serán útiles de
inmediato. Aprenderás a combinar comandos con tuberías, a comprender
las responsabilidades del shell de Linux, a recordar y editar rápidamente
comandos del pasado y a navegar por el sistema de archivos de Linux con
gran velocidad.
Capítulo 1. Combinar comandos
Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y
comentarios: translation-feedback@[Link]
Cuando trabajas en Windows, macOS y la mayoría de los demás sistemas
operativos, probablemente pasas el tiempo ejecutando aplicaciones como
navegadores web, procesadores de texto, hojas de cálculo y juegos. Una
aplicación típica está repleta de funciones: todo lo que los diseñadores
pensaron que necesitarían sus usuarios. Por tanto, la mayoría de las
aplicaciones son autosuficientes. No dependen de otras aplicaciones.
Puede que copies y pegues entre aplicaciones de vez en cuando, pero en
su mayor parte son independientes.
La línea de comandos de Linux es diferente. En lugar de grandes
aplicaciones con toneladas de funciones, Linux proporciona miles de
pequeños comandos con muy pocas funciones. El comando cat , por
ejemplo, imprime archivos en la pantalla y eso es todo. ls enumera los
archivos de un directorio, mv renombra archivos, y así sucesivamente.
Cada comando tiene una finalidad sencilla y bastante bien definida.
¿Y si necesitas hacer algo más complicado? No te preocupes. Linux facilita
la combinación de comandos para que sus funciones individuales
trabajen juntas para lograr tu objetivo. Esta forma de trabajar produce
una mentalidad muy diferente sobre la informática. En lugar de
preguntarte "¿Qué aplicación debo ejecutar?" para conseguir algún
resultado, la pregunta pasa a ser "¿Qué comandos debo combinar?".
En este capítulo aprenderás a organizar y ejecutar comandos en distintas
combinaciones para hacer lo que necesitas. Para simplificar las cosas, te
presentaré sólo seis comandos de Linux y sus usos más básicos, para que
puedas centrarte en la parte más compleja e interesante -combinarlos- sin
una enorme curva de aprendizaje. Es un poco como aprender a cocinar
con seis ingredientes, o aprender carpintería con sólo un martillo y una
sierra. (En elCapítulo 5 añadiré más comandos a tu caja de herramientas
de Linux).
Combinarás comandos utilizando tuberías, una función de Linux que
conecta la salida de un comando con la entrada de otro. Cuando
introduzca cada comando (wc, head, cut, grep, sort, y uniq), demostraré
inmediatamente su uso con tuberías. Algunosejemplos serán prácticos
para el uso diario de Linux, mientras que otros son sólo ejemplos de
juguete para demostrar una característica importante.
Entrada, salida y tuberías
La mayoría de los comandos de Linux leen la entrada del teclado,
escriben la salida en la pantalla, o ambas cosas. Linux tiene nombres
elegantes para esta lectura y escritura:
stdin (se pronuncia "entrada estándar" o "standard in")
El flujo de entrada que Linux lee de tu teclado. Cuando escribes
cualquier comando en un prompt, estás suministrando datos en
stdin.
stdout (pronunciado "salida estándar" o "salida estándar")
El flujo de salida que Linux escribe en tu pantalla. Cuando ejecutas
el comandols para imprimir nombres de archivos, los resultados
aparecen en stdout.
Ahora viene la parte guay. Puedes conectar la salida estándar de un
comando a la entrada estándar de otro, de modo que el primer comando
alimente al segundo. Empecemos con el conocido comando ls -l para
listar un directorio grande, como /bin, en formato largo:
$ ls -l /bin
total 12104
-rwxr-xr-x 1 root root 1113504 Jun 6 2019 bash
-rwxr-xr-x 1 root root 170456 Sep 21 2019 bsd-csh
-rwxr-xr-x 1 root root 34888 Jul 4 2019 bunzip2
-rwxr-xr-x 1 root root 2062296 Sep 18 2020 busybox
⋮
-rwxr-xr-x 1 root root 34888 Jul 4 2019 bzcat
-rwxr-xr-x 1 root root 5047 Apr 27 2017 znew
Este directorio contiene muchos más archivos que líneas tiene tu pantalla,
por lo que la salida se desplaza rápidamente fuera de la pantalla. Es una
pena que ls no pueda imprimir la información pantalla a pantalla,
haciendo una pausa hasta que pulses una tecla para continuar. Pero
espera: otro comando de Linux tiene esa función. El
comando less muestra un archivo de pantalla en pantalla:
$ less myfile View the file; press
q to quit
Puedes conectar estos dos comandos porque ls escribe en stdout
yless puede leer de stdin. Utiliza una tubería para enviar la salida de ls a
la entrada de less:
$ ls -l /bin | less
Este comando combinado muestra el contenido del directorio pantalla a
pantalla. La barra vertical (|)entre los comandos es el símbolo de tubería
de Linux.1 Conecta la salida estándar del primer comando con la entrada
estándar del siguiente. Cualquier línea de comandos que contenga
tuberías se denomina tubería.
Por lo general, los comandos no son conscientes de que forman parte de
una canalización. ls cree que está escribiendo en la pantalla, cuando en
realidad su salida ha sido redirigida a less. Y less cree que está leyendo
del teclado, cuando en realidad está leyendo la salida de ls.
¿QUÉ ES UN MANDO?
La palabra comando tiene tres significados diferentes en Linux, que se
muestran enla Figura 1-1:
Un programa
Un programa ejecutable llamado y ejecutado por una sola palabra,
comols, o una función similar integrada en el intérprete de
comandos, como cd (llamado shell builtin)2
Un simple comando
Un nombre de programa (o shell builtin) opcionalmente seguido de
argumentos, comols -l /bin
Un mando combinado
Varios comandos sencillos tratados como una unidad, como la
tubería ls -l /bin | less
FIGURA 1-1. LOS PROGRAMAS, LOS COMANDOS SIMPLES Y LOS COMANDOS COMBINADOS SE
DENOMINAN "COMANDOS".
En este libro, utilizaré la palabra mando de todas estas formas.
Normalmente, el contexto aclarará a cuál me refiero, pero si no es así,
utilizaré uno de los términos más específicos.
Seis comandos para empezar
Las tuberías son una parte esencial de la pericia en Linux. Vamos a
sumergirnos en el desarrollo de tus habilidades con las tuberías con un
pequeño conjunto de comandos Linux, de modo que no importa con
cuáles te encuentres más adelante, estarás preparado para combinarlos.
Los seis comandos -wc, head, cut, grep, sort y uniq- tienen numerosas
opciones y modos de funcionamiento que omitiré en gran medida por
ahora para centrarme en las tuberías. Para saber más sobre cualquier
comando, ejecuta el comando man para ver la documentación completa.
Porejemplo:
$ man wc
Para demostrar nuestros seis comandos en acción, utilizaré un archivo
[Link] que enumera cierta información de los libros de
O'Reilly, mostrado enel Ejemplo 1-1.
Ejemplo 1-1. Dentro del archivo [Link]
python Programming Python 2010 Lutz, Mark
snail SSH, The Secure Shell 2005 Barrett, Daniel
alpaca Intermediate Perl 2012 Schwartz, Randal
robin MySQL High Availability 2014 Bell, Charles
horse Linux in a Nutshell2009 Siever, Ellen
donkey Cisco IOS in a Nutshell 2005 Boney, James
oryx Writing Word Macros1999 Roman, Steven
Cada línea contiene cuatro datos sobre un libro de O'Reilly, separados por
un único carácter de tabulación: el animal de la portada, el título del libro,
el año de publicación y el nombre del primer autor.
Comando nº 1: wc
El comando wc imprime el número de líneas, palabras y caracteres de un
archivo:
$ wc [Link]
7 51 325 [Link]
wc informa de que el archivo [Link] tiene 7 líneas, 51 palabras y 325
caracteres. Si cuentas los caracteres a ojo, incluyendo espacios y
tabuladores, encontrarás sólo 318 caracteres, pero wc también incluye el
carácter invisible de nueva línea que termina cada línea.
Las opciones -l, -w , y -c indican a wc que imprima sólo el número de
líneas, palabras y caracteres, respectivamente:
$ wc -l [Link]
7 [Link]
$ wc -w [Link]
51 [Link]
$ wc -c [Link]
325 [Link]
Contar es una tarea tan útil y de uso general que los autores
dewc diseñaron el comando para que funcionara con tuberías. Lee de la
entrada estándar si omites el nombre del archivo, y escribe en la salida
estándar. Utilicemos ls para listar el contenido del directorio actual y
canalicémoslo a wc para contar las líneas. Esta tubería responde a la
pregunta: "¿Cuántos archivos hay visibles en mi directorio actual?".
$ ls -1
[Link]
myfile
myfile2
[Link]
$ ls -1 | wc -l
4
La opción -1, que indica a ls que imprima sus resultados en una sola
columna, no es estrictamente necesaria aquí. Para saber por qué la he
utilizado, consulta la barra lateral "ls cambia su comportamiento cuando
se redirige".
wc es el primer comando que has visto en este capítulo, así que estás un
poco limitado en lo que puedes hacer con las tuberías. Sólo por diversión,
canaliza la salida de wc hacia sí misma, demostrando que el mismo
comando puede aparecer más de una vez en una canalización. Este
comando combinado informa de que el número de palabras en la salida
de wc es cuatro: tres enteros y unnombre de archivo:
$ wc [Link]
7 51 325 [Link]
$ wc [Link] | wc -w
4
¿Por qué parar ahí? Añade un tercer wc a la tubería y cuenta líneas,
palabras y caracteres en la salida "4":
$ wc [Link] | wc -w | wc
1 1 2
La salida indica una línea (que contiene el número 4), una palabra (el
propio número 4) y dos caracteres. ¿Por qué dos? Porque la línea "4"
termina con un carácter de nueva línea invisible.
Ya está bien de pipelines tontos con wc. A medida que adquieras más
comandos, las canalizaciones serán más prácticas.
LS CAMBIA SU COMPORTAMIENTO CUANDO ES REDIRIGIDO
A diferencia de prácticamente todos los demás comandos de Linux, ls es
consciente de si stdout es la pantalla o si ha sido redirigida (a una tubería
o de otro modo). La razón es la facilidad de uso. Cuando stdout es la
pantalla, ls organiza su salida en varias columnas para facilitar la lectura:
$ ls /bin
bash dir kmod networkctl red
tar
bsd-csh dmesg less nisdomainname rm
⋮
tempfile
Sin embargo, cuando se redirige stdout, ls produce una sola columna. Lo
demostraré canalizando la salida de ls a un comando que simplemente
reproduzca su entrada, como cat:3
$ ls /bin | cat
bash
bsd-csh
bunzip2
⋮
busybox
Este comportamiento puede dar lugar a resultados de aspecto extraño,
como en el ejemplo siguiente:
$ ls
[Link] myfile myfile2 [Link]
$ ls | wc -l
4
El primer comando ls imprime todos los nombres de archivo en una
línea, pero el segundo comando informa de que ls produjo cuatro líneas.
Si no conoces el peculiar comportamiento de ls, puede que esta
discrepancia te resulte confusa.
ls tiene opciones para anular su comportamiento por defecto. Fuerza
a lsa imprimir una sola columna con la opción -1, o fuerza a imprimir
varias columnas con la opción -C.
Comando nº 2: cabeza
El comando head imprime las primeras líneas de un archivo. Imprime las
tres primeras líneas [Link] con headutilizando la opción -n:
$ head -n3 [Link]
python Programming Python 2010 Lutz, Mark
snail SSH, The Secure Shell 2005 Barrett, Daniel
alpaca Intermediate Perl 2012 Schwartz, Randal
Si solicitas más líneas de las que contiene el archivo, head imprime todo el
archivo (como hace cat ). Si omites la opción -n, head imprime por defecto
10 líneas (-n10).
Por sí solo, head es útil para echar un vistazo a la parte superior de un
archivo cuando no te interesa el resto del contenido. Es un comando
rápido y eficaz, incluso para archivos muy grandes, porque no necesita
leer todo el archivo. Además, head escribe en stdout, lo que lo hace útil en
pipelines. Cuenta el número de palabras de las tres primeras líneas
de [Link]:
$ head -n3 [Link] | wc -w
20
head también puede leer desde stdin para divertirse más. Un uso habitual
es reducir la salida de otro comando cuando no te interesa verlo todo,
como un largo listado de directorios. Por ejemplo, lista los cinco primeros
nombres de archivo del directorio /bin:
$ ls /bin | head -n5
bash
bsd-csh
bunzip2
busybox
bzcat
Comando nº 3: cortar
El comando cut imprime una o varias columnas de un archivo. Por
ejemplo, imprime todos los títulos de libros de [Link], que aparecen
en la segunda columna:
$ cut -f2 [Link]
Programming Python
SSH, The Secure Shell
Intermediate Perl
MySQL High Availability
Linux in a Nutshell
Cisco IOS in a Nutshell
Writing Word Macros
cut proporciona dos formas de definir lo que es una "columna". La
primera es cortar por campo (-f), cuando la entrada consiste en cadenas
(campos) separadas cada una por un único carácter de tabulación.
Convenientemente, ése es exactamente el formato del
archivo [Link]. El comandocut anterior imprime el segundo campo
de cada línea, gracias a la opción -f2.
Para acortar la salida, envíala a head para imprimir sólo las tres primeras
líneas:
$ cut -f2 [Link] | head -n3
Programming Python
SSH, The Secure Shell
Intermediate Perl
También puedes cortar varios campos, separando sus números de campo
concomas:
$ cut -f1,3 [Link] | head -n3
python 2010
snail 2005
alpaca 2012
o por rango numérico:
$ cut -f2-4 [Link] | head -n3
Programming Python 2010 Lutz, Mark
SSH, The Secure Shell 2005 Barrett, Daniel
Intermediate Perl 2012 Schwartz, Randal
La segunda forma de definir una "columna" para cut es por posición de
carácter, utilizando la opción -c. Imprime los tres primeros caracteres de
cada línea del archivo, que puedes especificar con comas ( 1,2,3) o como
un rango (1-3):
$ cut -c1-3 [Link]
pyt
sna
alp
rob
hor
don
ory
Ahora que has visto la funcionalidad básica, prueba algo más práctico
con cut y tuberías. Imagina que el archivo [Link] tiene miles de
líneas y necesitas extraer sólo los apellidos de los autores. Primero, aísla
el cuarto campo, nombre del autor:
$ cut -f4 [Link]
Lutz, Mark
Barrett, Daniel
⋮
Schwartz, Randal
A continuación, vuelve a enviar los resultados a cut, utilizando la opción -
d (que significa "delimitador") para cambiar el carácter separador por una
coma en lugar de un tabulador, para aislar los apellidos de los autores:
$ cut -f4 [Link] | cut -d, -f1
Lutz
Barrett
⋮
Schwartz
AHORRA TIEMPO CON EL HISTORIAL DE COMANDOS Y LA EDICIÓN
¿Reescribes muchos comandos? En su lugar, pulsa repetidamente la tecla de
flecha arriba para desplazarte por los comandos que hayas ejecutado antes. (Esta
función del shell se denomina historial de comandos.) Cuando llegues al
comando deseado, pulsa Intro para ejecutarlo inmediatamente, o edítalo
primero utilizando las teclas de flecha izquierda y derecha para colocar el cursor
y la tecla Retroceso para borrar. (Esta función es la edición de la línea de
comandos.)
Hablaré de funciones mucho más potentes para el historial de comandos y la
edición en el Capítulo 3.
Comando nº 4: grep
grep es un comando extremadamente potente, pero por ahora ocultaré la
mayoría de sus capacidades y diré que imprime las líneas que coinciden
con una cadena dada. (Encontrarás más detalles en el Capítulo 5.) Por
ejemplo, el siguiente comando muestra las líneas de [Link] que
contienen la cadena Nutshell:
$ grep Nutshell [Link]
horse Linux in a Nutshell2009 Siever, Ellen
donkey Cisco IOS in a Nutshell 2005 Boney, James
También puedes imprimir las líneas que no coincidan con una cadena
determinada, con la opción-v . Observa que las líneas que contienen
"Nutshell" están ausentes:
$ grep -v Nutshell [Link]
python Programming Python 2010 Lutz, Mark
snail SSH, The Secure Shell 2005 Barrett, Daniel
alpaca Intermediate Perl 2012 Schwartz, Randal
robin MySQL High Availability 2014 Bell, Charles
oryx Writing Word Macros1999 Roman, Steven
En general, grep es útil para encontrar texto en una colección de archivos.
El siguiente comando imprime las líneas que contienen la cadena Perl en
archivos cuyos nombres terminan en .txt:
$ grep Perl *.txt
[Link]:alpaca Intermediate Perl 2012
Schwartz, Randal
[Link]:really love the Perl programming language, which
is
[Link]:languages such as Perl, Python, PHP, and Ruby
En este caso, grep encontró tres líneas coincidentes, una en [Link]
dos en [Link].
grep lee stdin y escribe stdout, por lo que es ideal para pipelines.
Supongamos que quieres saber cuántos subdirectorios hay en el gran
directorio /usr/lib. No existe un único comando Linux que proporcione
esa respuesta, así que construye una canalización. Empieza con el
comando ls -l:
$ ls -l /usr/lib
drwxrwxr-x 12 root root 4096 Mar 1 2020 4kstogram
drwxr-xr-x 3 root root 4096 Nov 30 2020
GraphicsMagick-1.4
drwxr-xr-x 4 root root 4096 Mar 19 2020
NetworkManager
-rw-r--r-- 1 root root 35568 Dec 1 2017
attica_kde.so
-rwxr-xr-x 1 root root 684 May 5 2018 cnf-update-
⋮
db
Observa que ls -l marca los directorios con un d al principio de la línea.
Utiliza cut para aislar la primera columna, que puede ser o no un d:
$ ls -l /usr/lib | cut -c1
d
d
d
-
⋮
-
A continuación, utiliza grep para conservar sólo las líneas que
contengan d:
$ ls -l /usr/lib | cut -c1 | grep d
d
d
⋮
d
Por último, cuenta las líneas con wc, y tendrás tu respuesta, producida por
un pipeline de cuatro comandos: /usr/lib contiene 145 subdirectorios:
$ ls -l /usr/lib | cut -c1 | grep d | wc -l
145
Comando nº 5: ordenar
El comando sort reordena las líneas de un archivo en orden ascendente
(por defecto):
$ sort [Link]
alpaca Intermediate Perl 2012 Schwartz, Randal
donkey Cisco IOS in a Nutshell 2005 Boney, James
horse Linux in a Nutshell2009 Siever, Ellen
oryx Writing Word Macros1999 Roman, Steven
python Programming Python 2010 Lutz, Mark
robin MySQL High Availability 2014 Bell, Charles
snail SSH, The Secure Shell 2005 Barrett, Daniel
u orden descendente (con la opción -r ):
$ sort -r [Link]
snail SSH, The Secure Shell 2005 Barrett, Daniel
robin MySQL High Availability 2014 Bell, Charles
python Programming Python 2010 Lutz, Mark
oryx Writing Word Macros1999 Roman, Steven
horse Linux in a Nutshell2009 Siever, Ellen
donkey Cisco IOS in a Nutshell 2005 Boney, James
alpaca Intermediate Perl 2012 Schwartz, Randal
sort puede ordenar las líneas alfabéticamente (por defecto) o
numéricamente (con la opción -n ). Lo demostraré con líneas que recortan
el tercer campo de [Link], el año de publicación:
$ cut -f3 [Link] Unsorted
2010
2005
2012
2014
2009
2005
1999
$ cut -f3 [Link] | sort -n Ascending
1999
2005
2005
2009
2010
2012
2014
$ cut -f3 [Link] | sort -nr Descending
2014
2012
2010
2009
2005
2005
1999
Para saber el año del libro más reciente en [Link], canaliza la
salida de sort a la entrada de head e imprime sólo la primera línea:
$ cut -f3 [Link] | sort -nr | head -n1
2014
VALORES MÁXIMOS Y MÍNIMOS
sort y head son potentes compañeros cuando se trabaja con datos numéricos,
un valor por línea. Puedes imprimir el valor máximo canalizando los datos a:
... | sort -nr | head -n1
e imprime el valor mínimo con:
... | sort -n | head -n1
Como otro ejemplo, vamos a jugar con el archivo /etc/passwd, que
enumera los usuarios que pueden ejecutar procesos en el
sistema.4 Generará una lista de todos los usuarios por orden alfabético. Si
echas un vistazo a las cinco primeras líneas, verás algo como esto
$ head -n5 /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
smith:x:1000:1000:Aisha Smith,,,:/home/smith:/bin/bash
jones:x:1001:1001:Bilbo Jones,,,:/home/jones:/bin/bash
Cada línea está formada por cadenas separadas por dos puntos, y la
primera cadena es el nombre de usuario, por lo que puedes aislar los
nombres de usuario con el comandocut:
$ head -n5 /etc/passwd | cut -d: -f1
root
daemon
bin
smith
jones
y clasifícalos:
$ head -n5 /etc/passwd | cut -d: -f1 | sort
bin
daemon
jones
root
smith
Para obtener la lista ordenada de todos los nombres de usuario, no sólo de
los cinco primeros, sustituye head por cat:
$ cat /etc/passwd | cut -d: -f1 | sort
Para detectar si un usuario determinado tiene una cuenta en tu sistema,
haz coincidir su nombre de usuario con grep. Una salida vacía significa
que no hay cuenta:
$ cut -d: -f1 /etc/passwd | grep -w jones
jones
$ cut -d: -f1 /etc/passwd | grep -w rutabaga
(produces no output)
La opción -w indica a grep que sólo debe coincidir con palabras completas,
no con palabras parciales, en caso de que tu sistema también tenga un
nombre de usuario que contenga "jones", como sallyjones2.
Comando nº 6: uniq
El comando uniq detecta líneas repetidas y adyacentes en un archivo. Por
defecto, elimina las repeticiones. Lo demostraré con un archivo sencillo
que contiene letras mayúsculas:
$ cat letters
A
A
A
B
B
A
C
C
C
C
$ uniq letters
A
B
A
C
Observa que uniq redujo las tres primeras líneas A a una sola A, pero dejó
la última A en su sitio porque no era adyacente a las tres primeras.
También puedes contar las ocurrencias con la opción -c:
$ uniq -c letters
3 A
2 B
1 A
4 C
Lo admito, cuando me encontré por primera vez con el comando uniq, no
le vi mucha utilidad, pero rápidamente se convirtió en uno de mis
favoritos. Supón que tienes un archivo separado por tabuladores con las
calificaciones finales de los alumnos de un curso universitario, que van
desde A (la mejor) hasta F (la peor):
$ cat grades
C Geraldine
B Carmine
A Kayla
A Sophia
B Haresh
C Liam
B Elijah
B Emma
A Olivia
D Noah
F Ava
Te gustaría imprimir la calificación con más apariciones. (Si hay empate,
imprime sólo una de las ganadoras.) Comienza aislando las calificaciones
con cut y ordenándolas:
$ cut -f1 grades | sort
A
A
A
B
B
B
B
C
C
D
F
A continuación, utiliza uniq para contar las líneas adyacentes:
$ cut -f1 grades | sort | uniq -c
3 A
4 B
2 C
1 D
1 F
A continuación, ordena las líneas en orden inverso, numéricamente, para
desplazar el grado más frecuente a la línea superior:
$ cut -f1 grades | sort | uniq -c | sort -nr
4 B
3 A
2 C
1 F
1 D
y mantener sólo la primera línea con head:
$ cut -f1 grades | sort | uniq -c | sort -nr | head -n1
4 B
Por último, como sólo quieres la letra de la nota, no el recuento, aísla la
nota con cut:
$ cut -f1 grades | sort | uniq -c | sort -nr | head -n1 |
cut -c9
B
y ahí tienes la respuesta, gracias a una tubería de seis comandos, la más
larga hasta ahora. Este tipo de construcción de canalizaciones paso a paso
no es sólo un ejercicio educativo. Es cómo trabajan realmente los expertos
en Linux. El Capítulo 8 está dedicado a esta técnica.
Detectar archivos duplicados
Combinemos lo que has aprendido con un ejemplo más amplio. Supón
que estás en un directorio lleno de archivos JPEG y quieres saber si alguno
está duplicado:
$ ls
[Link] [Link] [Link] [Link]
[Link]
[Link] [Link] [Link] [Link]
⋮
[Link]
Puedes responder a esta pregunta con un pipeline. Necesitarás otro
comando, md5sum, que examina el contenido de un archivo y calcula una
cadena de 32 caracteres llamada unasuma de comprobación:
$ md5sum [Link]
146b163929b6533f02e91bdf21cb9563 [Link]
Por razones matemáticas, es muy probable que la suma de comprobación
de un archivo sea única. Por tanto, si dos archivos tienen la misma suma
de comprobación, es casi seguro que sean duplicados. Aquí, md5sum indica
que el primer y el tercer fichero son duplicados:
$ md5sum [Link] [Link] [Link]
146b163929b6533f02e91bdf21cb9563 [Link]
63da88b3ddde0843c94269638dfa6958 [Link]
146b163929b6533f02e91bdf21cb9563 [Link]
Las sumas de comprobación duplicadas son fáciles de detectar a simple
vista cuando sólo hay tres archivos, pero ¿y si tienes tres mil? Son las
tuberías al rescate. Calcula todas las sumas de comprobación,
utiliza cut para aislar los 32 primeros caracteres de cada línea y ordena
las líneas para que los duplicados sean adyacentes:
$ md5sum *.jpg | cut -c1-32 | sort
1258012d57050ef6005739d0e6f6a257
146b163929b6533f02e91bdf21cb9563
146b163929b6533f02e91bdf21cb9563
⋮
17f339ed03733f402f74cf386209aeb3
Ahora añade uniq para contar las líneas repetidas:
$ md5sum *.jpg | cut -c1-32 | sort | uniq -c
1 1258012d57050ef6005739d0e6f6a257
2 146b163929b6533f02e91bdf21cb9563
⋮
1 17f339ed03733f402f74cf386209aeb3
Si no hay duplicados, todos los recuentos producidos por uniq serán 1.
Ordena los resultados numéricamente de mayor a menor, y cualquier
recuento mayor que 1 aparecerá en la parte superior de la salida:
$ md5sum *.jpg | cut -c1-32 | sort | uniq -c | sort -nr
3 f6464ed766daca87ba407aede21c8fcc
2 c7978522c58425f6af3f095ef1de1cd5
2 146b163929b6533f02e91bdf21cb9563
⋮
1 d8ad913044a51408ec1ed8a204ea9502
Ahora vamos a eliminar los no duplicados. Sus sumas de comprobación
van precedidas de seis espacios, el número uno y un solo espacio.
Utilizaremos grep -v para eliminar estas líneas:5
$ md5sum *.jpg | cut -c1-32 | sort | uniq -c | sort -nr |
grep -v " 1 "
3 f6464ed766daca87ba407aede21c8fcc
2 c7978522c58425f6af3f095ef1de1cd5
2 146b163929b6533f02e91bdf21cb9563
Por último, tienes tu lista de sumas de comprobación duplicadas,
ordenadas por el número de ocurrencias, producidas por una hermosa
canalización de seis comandos. Si no produce ningún resultado, no hay
archivos duplicados.
Este comando sería aún más útil si mostrara los nombres de los archivos
duplicados, pero esa operación requiere funciones que aún no hemos
discutido. (Las aprenderás en "Mejorar el detector de archivos
duplicados".) Por ahora, identifica los archivos que tengan una suma de
comprobación determinada buscando con grep:
$ md5sum *.jpg | grep 146b163929b6533f02e91bdf21cb9563
146b163929b6533f02e91bdf21cb9563 [Link]
146b163929b6533f02e91bdf21cb9563 [Link]
y limpiando la salida con cut:
$ md5sum *.jpg | grep 146b163929b6533f02e91bdf21cb9563 |
cut -c35-
[Link]
[Link]
Resumen
Ya has visto el poder de stdin, stdout y las tuberías. Convierten un
pequeño puñado de comandos en una colección de herramientas
componibles, demostrando que el todo es mayor que la suma de las
partes. Cualquiercomando que lea stdin o escriba stdout puede participar
en tuberías.6 A medida que aprendas más comandos, podrás aplicar los
conceptos generales de este capítulo para forjar tus propias y potentes
combinaciones.
1 En los teclados estadounidenses, el símbolo de la pipa está en la misma
tecla que la barra invertida (\), normalmente situada entre las teclas Intro
y Retroceso o entre la tecla Mayúsculas izquierda y la Z.
2 El estándar POSIX llama a esta forma de comando una utilidad.
3 Dependiendo de tu configuración, ls también puede utilizar otras
funciones de formato, como el color, al imprimir en pantalla pero no al
redirigir.
4 Algunos sistemas Linux almacenan la información del usuario en otro
lugar.
5 Técnicamente, no necesitas el sort -nr final de esta cadena para aislar
los duplicados, porque grep elimina todos los no duplicados.
6Algunos comandos no utilizan stdin/stdout y, por tanto, no pueden leer
de las tuberías ni escribir en ellas. Algunos ejemplos son mv y rm. Sin
embargo, las tuberías pueden incorporar estos comandos de otras formas;
verás ejemplos en el Capítulo 8.
Capítulo 2. Presentación del Shell
Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y
comentarios: translation-feedback@[Link]
Por tanto, puedes ejecutar comandos en un prompt. Pero, ¿qué es ese
prompt? ¿De dónde viene, cómo se ejecutan tus órdenes y por qué
importa?
Ese pequeño prompt es producido por un programa llamado shell. Es una
interfaz de usuario que se sitúa entre tú y el sistema operativo Linux.
Linux proporciona varias shell, y la más común (y la estándar de este
libro) se llama bash . (Consulta el Apéndice B para ver notas sobre otras
shell).
bash y otros shells hacen mucho más que simplemente ejecutar comandos.
Por ejemplo, cuando un comando incluye un comodín (*) para referirse a
varios archivos a la vez:
$ ls *.py
[Link] [Link] user_interface.py
el comodín es manejado enteramente por el intérprete de comandos, no
por el programals. El intérprete de comandos evalúa la expresión *.py y
la sustituye de forma invisible por una lista de nombres de archivo
coincidentes antes de que se ejecute ls. En otras palabras, ls nunca ve
el comodín. Desde la perspectiva dels, has escrito el siguiente comando:
$ ls [Link] [Link] user_interface.py
El shell también gestiona las tuberías que viste en el Capítulo 1.
Redirige stdin y stdout de forma transparente para que los programas
implicados no tengan ni idea de que se están comunicando entre sí.
Cada vez que se ejecuta un comando, algunos pasos son
responsabilidad del programa invocado, como ls, y otros son
responsabilidad del shell. Los usuarios expertos entienden cuál es cuál.
Esa es una de las razones por las que pueden crear comandos largos y
complejos de la nada y ejecutarlos con éxito. Ya saben lo que hará el
comando antes de pulsar Intro, en parte porque comprenden la
separación entre el shell y los programas que invoca.
En este capítulo, iniciaremos tu comprensión del shell de Linux. Adoptaré
el mismo enfoque minimalista que utilicé para los comandos y las
tuberías en el Capítulo 1. En lugar de cubrir docenas de funciones del
intérprete de comandos, te daré sólo la información suficiente para que
pases al siguiente paso de tu viaje de aprendizaje:
Concordancia de patrones para nombres de archivo
Variables para almacenar valores
Redirección de entrada y salida
Comillas y escapes para desactivar ciertas funciones del
intérprete de comandos
La ruta de búsqueda para localizar los programas a ejecutar
Guardar cambios en tu entorno shell
Vocabulario de conchas
La palabra shell tiene dos significados. A veces significa el concepto del
intérprete de comandos de Linux en general, como en "El intérprete de
comandos es una herramienta poderosa" o "bash es un intérprete de
comandos". Otras veces significa una instancia específica de un intérprete
de comandos que se ejecuta en un ordenador Linux determinado, a la
espera de tu próxima orden.
En este libro, el significado de shell debería quedar claro por el contexto
la mayoría de las veces. Cuando sea necesario, me referiré al segundo
significado como unainstancia de shell, un shell en ejecución, o tushell
actual.
Algunas instancias del shell, pero no todas, presentan un prompt para que
puedas interactuar con ellas. Utilizaré el términoshell interactivo para
referirme a estas instancias. Otras instancias del shell no son interactivas:
ejecutan una secuencia de comandos y salen.
Coincidencia de patrones para
nombres de archivo
En el Capítulo 1, trabajaste con varios comandos que aceptan nombres de
archivo como argumentos, como cut, sort ygrep. Estos comandos (y
muchos otros) aceptan varios nombres de archivo como argumentos. Por
ejemplo, puedes buscar la palabra Linux en cien archivos a la vez,
llamados del capítulo1 al capítulo100:
$ grep Linux chapter1 chapter2 chapter3 chapter4 chapter5
...and so on...
Listar varios archivos por su nombre es una tediosa pérdida de tiempo,
por lo que el shell proporciona caracteres especiales como abreviatura
para referirse a archivos o directorios con nombres similares. Mucha
gente llama a estos caracteres comodines, pero el concepto más general se
llama concordancia de patroneso globbing. La concordancia de patrones
es una de las dos técnicas de rapidez más comunes que aprenden los
usuarios de Linux. (La otra es pulsar la tecla de flecha arriba para
recuperar el comando anterior del shell, que describo en el Capítulo 3).
La mayoría de los usuarios de Linux están familiarizados con el carácter
estrella o asterisco (*), que coincide con cualquier secuencia de cero o más
caracteres (excepto un punto inicial o una barra de directorio) 1 en rutas
de archivos o directorios:
$ grep Linux chapter*
Entre bastidores, el intérprete de comandos (¡no grep!) expande el
patrónchapter* en una lista de 100 nombres de archivo coincidentes. A
continuación, el intérprete de comandos ejecutagrep.
Muchos usuarios también han visto el signo de interrogación (?)carácter
especial, que coincide con cualquier carácter simple (excepto un punto
inicial o una barra de directorio). Por ejemplo, podrías buscar la
palabra Linux sólo en los capítulos del 1 al 9, proporcionando un único
signo de interrogación para que el intérprete de comandos coincida con
dígitos sueltos:
$ grep Linux chapter?
o en los capítulos del 10 al 99, con dos signos de interrogación que
coincidan con dos cifras:
$ grep Linux chapter??
Menos usuarios están familiarizados con los corchetes ( []), que solicitan
al shell que busque un único carácter de un conjunto. Por ejemplo,
podrías buscar sólo los cinco primeros capítulos:
$ grep Linux chapter[12345]
De forma equivalente, podrías proporcionar un rango de caracteres con
un guión:
$ grep Linux chapter[1-5]
También puedes buscar capítulos pares, combinando el asterisco y los
corchetes para que el intérprete de comandos coincida con los nombres
de archivo que terminen en un dígito par:
$ grep Linux chapter*[02468]
Cualquier carácter, no sólo dígitos, puede aparecer dentro de los corchetes
para la coincidencia. Por ejemplo, los nombres de archivo que empiecen
con una letra mayúscula, contengan un guión bajo y terminen con un
símbolo @ serían comparados por el intérprete de comandos en este
comando:
$ ls [A-Z]*_*@
TERMINOLOGÍA: EVALUACIÓN DE EXPRESIONES Y EXPANSIÓN DE PATRONES
Las cadenas que introduces en la línea de comandos,
como chapter* oEfficient Linux, se denominan expresiones. Un comando
completo como ls -l chapter* también es una expresión.
Cuando el shell interpreta y maneja los caracteres especiales de una expresión,
como los asteriscos y los símbolos de tubería, decimos que el shellevalúa la
expresión.
La coincidencia de patrones es un tipo de evaluación. Cuando el intérprete de
comandos evalúa una expresión que contiene símbolos de coincidencia de
patrones, como chapter*, y la sustituye por nombres de archivo que coinciden
con el patrón, decimos que el intérprete de comandos expande el patrón.
Los patrones son válidos prácticamente en cualquier lugar en el que
suministres rutas de archivos o directorios en la línea de comandos. Por
ejemplo, puedes listar todos los archivos del directorio /etc cuyos
nombres terminen en .conf utilizando un patrón:
$ ls -1 /etc/*.conf
/etc/[Link]
⋮
/etc/[Link]
/etc/[Link]
Ten cuidado al utilizar un patrón con un comando que sólo acepte un
argumento de archivo o directorio, como cd. Puede que no obtengas el
comportamiento que esperas:
$ ls
Pictures Poems Politics
$ cd P* Three
directories will match
bash: cd: too many arguments
Si un patrón no coincide con ningún archivo, el shell lo deja sin cambios
para pasarlo literalmente como argumento de un comando. En el
siguiente comando, el patrón *.doc no coincide con nada en el directorio
actual, por lo que lsbusca un nombre de archivo llamado
literalmente *.doc y falla:
$ ls *.doc
/bin/ls: cannot access '*.doc': No such file or directory
Cuando trabajes con patrones de archivos, es de vital importancia
recordar dos puntos. El primero, como ya he recalcado, es que el shell, no
el programa invocado, realiza la coincidencia de patrones. Sé que no paro
de repetirlo, pero a menudo me sorprende la cantidad de usuarios de
Linux que no lo saben y desarrollan supersticiones sobre por qué
determinados comandos tienen éxito o fallan.
El segundo punto importante es que la concordancia de patrones del shell
sólo se aplica a las rutas de archivos y directorios. No funciona para
nombres de usuario, nombres de host y otros tipos de argumentos que
aceptan ciertos comandos. Tampoco puedes escribir (digamos) s?
rt alprincipio de la línea de comandos y esperar que el shell ejecute el
programa sort. (Algunos comandos de Linux como grep, sed y awk realizan
sus propias marcas de coincidencia de patrones, que exploraremos en el
Capítulo 5).
COINCIDENCIA DE PATRONES DE NOMBRE DE ARCHIVO Y TUS PROPIOS PROGRAMAS
Todos los programas que aceptan nombres de archivo como argumentos
"funcionan" automáticamente con la concordancia de patrones, porque el shell
evalúa los patrones antes de que se ejecute el programa. Esto es así incluso para
los programas y scripts que escribas tú mismo. Por ejemplo, si escribieras un
programa english2swedish que tradujera archivos del inglés al sueco y
aceptara varios nombres de archivo en la línea de comandos, podrías ejecutarlo
instantáneamente con la concordancia de patrones:
$ english2swedish *.txt
Evaluación de variables
Un shell en ejecución puede definir variables y almacenar valores en
ellas. Una variable del shell es muy parecida a una variable en álgebra:
tiene un nombre y un valor. Un ejemplo es la variable de shell HOME. Su
valor es la ruta a tu directorio personal de Linux, como /home/smith. Otro
ejemplo es USER, cuyo valor es tu nombre de usuario de Linux, que
supondré que essmith a lo largo de este libro.
Para imprimir los valores de HOME y USER en stdout, ejecuta el
comando printenv:
$ printenv HOME
/home/smith
$ printenv USER
smith
Cuando el shell evalúa una variable, sustituye el nombre de la variable
por su valor. Basta con colocar el signo de dólar delante del nombre para
evaluar la variable. Por ejemplo, $HOME se evalúa como la
cadena/home/smith.
La forma más sencilla de ver cómo el intérprete de comandos evalúa una
línea de comandos es ejecutar el comando echo , que simplemente
imprime sus argumentos (después de que el intérprete de comandos
termine de evaluarlos):
$ echo My name is $USER and my files are in $HOME
Evaluating variables
My name is smith and my files are in /home/smith
$ echo ch*ter9
Evaluating a pattern
chapter9
De dónde vienen las variables
Las variables como USER y HOME están predefinidas por el shell. Sus valores
se establecen automáticamente cuando te conectas. (Tradicionalmente,
estas variables predefinidas tienen nombres en mayúsculas.
También puedes definir o modificar una variable en cualquier momento
asignándole un valor utilizando esta sintaxis:
name=value
Por ejemplo, si trabajas frecuentemente en el
directorio/home/smith/Proyectos, podrías asignar su nombre a una
variable:
$ work=$HOME/Projects
y utilízalo como atajo práctico con cd:
$ cd $work
$ pwd
/home/smith/Projects
Puedes proporcionar $work a cualquier comando que espere un directorio:
$ cp myfile $work
$ ls $work
myfile
Al definir una variable, no se permiten espacios alrededor del signo igual.
Si lo olvidas, el intérprete de comandos asumirá (erróneamente) que la
primera palabra de la línea de comandos es un programa a ejecutar, y
que el signo igual y el valor son sus argumentos, y verás un mensaje de
error:
$ work = $HOME/Projects The shell assumes
"work" is a command
work: command not found
Una variable definida por el usuario como work es tan legítima y utilizable
como una variable definida por el sistema como HOME. La única diferencia
práctica es que algunos programas Linux cambian su comportamiento
internamente basándose en los valores de HOME, USER, y otras variables
definidas por el sistema. Por ejemplo, un programa Linux con una
interfaz gráfica podría recuperar tu nombre de usuario del intérprete de
comandos y mostrarlo. Tales programas no prestan atención a una
variable inventada como work porque no fueron programados para
hacerlo.
Variables y superstición
Cuando imprimes el valor de una variable con echo:
$ echo $HOME
/home/smith
podrías pensar que el comando echo examina la variable HOME e imprime
su valor. No es así. echo no sabe nada de variables. Lo que ocurre en
realidad es que el shell evalúa $HOME antes de ejecutar echo. Desde la
perspectiva de echo, has escrito
$ echo /home/smith
Es muy importante comprender este comportamiento, sobre todo cuando
nos adentramos en comandos más complicados. El shell evalúa las
variables de una orden -así como patrones y otras construcciones del
shell- antes de ejecutar la orden.
Patrones frente a variables
Pongamos a prueba tu comprensión de la evaluación de patrones y
variables. Supón que estás en un directorio con dos
subdirectorios,mamíferos y reptiles, y que, curiosamente, el
subdirectorio mamíferos contiene archivos
llamados [Link] y [Link]:
$ ls
mammals reptiles
$ ls mammals
[Link] [Link]
En el mundo real, los lagartos y las serpientes no son mamíferos, por lo
que los dos archivos deben trasladarse al subdirectorio reptiles. Aquí
tienes dos propuestas para hacerlo. Una funciona y la otra no:
mv mammals/*.txt reptiles Method 1
FILES="[Link] [Link]"
mv mammals/$FILES reptiles Method 2
El método 1 funciona porque los patrones coinciden con una ruta de
archivo completa. Observa cómo el nombre del
directorio mamíferos forma parte de ambas coincidencias
para mammals/*.txt:
$ echo mammals/*.txt
mammals/[Link] mammals/[Link]
Así, el método 1 funciona como si hubieras escrito el siguiente comando
correcto:
$ mv mammals/[Link] mammals/[Link] reptiles
El método 2 utiliza variables, que sólo se evalúan a su valor literal. No
tienen un tratamiento especial para las rutas de los archivos:
$ echo mammals/$FILES
mammals/[Link] [Link]
Así, el método 2 funciona como si hubieras escrito el siguiente comando
problemático:
$ mv mammals/[Link] [Link] reptiles
Este comando busca el archivo [Link] en el directorio actual, no en
el subdirectorio mamíferos, y falla:
$ mv mammals/$FILES reptiles
/bin/mv: cannot stat '[Link]': No such file or
directory
Para que una variable funcione en esta situación, utiliza un bucle for que
anteponga el nombre del directorio mamíferos a cada nombre de
archivo:
FILES="[Link] [Link]"
for f in $FILES; do
mv mammals/$f reptiles
done
Acortar comandos con alias
Una variable es un nombre que sustituye a un valor. El shell también
tiene nombres que sustituyen a órdenes. Se llaman alias. Define un alias
inventando un nombre y siguiéndolo de un signo igual y un comando:
$ alias g=grep A command with no arguments
$ alias ll="ls -l" A command with arguments:
quotes are required
Ejecuta un alias escribiendo su nombre como comando. Cuando los alias
son más cortos que los comandos que invocan, ahorras tiempo de tecleo:
$ ll Runs "ls -
l"
-rw-r--r-- 1 smith smith 325 Jul 3 17:44 [Link]
$ g Nutshell [Link] Runs "grep
Nutshell [Link]"
horse Linux in a Nutshell 2009 Siever, Ellen
donkey Cisco IOS in a Nutshell 2005 Boney, James
CONSEJO
Define siempre un alias en su propia línea, no como parte de un comando
combinado. (Consulta man bash para conocer los detalles técnicos).
Puedes definir un alias que tenga el mismo nombre que un comando
existente, sustituyendo de hecho a ese comando en tu shell. A esta
práctica se la denomina hacer sombra al comando. Supongamos que te
gusta el comando less para leer archivos, pero quieres que limpie la
pantalla antes de mostrar cada página. Esta función se activa con la
opción -c , así que define un alias llamado "less" que ejecute less -c:2
$ alias less="less -c"
Los alias tienen precedencia sobre los comandos del mismo nombre, por
lo que ahora has hecho sombra al comando less en el shell actual.
Explicaré lo que significa precedencia en "Ruta de búsqueda y alias".
Para listar los alias de un intérprete de comandos y sus valores,
ejecuta alias sin argumentos:
$ alias
alias g='grep'
alias ll='ls -l'
Para ver el valor de un solo alias, ejecuta alias seguido de su nombre:
$ alias g
alias g='grep'
Para eliminar un alias de un intérprete de comandos, ejecuta unalias:
$ unalias g
Redirigir la entrada y la salida
El shell controla la entrada y la salida de los comandos que ejecuta. Ya has
visto un ejemplo: las tuberías, que dirigen la salida estándar de un
comando a la entrada estándar de otro. La sintaxis de las tuberías,|,es una
característica del shell.
Otra función del shell es redirigir stdout a un archivo. Por ejemplo, si
utilizas grep para imprimir las líneas coincidentes del
archivo [Link] del Ejemplo 1-1, el comando escribe por defecto en
stdout:
$ grep Perl [Link]
alpaca Intermediate Perl 2012 Schwartz, Randal
Puedes enviar esa salida a un archivo en su lugar, utilizando una función
del shell llamada redirección de [Link] añade el
símbolo> seguido del nombre de un archivo para recibir la salida:
$ grep Perl [Link] > outfile
(displays no output)
$ cat outfile
alpaca Intermediate Perl 2012 Schwartz, Randal
Acabas de redirigir stdout al archivo outfile en lugar de a la pantalla. Si el
archivo outfile no existe, se crea. Si existe, la redirección sobrescribe su
contenido. Si prefieres añadir al archivo de salida en lugar de
sobrescribirlo, utiliza en su lugar el símbolo>> :
$ grep Perl [Link] > outfile Create or
overwrite outfile
$ echo There was just one match >> outfile Append to
outfile
$ cat outfile
alpaca Intermediate Perl 2012 Schwartz, Randal
There was just one match
La redirección de salida tiene una compañera, la redirección de
entrada, que redirige la entrada estándar para que proceda de un archivo
en lugar del teclado. Utiliza el símbolo < seguido de un nombre de archivo
para redirigir la entrada estándar.
Muchos comandos de Linux que aceptan nombres de archivos como
argumentos, y leen de esos archivos, también leen de stdin cuando se
ejecutan sin argumentos. Un ejemplo es wc para contar líneas, palabras y
caracteres de un archivo:
$ wc [Link] Reading from a
named file
7 51 325 [Link]
$ wc < [Link] Reading from
redirected stdin
7 51 325
ERROR ESTÁNDAR (STDERR) Y REDIRECCIÓN
En tu uso cotidiano de Linux, puedes notar que algunas salidas no pueden
ser redirigidas por >, como ciertos mensajes de error. Por ejemplo, pídele
acp que copie un archivo que no existe, y producirá este mensaje de error:
$ cp [Link] [Link]
cp: cannot stat '[Link]': No such file or
directory
Si rediriges la salida (stdout) de este comando cp a un archivo,errores, el
mensaje sigue apareciendo en pantalla:
$ cp [Link] [Link] > errors
cp: cannot stat '[Link]': No such file or
directory
y el archivo errores está vacío:
$ cat errors
(produces no output)
¿Por qué ocurre esto? Los comandos de Linux pueden producir más de un
flujo de salida. Además de stdout, también existe stderr (pronunciado
"error estándar" o "standard err"), un segundo flujo de salida que
tradicionalmente se reserva para los mensajes de error. Los flujos stderr y
stdout parecen idénticos en la pantalla, pero internamente están
separados. Puedes redirigir stderr con el símbolo 2> seguido de un
nombre de archivo:
$ cp [Link] [Link] 2> errors
$ cat errors
cp: cannot stat '[Link]': No such file or
directory
y añade stderr a un archivo con 2>> seguido de un nombre de archivo:
$ cp [Link] [Link] 2> errors
$ cp [Link] [Link] 2>> errors
$ cat errors
cp: cannot stat '[Link]': No such file or
directory
cp: cannot stat '[Link]': No such file or directory
Para redirigir tanto stdout como stderr al mismo archivo,
utiliza &> seguido de un nombre de archivo:
$ echo This file exists > [Link]
Create a file
$ cat [Link] [Link] &> [Link]
$ cat [Link]
This file exists
cat: [Link]: No such file or directory
Es muy importante que entiendas en qué se diferencian estos dos
comandos de wc:
En el primer comando, wc recibe el nombre de
archivo [Link] como argumento, por lo que wc sabe que el
archivo existe. wc abre deliberadamente el archivo en el disco y
lee su contenido.
En el segundo comando, wc se invoca sin argumentos, por lo que
lee de stdin, que normalmente es el teclado. Sin embargo, el
intérprete de comandos redirige sigilosamente la entrada
estándar para que proceda de animals. [Link] no tiene ni idea de
que existe el archivo [Link].
El shell puede redirigir la entrada y la salida en el mismo comando:
$ wc < [Link] > count
$ cat count
7 51 325
e incluso pueden utilizar tuberías al mismo tiempo. Aquí, grep lee de la
entrada estándar redirigida y canaliza los resultados a wc, que escribe en
la salida estándar redirigida, produciendo el recuento de archivos:
$ grep Perl < [Link] | wc > count
$ cat count
1 6 47
Profundizarás en estos comandos combinados en el capítulo 8 y verás
muchos otros ejemplos de redirección a lo largo del libro.
Evaluación Desactivadora con Citas y
Escapes
Normalmente, el shell utiliza el espacio en blanco como separador entre
palabras. El siguiente comando tiene cuatro palabras: un nombre de
programa seguido de tres argumentos:
$ ls file1 file2 file3
A veces, sin embargo, necesitas que el shell trate los espacios en blanco
como significativos, no como separadores. Un ejemplo común son los
espacios en blanco en un nombre de archivo como Consejos Eficaces
[Link]:
$ ls -l
-rw-r--r-- 1 smith smith 36 Aug 9 22:12 Efficient Linux
[Link]
Si haces referencia a un nombre de archivo de este tipo en la línea de
comandos, tu comando puede fallar porque el shell trata los caracteres de
espacio como separadores:
$ cat Efficient Linux [Link]
cat: Efficient: No such file or directory
cat: Linux: No such file or directory
cat: [Link]: No such file or directory
Para forzar al shell a tratar los espacios como parte de un nombre de
archivo, tienes tres opciones: comillas simples, comillas dobles y barras
invertidas:
$ cat 'Efficient Linux [Link]'
$ cat "Efficient Linux [Link]"
$ cat Efficient\ Linux\ [Link]
Comillas simples indican al intérprete de comandos que trate literalmente
todos los caracteres de una cadena, aunque normalmente tengan un
significado especial para el intérprete de comandos, como los espacios y
los signos de dólar:
$ echo '$HOME'
$HOME
Las comillas dobles indican al intérprete de comandos que trate todos los
caracteres literalmente, excepto algunos signos de dólar y algunos otros
que aprenderás más adelante:
$ echo "Notice that $HOME is evaluated"
Double quotes
Notice that /home/smith is evaluated
$ echo 'Notice that $HOME is not'
Single quotes
Notice that $HOME is not
Una barra invertida, también llamado carácter de escape, indica al shell
que trate literalmente el siguiente carácter. El siguiente comando incluye
un signo de dólar escapado:
$ echo \$HOME
$HOME
Las barras invertidas actúan como caracteres de escape incluso dentro de
comillas dobles:
$ echo "The value of \$HOME is $HOME"
The value of $HOME is /home/smith
pero no entre comillas simples:
$ echo 'The value of \$HOME is $HOME'
The value of \$HOME is $HOME
Utiliza la barra invertida para escapar de un carácter de comillas dobles
dentro de comillas dobles:
$ echo "This message is \"sort of\" interesting"
This message is "sort of" interesting
Una barra invertida al final de una línea desactiva la naturaleza especial
del carácter de nueva línea invisible, permitiendo que los comandos del
shell abarquen varias líneas:
$ echo "This is a very long message that needs to extend \
onto multiple lines"
This is a very long message that needs to extend onto
multiple lines
Las barras invertidas finales son estupendas para hacer más legibles las
cadenas, como ésta del "Comando nº 6: uniq":
$ cut -f1 grades \
| sort \
| uniq -c \
| sort -nr \
| head -n1 \
| cut -c9
Cuando se utiliza de este modo, la barra invertida se denomina a
vecescarácter de continuación de línea.
Una barra invertida inicial antes de un alias escapa del alias, haciendo
que el intérprete de comandos busque un comando con el mismo nombre,
ignorando cualquier sombra:
$ alias less="less -c" Define an alias
$ less myfile Run the alias, which invokes
less -c
$ \less myfile Run the standard less
command, not the alias
Localización de programas a ejecutar
Cuando el shell encuentra por primera vez un comando simple, como ls
*.py, es sólo una cadena de caracteres sin sentido. Rápido como un rayo,
el shell divide la cadena en dos palabras, "ls" y "*.py". En este caso, la
primera palabra es el nombre de un programa en disco, y el shell debe
localizar el programa para ejecutarlo.
Resulta que el programa ls es un archivo ejecutable en el directorio/bin.
Puedes verificar su ubicación con este comando:
$ ls -l /bin/ls
-rwxr-xr-x 1 root root 133792 Jan 18 2018 /bin/ls
o puedes cambiar de directorio con cd /bin y ejecutar este
encantadorcomando de aspecto críptico:
$ ls ls
ls
que utiliza el comando ls para listar el archivo ejecutable ls.
¿Cómo localiza el shell ls en el directorio /bin? Entre bastidores, el
intérprete de comandos consulta una lista preestablecida de directorios
que guarda en memoria, denominada ruta de búsqueda. La lista se
almacena como valor de la variable del shell PATH:
$ echo $PATH
/home/smith/bin:/usr/local/bin:/usr/bin:/bin:/usr/games:/
usr/lib/java/bin
Los directorios de una ruta de búsqueda se separan mediante dos puntos
(:). Para una visión más clara, convierte los dos puntos en caracteres de
nueva línea pasando la salida al comando tr , que traduce un carácter en
otro (más detalles en el Capítulo 5):
$ echo $PATH | tr : "\n"
/home/smith/bin
/usr/local/bin
/usr/bin
/bin
/usr/games
/usr/lib/java/bin
El shell consulta los directorios de tu ruta de búsqueda del primero al
último cuando localiza un programa como ls.
"¿Existe /home/smith/bin/ls? No. ¿Existe /usr/local/bin/ls? No.
¿Y /usr/bin/ls? Otra vez no. ¿Quizá /bin/ls? Sí, ¡ahí está! Ejecutaré /bin/ls".
Esta búsqueda ocurre demasiado rápido como para darse cuenta.3
Para localizar un programa en tu ruta de búsqueda, utiliza el
comando which :
$ which cp
/bin/cp
$ which which
/usr/bin/which
o el comando más potente (y verboso) type , un shell builtinque también
localiza alias, funciones y shell builtins:4
$ type cp
cp is hashed (/bin/cp)
$ type ll
ll is aliased to ‘/bin/ls -l’
$ type type
type is a shell builtin
Tu ruta de búsqueda puede contener el comando del mismo nombre en
directorios diferentes, como /usr/bin/less y /bin/less. El shell ejecuta el
comando que aparezca en el directorio anterior de la ruta. Aprovechando
este comportamiento, puedes anular un comando de Linux colocando un
comando con el mismo nombre en un directorio anterior de tu ruta de
búsqueda, como tu directorio personal $HOME/bin.
RUTA DE BÚSQUEDA Y ALIAS
Cuando el shell busca un comando por su nombre, comprueba si ese nombre es
un alias antes de comprobar la ruta de búsqueda. Por eso un alias puede hacer
sombra a (tener prioridad sobre) un comando del mismo nombre.
La ruta de búsqueda es un gran ejemplo de tomar algo misterioso de
Linux y mostrar que tiene una explicación ordinaria. El shell no saca
comandos de la nada ni los localiza por arte de magia. Examina
metódicamente los directorios de una lista hasta encontrar el archivo
ejecutable solicitado.
Entornos y ficheros de inicialización, la
versión corta
Un intérprete de comandos en ejecución contiene un montón de
información importante en variables: la ruta de búsqueda, el directorio
actual, tu editor de texto preferido, tu prompt personalizado del
intérprete de comandos y mucho más. Las variables de un intérprete de
comandos en ejecución se denominan colectivamente entorno del
intérprete de comandos. Cuando el intérprete de comandos sale, su
entorno se destruye.
Sería extremadamente tedioso definir a mano el entorno de cada shell. La
solución es definir el entorno una vez, en scripts de shell
llamados archivos de inicio y archivos de inicialización, y hacer que
cada shell ejecute estos scripts al iniciarse. El efecto es que cierta
información parece ser "global" o "conocida" por todos tus shells en
ejecución.
Me sumergiré en los detalles escabrosos en "Configurar tu entorno". Por
ahora, te enseñaré un archivo de inicialización para que puedas avanzar
en los próximos capítulos. Se encuentra en tu directorio personal y se
llama .bashrc(pronunciado "dot bash R C"). Como su nombre empieza por
un punto, lsno lo lista por defecto:
$ ls $HOME
apple banana carrot
$ ls -a $HOME
.bashrc apple banana carrot
Si $HOME/.bashrc no existe, créalo con un editor de texto. Los comandos
que coloques en este archivo se ejecutarán automáticamente cuando se
inicie el shell,5 por lo que es un buen lugar para definir variables para el
entorno del shell, y otras cosas importantes para el shell, como los alias.
Aquí tienes un ejemplo de archivo .bashrc. Las líneas que empiezan
por# son comentarios:
# Set the search path
PATH=$HOME/bin:/usr/local/bin:/usr/bin:/bin
# Set the shell prompt
PS1='$ '
# Set your preferred text editor
EDITOR=emacs
# Start in my work directory
cd $HOME/Work/Projects
# Define an alias
alias g=grep
# Offer a hearty greeting
echo "Welcome to Linux, friend!"
Los cambios que realices en $HOME/.bashrc no afectan a ninguna shell
en ejecución, sólo a las futuras. Puedes forzar a una shell en ejecución a
releer y ejecutar$HOME/.bashrc con cualquiera de los siguientes
comandos:
$ source $HOME/.bashrc Uses the builtin
"source" command
$ . $HOME/.bashrc Uses a dot
Este proceso se conoce como sourcing el archivo de inicialización. Si
alguien te dice "obtén tu archivo punto-bash-R-C", se refiere a que ejecutes
uno de los comandos anteriores.
ADVERTENCIA
En la vida real, no pongas toda la configuración de tu shell en$HOME/.bashrc.
Una vez que hayas leído los detalles en"Configurar tu entorno", examina
tu $HOME/.bashrc y mueve los comandos a sus archivos correspondientes según
sea necesario.
Resumen
Sólo he cubierto un pequeño número de funciones de bash y sus usos más
básicos. Verás muchas más en los capítulos siguientes, sobre todo en el
Capítulo 6. Por ahora, tu trabajo más importante es comprender los
siguientes conceptos:
El armazón existe y tiene responsabilidades importantes.
El intérprete de comandos evalúa la línea de comandos antes de
ejecutar cualquier comando.
Los comandos pueden redirigir stdin, stdout y stderr.
El entrecomillado y el escape evitan que se evalúen los
caracteres especiales del intérprete de comandos.
El shell localiza los programas utilizando una ruta de
búsqueda de directorios.
Puedes cambiar el comportamiento predeterminado de un
intérprete de comandos añadiendo comandos al
archivo $HOME/.bashrc.
Cuanto mejor entiendas la división entre el intérprete de comandos y los
programas que invoca, más sentido tendrá la línea de comandos y mejor
podrás predecir lo que ocurrirá antes de pulsar Intro para ejecutar un
comando.
1 Por eso el comando ls * no lista los nombres de archivo que empiezan
por punto, es decir, los archivos con puntos.
2 bash evita la recursividad infinita al no expandir el segundo less como
alias.
3 Algunas shells memorizan (caché) las rutas a los programas a medida
que se localizan, reduciendo las búsquedas futuras.
4 Observa que el comando type which produce salida, pero el
comando which type no.
5 Esta afirmación está demasiado simplificada; encontrarás más detalles
en la Tabla 6-1.
Capítulo 3. Reejecutar comandos
Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y
comentarios: translation-feedback@[Link]
Supón que acabas de ejecutar un comando largo con una cadena
detallada, como éste de "Detectar archivos duplicados":
$ md5sum *.jpg | cut -c1-32 | sort | uniq -c | sort -nr
y quieres ejecutarlo una segunda vez. ¡No lo vuelvas a escribir! En lugar
de eso, pide al intérprete de comandos que se remonte al historial y
vuelva a ejecutar el comando. Entre bastidores, el intérprete de comandos
guarda un registro de los comandos que invocas para que puedas
recuperarlos fácilmente y volver a ejecutarlos con sólo pulsar unas teclas.
Esta función del shell se llama historial de comandos. Los usuarios
expertos de Linux utilizan mucho el historial de comandos para acelerar
su trabajo y evitar perder el tiempo.
Del mismo modo, supón que cometes un error al escribir el comando
anterior antes de ejecutarlo, como escribir mal "jpg" como "jg":
$ md5sum *.jg | cut -c1-32 | sort | uniq -c | sort -nr
Para corregir el error, no pulses la tecla Retroceso docenas de veces y lo
vuelvas a escribir todo. En lugar de eso, cambia el comando en su lugar. El
shell admite la edición de la línea de comandos para corregir errores
tipográficos y realizar todo tipo de modificaciones como puede hacer un
editor de texto.
Este capítulo te mostrará cómo ahorrar mucho tiempo y teclear
aprovechando el historial de comandos y la edición de la línea de
comandos. Como de costumbre, no intentaré ser exhaustivo: me centraré
en las partes más prácticas y útiles de estas funciones del shell. (Si utilizas
un shell distinto de bash, consulta el Apéndice B para obtener notas
adicionales).
APRENDE A TECLEAR
Todos los consejos de este libro te servirán mejor si sabes mecanografiar con
rapidez. No importa lo experto que seas, si tecleas 40 palabras por minuto y tu
amigo, igualmente experto, teclea 120, está preparado para trabajar tres veces
más rápido que tú. Busca en Internet "prueba de velocidad de mecanografía"
para medir tu velocidad, y luego busca "tutor de mecanografía" y construye una
habilidad para toda la vida. Intenta alcanzar las 100 palabras por minuto.
Merece la pena el esfuerzo.
Ver el historial de comandos
Un historial de comandos es simplemente una lista de comandos
anteriores que has ejecutado en un intérprete de comandos interactivo.
Para ver el historial de un intérprete de comandos, ejecuta el
comandohistory , que es una función integrada en el intérprete de
comandos. Los comandos aparecen en orden cronológico con números de
identificación para facilitar la consulta. La salida tiene este aspecto:
$ history
1000 cd $HOME/Music
1001 ls
1002 mv jazz.mp3 jazzy-song.mp3
⋮
1003 play jazzy-song.mp3
Omitting 477 lines
1481 cd
1482 firefox [Link]
1483 history Includes the
command you just ran
La salida de history puede tener cientos de líneas de longitud (o más).
Limítala a los comandos más recientes añadiendo un argumento entero,
que especifica el número de líneas a imprimir:
$ history 3 Print the 3 most
recent commands
1482 firefox [Link]
1483 history
1484 history 3
Como history escribe en stdout, también puedes procesar la salida con
tuberías. Por ejemplo, visualiza tu historial pantalla a pantalla:
$ history | less Earliest to latest
entry
$ history | sort -nr | less Latest to earliest
entry
o imprimir sólo los comandos históricos que contengan la palabra cd:
$ history | grep -w cd
1000 cd $HOME/Music
1092 cd ..
1123 cd Finances
1375 cd Checking
1481 cd
1485 history | grep -w cd
Para borrar (eliminar) el historial del intérprete de comandos actual,
utiliza la opción -c:
$ history -c
Recordar órdenes del historial
Te mostraré tres formas de ahorrar tiempo al recuperar comandos del
historial de un intérprete de comandos:
Cursores
Extremadamente sencillo de aprender, pero a menudo lento en la
práctica
Ampliación de la historia
Más difícil de aprender (francamente, es críptico) pero puede ser
muy rápido
Búsqueda incremental
Simple y rápido
Cada método es mejor en situaciones concretas, por lo que recomiendo
aprender los tres. Cuantas más técnicas conozcas, mejor podrás elegir la
adecuada en cada situación.
Recorriendo la Historia
Para recuperar tu comando anterior en un determinado intérprete de
comandos, pulsa la tecla de flecha arriba. Así de sencillo. Sigue pulsando
la flecha hacia arriba para recuperar órdenes anteriores en orden
cronológico inverso. Pulsa la flecha hacia abajo para ir en la otra
dirección (hacia órdenes más recientes). Cuando llegues al comando
deseado, pulsa Intro para ejecutarlo.
Recorrer el historial de comandos es una de las dos mejoras de velocidad
más comunes que aprenden los usuarios de Linux. (La otra es la
coincidencia de patrones de nombres de archivo con *,que ya viste en
el Capítulo 2.) Recorrer el historial es eficaz si el comando que deseas está
cerca en el historial -no más de dos o tres comandos en el pasado-, pero es
tedioso llegar a comandos que están más lejos. Golpear la flecha hacia
arriba 137 veces cansa rápidamente.
El mejor caso de uso del cursor es recuperar y ejecutar el comando
inmediatamente anterior. En muchos teclados, la tecla de flecha arriba
está cerca de la tecla Intro, por lo que puedes pulsar las dos teclas en
secuencia con un rápido movimiento de los dedos. En un teclado QWERTY
americano de tamaño normal, coloco el dedo anular derecho en la flecha
arriba y el índice derecho en Intro para pulsar ambas teclas con eficacia.
(Pruébalo).
PREGUNTAS FRECUENTES SOBRE EL HISTORIAL DE COMANDOS
¿Cuántos comandos se almacenan en el historial de un intérprete de
comandos?
El máximo es quinientos o cualquier número almacenado en la
variable de shell HISTSIZE, que puedes cambiar:
$ echo $HISTSIZE
500
$ HISTSIZE=10000
La memoria del ordenador es tan barata y abundante que tiene
sentido fijarHISTSIZE en un número grande para que puedas
recuperar y volver a ejecutar órdenes del pasado lejano. (Un
historial de 10.000 órdenes ocupa sólo unos 200K de memoria.) O sé
atrevido y almacena órdenes ilimitadas fijando el valor en -1.
¿Qué texto se añade a la historia?
El shell añade exactamente lo que escribes, sin evaluar. Si
ejecutas ls $HOME, el historial contendrá "ls $HOME", no "ls
/home/smith". (Hay una excepción: consulta "Las expresiones del
historial no aparecen en el historial de comandos") .
¿Los comandos repetidos se añaden al historial?
La respuesta depende del valor de la variable HISTCONTROL. Por
defecto, si esta variable no está configurada, se añaden todos los
comandos. Si el valor es ignoredups (que es lo que recomiendo), no
se añaden los comandos repetidos si son consecutivos (consulta man
bash para otros valores):
$ HISTCONTROL=ignoredups
¿Cada proyectil tiene un historial independiente, o todos los proyectiles
comparten un único historial?
Cada intérprete de comandos interactivo tiene su propio historial.
He lanzado un nuevo intérprete de comandos interactivo y ya tiene un
historial. ¿Por qué?
Cada vez que un intérprete de comandos interactivo sale, escribe su
historial en el archivo $HOME/.bash_history o en la ruta
almacenada en la variable del intérprete de comandos HISTFILE:
$ echo $HISTFILE
/home/smith/.bash_history
Los nuevos shells interactivos cargan este archivo al iniciarse, por
lo que inmediatamente tienen un historial. Es un sistema peculiar si
ejecutas muchas shells porque todas escriben $HISTFILE al salir, así
que es un poco impredecible qué historial cargará una nueva shell.
La variable HISTFILESIZE controla cuántas líneas del historial se
escriben en el archivo. Si modificas HISTSIZE para controlar el
tamaño del historial en memoria, considera actualizar
también HISTFILESIZE:
$ echo $HISTFILESIZE
500
$ HISTFILESIZE=10000
Historia Expansión
La expansión del historial es una función del shell que accede al historial
de comandos mediante expresiones especiales. Las expresiones
comienzan con un signo de exclamación, que tradicionalmente se
pronuncia "bang". Por ejemplo, dos signos de exclamación seguidos
("bang bang") equivale al comando inmediatamente anterior:
$ echo Efficient Linux
Efficient Linux
$ !! "Bang bang" = previous
command
echo Efficient Linux The shell helpfully prints
the command being run
Efficient Linux
Para referirte al comando más reciente que empezaba por una
determinada cadena, coloca un signo de exclamación delante de esa
cadena. Así, para volver a ejecutar el comando grep más reciente, ejecuta
"bang grep":
$ !grep
grep Perl [Link]
alpaca Intermediate Perl 2012 Schwartz, Randal
Para hacer referencia a la orden más reciente que contenía una cadena
determinadaen algún lugar, no sólo al principio de la orden, rodea
también la cadena con signos de interrogación:1
$ !?grep?
history | grep -w cd
1000 cd $HOME/Music
⋮
1092 cd ..
También puedes recuperar un comando concreto del historial de un
intérprete de comandos por su posición absoluta: el número de
identificación a su izquierda en la salida de history. Por ejemplo, la
expresión !1203 ("bang 1203") significa "el comando en la posición 1203
del historial":
$ history | grep hosts
1203 cat /etc/hosts
$ !1203 The command at position
1203
cat /etc/hosts
[Link] localhost
[Link] [Link]
::1 [Link]
Un valor negativo recupera un comando por su posición relativa en el
historial, en lugar de por su posición absoluta. Por ejemplo, !-3 ("bang
menos tres") significa "el comando que ejecutaste hace tres comandos":
$ history
4197 cd /tmp/junk
4198 rm *
4199 head -n2 /etc/hosts
4199 cd
4200 history
$ !-3 The command you executed
three commands ago
head -n2 /etc/hosts
[Link] localhost
[Link] [Link]
La expansión del historial es rápida y cómoda, aunque un poco críptica.
Sin embargo, puede ser arriesgada si proporcionas un valor erróneo y la
ejecutas a ciegas. Fíjate bien en el ejemplo anterior. Si te equivocas y
escribes !-4 en lugar de !-3, ¡ejecutarías rm * en lugar del comando
previsto head y borrarías archivos de tu directorio personal por error!
Para mitigar este riesgo, añade el modificador :p para imprimir el
comando de tu historial pero no ejecutarlo:
$ !-3:p
head -n2 /etc/hosts Printed, not executed
El intérprete de comandos añade el comando no ejecutado ( head) al
historial, de modo que si tiene buena pinta, puedes ejecutarlo
cómodamente con un rápido "bang bang":
$ !-3:p
head -n2 /etc/hosts Printed, not executed,
and appended to history
$ !! Run the command for real
head -n2 /etc/hosts Printed and then executed
[Link] localhost
[Link] [Link]
Algunas personas se refieren a la expansión del historial como "comandos
bang", pero expresiones como !! y !grep no son comandos. Son
expresiones de cadena que puedes colocar en cualquier lugar de un
comando. Como demostración, utiliza echo para imprimir el valor de !! en
stdout sin ejecutarlo, y cuenta el número de palabras con wc:
$ ls -l /etc | head -n3 Run any command
total 1584
drwxr-xr-x 2 root root 4096 Jun 16 06:14
ImageMagick-6/
drwxr-xr-x 7 root root 4096 Mar 19 2020
NetworkManager/
$ echo "!!" | wc -w Count the words in the
previous command
echo "ls -l /etc | head -n3" | wc -w
6
Este ejemplo de juguete demuestra que las expansiones del historial
tienen más usos que ejecutar comandos. Verás una técnica más práctica y
potente en la siguiente sección.
Aquí sólo he cubierto algunas características del historial de comandos.
Para obtener información completa, ejecuta man history.
LAS EXPRESIONES DEL HISTORIAL NO APARECEN EN EL HISTORIAL DE COMANDOS
El shell añade comandos al historial literalmente -sin evaluar-, como mencioné
en "Preguntas frecuentes sobre el historial de comandos". La única excepción a
esta regla es la expansión del historial. Sus expresiones siempre se evalúan antes
de añadirse al historial de comandos:
$ ls Run any command
[Link]
$ cd Music Run some other command
$ !-2 Use history expansion
ls
song.mp3
$ history View the history
1000 ls
1001 cd Music
1002 ls "ls" appears in the history, not "!-2"
1003 history
Esta excepción tiene sentido. Imagina que intentas comprender un historial de
comandos lleno de expresiones como !-15 y !-92 que hacen referencia a otras
entradas del historial. Podrías tener que trazar un camino a través de todo el
historial a ojo para entender un solo comando.
No vuelvas a borrar el archivo equivocado
(Gracias a la Ampliación del Historial)
¿Alguna vez has querido borrar archivos utilizando un patrón,
como *.txt, pero accidentalmente has escrito mal el patrón y has borrado
los archivos equivocados? Aquí tienes un ejemplo con un carácter de
espacio accidental después del asterisco:
$ ls
123 [Link] [Link] [Link] dont-delete-me important-file
passwords
$ rm * .txt DANGER!! Don't run this! Deletes the
wrong files!
La solución más común a este peligro es poner un alias a rm para que
ejecute rm -i , de modo que pida confirmación antes de cada borrado:
$ alias rm='rm -i' Often found in a shell
configuration file
$ rm *.txt
/bin/rm: remove regular file '[Link]'? y
/bin/rm: remove regular file '[Link]'? y
/bin/rm: remove regular file '[Link]'? y
En consecuencia, un carácter de espacio de más no tiene por qué ser fatal,
porque las indicaciones de rm -i te advertirán de que estás eliminando los
archivos equivocados:
$ rm * .txt
/bin/rm: remove regular file '123'? Something is
wrong: kill the command
Sin embargo, la solución del alias es engorrosa, porque la mayoría de las
veces puede que no quieras o necesites que rm te avise. Tampoco funciona
si estás conectado a otra máquina Linux sin tus alias. Te mostraré una
forma mejor de evitar que coincidan los nombres de archivo erróneos con
un patrón. La técnica tiene dos pasos y se basa en la expansión del
historial:
1. Compruébalo. Antes de ejecutar rm, ejecuta ls con el patrón
deseado para ver qué archivos coinciden.
2. $ ls *.txt
[Link] [Link] [Link]
3. Borra. Si la salida de ls parece correcta, ejecuta rm !$ para
borrar los mismos archivos que coincidieron.2
4. $ rm !$
rm *.txt
La expansión del historial !$ ("dólar bang") significa "la última palabra
que escribiste en el comando anterior". Por lo tanto, rm !$ aquí es la
abreviatura de "borra lo que acabo de listar con ls," es decir,*.txt. Si
accidentalmente añades un espacio después del asterisco, la salida
de ls hará obvio -seguro- que algo va mal:
$ ls * .txt
/bin/ls: cannot access '.txt': No such file or directory
123 [Link] [Link] [Link] dont-delete-me important-file
passwords
¡Menos mal que ejecutaste primero ls en lugar de rm! Ahora puedes
modificar el comando para eliminar el espacio sobrante y proceder con
seguridad. Esta secuencia de dos comandos -ls seguido de rm !$- es una
gran característica de seguridad para incorporar a tu caja de
herramientas de Linux.
Una técnica relacionada es echar un vistazo al contenido de un archivo
con head antes de eliminarlo, para asegurarte de que estás apuntando al
archivo correcto, y luego ejecutar rm !$:
$ head [Link]
(first 10 lines of the file appear)
$ rm !$
rm [Link]
El shell también proporciona una expansión del historial !* ("bang star"),
que coincide con todos los argumentos que escribiste en el comando
anterior, en lugar de sólo con el argumento final:
$ ls *.txt *.o *.log
[Link] [Link] [Link] main.o [Link] parser.o
$ rm !*
rm *.txt *.o *.log
En la práctica, utilizo !* con mucha menos frecuencia que !$. Su asterisco
conlleva el mismo riesgo de ser interpretado como un carácter de
coincidencia de patrones para nombres de archivo (si tecleas algo mal),
por lo que no es mucho más seguro que teclear a mano un patrón
como *.txt.
Búsqueda incremental del historial de comandos
¿No sería estupendo poder escribir unos cuantos caracteres de un
comando y que el resto apareciera al instante, listo para ejecutarse? Pues
puedes. Esta rápida función del shell, llamada búsqueda incremental, es
similar a las sugerencias interactivas que ofrecen los buscadores web. En
la mayoría de los casos, la búsqueda incremental es la técnica más fácil y
rápida para recuperar comandos del historial, incluso comandos que
ejecutaste hace mucho tiempo. Te recomiendo encarecidamente que la
añadas a tu caja de herramientas:
1. En el indicador del intérprete de comandos, pulsa Ctrl-R
(la R significa búsqueda incremental inversa).
2. Empieza a escribir cualquier parte de un comando anterior:
principio, mitad o final.
3. Con cada carácter que escribes, el intérprete de comandos
muestra el comando histórico más reciente que coincide con lo
que has escrito hasta ahora.
4. Cuando veas el comando que deseas, pulsa Intro para ejecutarlo.
Supón que has escrito el comando cd $HOME/Finances/Bank hace un rato y
quieres volver a ejecutarlo. Pulsa Ctrl-R en el indicador del intérprete de
comandos. El indicador cambia para indicar una búsqueda incremental:
(reverse-i-search)`':
Empieza a escribir el comando deseado. Por ejemplo, escribe c:
(reverse-i-search)`': c
El intérprete de comandos muestra el comando más reciente que contiene
la cadena c, resaltando lo que has escrito:
(reverse-i-search)`': less /etc/hosts
Escribe la siguiente letra, d:
(reverse-i-search)`': cd
El intérprete de comandos muestra el comando más reciente que contiene
la cadena cd, resaltando de nuevo lo que has escrito:
(reverse-i-search)`': cd /usr/local
Continúa escribiendo el comando, añadiendo un espacio y un signo de
dólar:
(reverse-i-search)`': cd $
La línea de comandos se convierte en:
(reverse-i-search)`': cd $HOME/Finances/Bank
Este es el comando que quieres. Pulsa Intro para ejecutarlo, y habrás
terminado en cinco rápidas pulsaciones.
He supuesto aquí que cd $HOME/Finances/Bank era el comando coincidente
más reciente del historial. ¿Y si no es así? ¿Y si has escrito un montón de
comandos que contienen la misma cadena? Si es así, la búsqueda
incremental anterior habría mostrado una coincidencia diferente, como
por ejemplo
(reverse-i-search)`': cd $HOME/Music
¿Y ahora qué? Podrías escribir más caracteres para centrarte en el
comando deseado, pero en lugar de eso, pulsa Ctrl-R una segunda vez.
Esta pulsación hace que el intérprete de comandos salte
al siguiente comando coincidente del historial:
(reverse-i-search)`': cd $HOME/Linux/Books
Sigue pulsando Ctrl-R hasta que llegues al comando deseado:
(reverse-i-search)`': cd $HOME/Finances/Bank
y pulsa Intro para ejecutarlo.
Aquí tienes algunos trucos más con la búsqueda incremental:
Para recuperar la cadena más reciente que has buscado y
ejecutado, empieza pulsando Ctrl-R dos veces seguidas.
Para detener una búsqueda incremental y seguir trabajando en
el comando actual, pulsa la tecla Escape, o Ctrl-J, o cualquier
tecla de edición de la línea de comandos (el siguiente tema de
este capítulo), como la tecla de flecha izquierda o derecha.
Para salir de una búsqueda incremental y borrar la línea de
comandos, pulsa Ctrl-G oCtrl-C.
Tómate tu tiempo para convertirte en un experto de la búsqueda
incremental. Pronto localizarás comandos con una rapidez increíble.3
Edición desde la línea de comandos
Hay todo tipo de razones para editar un comando, ya sea mientras lo
escribes o después de haberlo ejecutado:
Para corregir errores
Para crear un comando trozo a trozo, por ejemplo, escribiendo
primero el final del comando y luego desplazándote al principio
de la línea y escribiendo el principio
Para construir un nuevo comando basándote en uno anterior de
tu historial de comandos (una habilidad clave para construir
canalizaciones complejas, como verás en el Capítulo 8)
En esta sección, te mostraré tres formas de editar un comando para
aumentar tu habilidad y velocidad:
Cursores
De nuevo, el método más lento y menos potente, pero sencillo de
aprender
Notación caret
Una forma de expansión de la historia
Pulsaciones estilo Emacs o Vim
Para editar la línea de comandos de forma potente
Como antes, te recomiendo que aprendas las tres técnicas de flexibilidad.
Cursor dentro de un comando
Sólo tienes que pulsar las teclas de flecha izquierda y flecha derecha para
moverte hacia delante y hacia atrás en la línea de comandos, carácter a
carácter. Utiliza la tecla Retroceso o Supr para eliminar texto y, a
continuación, escribe las correcciones que necesites. La Tabla 3-1 resume
éstas y otras pulsaciones estándar para editar la línea de comandos.
Avanzar y retroceder es fácil pero ineficaz. Es mejor cuando los cambios
son pequeños y sencillos.
Pulsación de teclas Acción
Flecha izquierda Mover un carácter a la izquierda
Flecha derecha Mover a la derecha un carácter
Ctrl + flecha izquierda Mover a la izquierda una palabra
Ctrl + flecha derecha Muévete a la derecha una palabra
Inicio Mover al principio de la línea de comandos
Finaliza Mover al final de la línea de comandos
Retroceso Borrar un carácter antes del cursor
Borra Borrar un carácter bajo el cursor
Tabla 3-1. Teclas de cursor para la edición sencilla de la línea de comandos
Ampliación de la Historia con Carets
Supón que has ejecutado por error el siguiente comando escribiendo jgen
lugar de jpg:
$ md5sum *.jg | cut -c1-32 | sort | uniq -c | sort -nr
md5sum: '*.jg': No such file or directory
Para ejecutar el comando correctamente, podrías recuperarlo del historial
de comandos, situar el cursor sobre el error y corregirlo, pero hay una
forma más rápida de lograr tu objetivo. Sólo tienes que escribir el texto
antiguo (erróneo), el texto nuevo (corregido) y un par de carets (^), así:
$ ^jg^jpg
Pulsa Intro, y aparecerá y se ejecutará el comando correcto:
$ ^jg^jpg
⋮
md5sum *.jpg | cut -c1-32 | sort | uniq -c | sort -nr
La sintaxis del signo de intercalación, que es un tipo de expansión del
historial, significa: "En el comando anterior, en lugar de jg, sustituye jpg".
Observa que el intérprete de comandos imprime el nuevo comando antes
de ejecutarlo, lo cual es un comportamiento estándar para la expansión
del historial.
Esta técnica sólo cambia la primera aparición de la cadena de origen (jg)
en el comando. Si tu comando original contenía jg más de una vez, sólo la
primera instancia cambiaría a jpg.
SUSTITUCIÓN MÁS POTENTE CON AMPLIACIÓN DEL HISTORIAL
Puede que te resulte familiar utilizar los comandos sed o ed para cambiar
una cadena de origen en una cadena de destino:
s/source/target/
El shell también admite una sintaxis similar. Comienza con una expresión
de expansión del historial para recordar un comando, como !!. A
continuación, añade dos puntos,y termina con una sustitución al estilo
sed. Por ejemplo, para recuperar el comando anterior y
sustituir jg por jpg (sólo en la primera aparición), tal y como hace la
notación caret, ejecuta
$ !!:s/jg/jpg/
Puedes empezar con cualquier expansión del historial que desees, como !
md5sum, que recupera el comando más reciente que empieza por md5sum, y
realizar la misma sustitución de jg por jpg:
$ !md5sum:s/jg/jpg/
Esta notación puede parecer complicada, pero a veces es más rápida para
conseguir tu objetivo que otras técnicas de edición desde la línea de
comandos. Ejecuta man history para conocer todos los detalles.
Edición de línea de comandos estilo Emacs o Vim
La forma más potente de editar una línea de comandos es con pulsaciones
de teclas familiares inspiradas en los editores de texto Emacs y Vim. Si ya
eres hábil con uno de estos editores, puedes saltar a este estilo de edición
de la línea de comandos de inmediato. Si no,la Tabla 3-2 te ayudará a
empezar con las pulsaciones de teclas más comunes para el movimiento y
la edición. Ten en cuenta que la tecla "Meta" de Emacs suele ser Escape
(pulsada y soltada) o Alt (pulsada y mantenida).
El shell por defecto es de edición estilo Emacs, y lo recomiendo por ser
más fácil de aprender y utilizar. Si prefieres la edición estilo Vim, ejecuta
el siguiente comando (o añádelo a tu archivo $HOME/.bashrc y ejecútalo):
$ set -o vi
Para editar un comando utilizando las combinaciones de teclas de Vim,
pulsa la tecla Escape para entrar en el modo de edición de comandos y, a
continuación, utiliza las combinaciones de teclas de la columna "Vim"
dela Tabla 3-2. Para volver a la edición estilo Emacs, ejecuta:
$ set -o emacs
Ahora practica, practica y practica hasta que las pulsaciones de las teclas
(ya sean las de Emacs o las de Vim) sean algo natural. Créeme, te lo
devolverán rápidamente en tiempo ahorrado.
Acción Emacs Vim
Avanza un carácter Ctrl-f l
Retroceder un carácter Ctrl-b h
Avanza por una palabra Meta-f w
Retroceder una palabra Meta-b b
Mover al principio de la línea Ctrl-a 0
Mover al final de la línea Ctrl-e $
Transponer (intercambiar) dos Ctrl-t xp
caracteres
Transponer (intercambiar) dos Meta-t n/a
palabras
Escribe la palabra en mayúsculas Meta-c n/a
(primera letra en mayúscula)
Mayúsculas hasta final de palabra Meta-u n/a
Minúsculas hasta el final de la Meta-l n/a
palabra
Cambiar el caso del carácter actual n/a ~
Inserta el siguiente carácter Ctrl-v Ctrl-v
textualmente, incluidos los
caracteres de control
Acción Emacs Vim
Borrar hacia delante un carácter Ctrl-d x
Borrar un carácter hacia atrás Retroceso o Ctrl-h X
Adelanta una palabra Meta-d dw
Corta hacia atrás una palabra Meta-Retroceso o C db
trl-w
Cortar desde el cursor al principio Ctrl-u d^
de la línea
Cortar desde el cursor hasta el final Ctrl-k D
de la línea
Borra toda la línea Ctrl-e Ctrl-u dd
Pegar (tirar) el texto borrado más Ctrl-y p
recientemente
Pegar (jalar) el siguiente texto Meta-y n/a
eliminado (después de un jalado
anterior)
Deshacer la operación de edición Ctrl-_ u
anterior
Deshacer todas las ediciones Meta-r U
realizadas hasta el momento
Pasar del modo inserción al modo n/a Escapa
comando
Cambiar del modo comando al n/a i
modo inserción
Acción Emacs Vim
Abortar una operación de edición Ctrl-g n/a
en curso
Borra la pantalla Ctrl-l Ctrl-l
aLas acciones marcadas con n/a no tienen una pulsación simple, pero
pueden ser posibles con secuencias más largas de pulsaciones.
Tabla 3-2. Teclas para editar al estilo Emacs o Vima
Para más detalles sobre la edición al estilo Emacs, consulta en la
sección "Comandos Readline vinculables" del manual bash de GNU. Para la
edición al estilo Vim, consulta el documento "Readline VI Editing Mode
Cheat Sheet".
Resumen
Practica las técnicas de este capítulo y acelerarás enormemente tu uso de
la línea de comandos. Tres de las técnicas en particular transformaron mi
forma de utilizar Linux, y espero que también lo hagan contigo:
Borrar archivos con !$ por seguridad
Búsqueda incremental con Ctrl-R
Edición de línea de comandos estilo Emacs
1 Aquí puedes omitir el signo de interrogación final - !?grep-, pero en
algunos casos es necesario, como en la expansión del historial al estilo sed
(consulta "Sustitución más potente con la expansión del historial").
2 Asumo que no se han añadido o eliminado archivos coincidentes a tus
espaldas después del paso ls. No confíes en esta técnica en directorios que
cambian rápidamente.
3 Mientras escribía este libro, volví a ejecutar con frecuencia comandos de
control de versiones como git add, git commit y git push. La búsqueda
incremental hizo que volver a ejecutar estos comandos fuera pan comido.
Capítulo 4. Navegando por el Sistema
de Archivos
Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y
comentarios: translation-feedback@[Link]
En la película Las aventuras de Buckaroo Banzai en la 8ª Dimensión,
una comedia clásica de culto de 1984, el espadachín personaje del título
ofrece las siguientes palabras de sabiduría zen: "Recuerda, vayas donde
vayas... ahí estás". Buckaroo bien podría haber estado hablando del
sistema de archivos Linux:
$ cd /usr/share/lib/etc/bin No matter where
you go...
$ pwd
/usr/share/lib/etc/bin ...there you are.
También ocurre que dondequiera que estés en el sistema de archivos de
Linux -tu directorio actual- acabarás yendo a otro lugar (a otro directorio).
Cuanto más rápida y eficazmente puedas realizar esta navegación, más
productivo serás.
Las técnicas de este capítulo te ayudarán a navegar por el sistema de
archivos más rápidamente y tecleando menos. Parecen aparentemente
sencillas, pero son muy rentables, con pequeñas curvas de aprendizaje y
grandes beneficios. Estas técnicas se dividen en dos grandes categorías:
Moverse rápidamente a un directorio específico
Volver rápidamente a un directorio que has visitado antes
Para un rápido repaso de los directorios de Linux, consulta el Apéndice A.
Si utilizas un intérprete de comandos distinto de bash, consulta el
Apéndice B para obtener notas adicionales.
Visitar eficazmente directorios
específicos
Si preguntas a 10 expertos en Linux cuál es el aspecto más tedioso de la
línea de comandos, siete de ellos responderán: "Escribir largas rutas de
directorio".1 Al fin y al cabo, si tus archivos de trabajo están
en/home/mañana/Trabajo/Proyectos/Aplicaciones/Neutron-Star/src/inc
lude, tus documentos financieros están
en/home/mañana/Finanzas/Banco/Cheques/Declaraciones, y tus vídeos
están en /data/Artes/Vídeo/Colección, no es divertido volver a escribir
estas rutas una y otra vez. En esta sección, aprenderás técnicas para
navegar a un directorio determinado de forma eficiente.
Saltar a tu directorio de inicio
Empecemos por lo básico. Vayas donde vayas en el sistema de archivos,
puedes volver a tu directorio raíz ejecutando cd sin argumentos:
$ pwd
/etc Start somewhere else
$ cd Run cd with no
arguments...
$ pwd
/home/smith ...and you're home again
Para saltar a subdirectorios dentro de tu directorio personal desde
cualquier parte del sistema de archivos, haz referencia a tu directorio
personal con una abreviatura en lugar de una ruta absoluta
como /home/smith. Una abreviatura es la variable de shell HOME:
$ cd $HOME/Work
Otra es la tilde:
$ cd ~/Work
Tanto $HOME como ~ son expresiones expandidas por el intérprete de
comandos, hecho que puedes comprobar enviándolas por eco a stdout:
$ echo $HOME ~
/home/smith /home/smith
La tilde también puede referirse al directorio personal de otro usuario si
la colocas inmediatamente delante de su nombre de usuario:
$ echo ~jones
/home/jones
Muévete más rápido con la finalización por
pestañas
Cuando introduzcas comandos cd, ahorra tecleando pulsando la tecla
Tabulador para producir nombres de directorio automáticamente. Como
demostración, visita un directorio que contenga subdirectorios,
como /usr:
$ cd /usr
$ ls
bin games include lib local sbin share src
Supongamos que quieres visitar el subdirectorio Compartir. Escribe sha y
pulsa una vez la tecla Tabulador:
$ cd sha<Tab>
El intérprete de comandos completa el nombre del directorio por ti:
$ cd share/
Este práctico atajo se llama completar tabulación. Funciona
inmediatamente cuando el texto que has escrito coincide con un único
nombre de directorio. Cuando el texto coincide con varios nombres de
directorio, tu shell necesita más información para completar el nombre
deseado. Supongamos que sólo has escrito s y has pulsado Tabulador:
$ cd s<Tab>
El intérprete de comandos no puede completar el
nombre compartir (todavía) porque otros nombres de directorio también
empiezan por s: sbin y src. Pulsa Tabulador una segunda vez y el
intérprete de comandos imprimirá todas las terminaciones posibles para
guiarte:
$ cd s<Tab><Tab>
sbin/ share/ src/
y espera tu próxima acción. Para resolver la ambigüedad, escribe otro
carácter, h, y pulsa Tab una vez:
$ cd sh<Tab>
El intérprete de comandos completa el nombre del directorio por ti,
de sh a compartir:
$ cd share/
En general, pulsa Tabulador una vez para realizar la mayor compleción
posible, o pulsa dos veces para imprimir todas las compleciones posibles.
Cuantos más caracteres escribas, menos ambigüedad y mejor será la
coincidencia.
El tabulador es estupendo para acelerar la navegación. En lugar de
escribir una ruta larga como /home/smith/Proyectos/Web/src/include,
escribe tan poco como quieras y mantén pulsada la tecla Tabulador. Le
cogerás el truco rápidamente con la práctica.
PESTAÑA FINALIZACIÓN VARÍA SEGÚN EL PROGRAMA
El tabulador no es sólo para los comandos de cd. Funciona para la mayoría de
los comandos, aunque su comportamiento puede diferir. Cuando el comando
escd, la tecla Tab completa los nombres de directorio. Para otros comandos que
operan sobre archivos, como cat, grep, y sort, el completado con tabulador
también expande los nombres de archivo. Si el comando es ssh (shell seguro),
completa los nombres de host. Si el comando es chown (cambiar el propietario de
un archivo), completa los nombres de usuario. Incluso puedes crear tus propias
reglas de completado para mayor rapidez, como veremos en el Ejemplo 4-1.
Consulta también man bash y lee su tema "Finalización programable".
Salta a directorios visitados con frecuencia
utilizando alias o variables
Si visitas con frecuencia un directorio lejano,
como/home/smith/Trabajo/Proyectos/Web/src/include, crea un alias que
realice la operación cd:
# In a shell configuration file:
alias work="cd $HOME/Work/Projects/Web/src/include"
Sólo tienes que ejecutar el alias en cualquier momento para llegar a tu
destino:
$ work
$ pwd
/home/smith/Work/Projects/Web/src/include
También puedes crear una variable que contenga la ruta del directorio:
$ work=$HOME/Work/Projects/Web/src/include
$ cd $work
$ pwd
/home/smith/Work/Projects/Web/src/include
$ ls $work/css Use the
variable in other ways
[Link] [Link]
EDITAR ARCHIVOS EDITADOS FRECUENTEMENTE CON UN ALIAS
A veces, la razón para visitar un directorio con frecuencia es editar un archivo
concreto. Si ese es el caso, considera la posibilidad de definir un alias para editar
ese archivo por la ruta absoluta sin cambiar de directorio. La siguiente
definición de alias te permite editar $HOME/.bashrc, sin importar en qué parte
del sistema de archivos te encuentres, ejecutando rcedit. No se necesita cd:
# Place in a shell configuration file and source it:
alias rcedit='$EDITOR $HOME/.bashrc'
Si visitas regularmente muchos directorios con rutas largas, puedes crear
alias o variables para cada uno de ellos. Sin embargo, este enfoque tiene
algunas desventajas:
Es difícil recordar todos esos alias/variables.
Podrías crear accidentalmente un alias con el mismo nombre
que un comando existente, provocando un conflicto.
Una alternativa es crear una función shell como la que aparece enEjemplo
4-1, a la que he llamado qcd ("cd rápido"). Esta función acepta una clave de
cadena como argumento, como work o recipes, y ejecutacd hacia una ruta
de directorio seleccionada.
Ejemplo 4-1. Una función para cd-ing a directorios lejanos
# Define the qcd function
qcd () {
# Accept 1 argument that's a string key, and perform a different
# "cd" operation for each key.
case "$1" in
work)
cd $HOME/Work/Projects/Web/src/include
;;
recipes)
cd $HOME/Family/Cooking/Recipes
;;
video)
cd /data/Arts/Video/Collection
;;
beatles)
cd $HOME/Music/mp3/Artists/B/Beatles
;;
*)
# The supplied argument was not one of the supported keys
echo "qcd: unknown key '$1'"
return 1
;;
esac
# Helpfully print the current directory name to indicate where you
are
pwd
}
# Set up tab completion
complete -W "work recipes video beatles" qcd
Guarda la función en un archivo de configuración del intérprete de
comandos, como$HOME/.bashrc (consulta "Entornos y archivos de
inicialización, la versión abreviada"), obtén el código fuente y estará lista
para ejecutarse. Escribe qcd seguido de una de las teclas admitidas para
visitar rápidamente el directorio asociado:
$ qcd beatles
/home/smith/Music/mp3/Artists/B/Beatles
Como extra, la línea final del script ejecuta el comando complete, un shell
builtin que configura el completado de tabulador personalizado para qcd,
de modo que completa las cuatro teclas admitidas. Ahora ya no tienes que
recordar los argumentos deqcd. Sólo tienes que escribir qcd seguido de un
espacio y pulsar dos veces la tecla Tabulador, y el intérprete de comandos
imprimirá todas las teclas para tu referencia, y podrás completar
cualquiera de ellas de la forma habitual:
$ qcd <Tab><Tab>
beatles recipes video work
$ qcd v<Tab><Enter> Completes 'v' to
'video'
/data/Arts/Video/Collection
Haz que un sistema de archivos grande parezca
más pequeño con CDPATH
La función qcd sólo maneja los directorios que especifiques. El shell
proporciona una solución más general cd-ing sin este inconveniente,
denominada ruta de búsqueda cd. Esta función del shell transformó mi
forma de navegar por el sistema de archivos de Linux.
Supón que tienes un subdirectorio importante que visitas a menudo,
llamadoFotos. Se encuentra en /home/smith/Family/Memories/Photos.
Mientras navegas por el sistema de archivos, siempre que quieras llegar
al directorioFotos, tendrás que escribir una ruta larga, como por ejemplo
$ cd ~/Family/Memories/Photos
¿No sería estupendo que pudieras acortar esta ruta a sólo Fotos, sin
importar en qué parte del sistema de archivos te encuentres, y llegar a tu
subdirectorio?
$ cd Photos
Normalmente, este comando fallaría:
bash: cd: Photos: No such file or directory
a menos que por casualidad te encuentres en el directorio principal
correcto(~/Familia/Memorias) o en algún otro directorio con un
subdirectorio Fotos. Pues bien, con una pequeña configuración, puedes
ordenar a cd que busque tu subdirectorio Fotos en ubicaciones distintas
de tu directorio actual. La búsqueda es rapidísima y sólo busca en los
directorios padre que especifiques. Por ejemplo, puedes indicar a cd que
busque en $HOME/Familia/Memorias además de en el directorio actual.
Entonces, cuando escribas cd Photos desde cualquier otro lugar del
sistema de archivos, cd tendrá éxito:
$ pwd
/etc
$ cd Photos
/home/smith/Family/Memories/Photos
Una ruta de búsqueda cd funciona como tu ruta de búsqueda de
comandos, $PATH, pero en lugar de encontrar comandos, encuentra
subdirectorios. Configúrala con la variable de shell CDPATH, que tiene el
mismo formato que PATH: una lista de directorios separados por dos
puntos. Si tu CDPATH consta de estos cuatro directorios, por ejemplo:
$HOME:$HOME/Projects:$HOME/Family/Memories:/usr/local
y tecleas:
$ cd Photos
entonces cd comprobará la existencia de los siguientes directorios en
orden, hasta que encuentre uno o falle por completo:
1. Fotos en el directorio actual
2. $HOME/Fotos
3. $HOME/Proyectos/Fotos
4. $HOME/Familia/Memorias/Fotos
5. /usr/local/Fotos
En este caso, cd tiene éxito en su cuarto intento y cambia el directorio
a$HOME/Familia/Memorias/Fotos. Si dos directorios de $CDPATH tienen un
subdirectorio llamado Fotos, gana el anterior.
NOTA
Normalmente, un cd correcto no imprime ninguna salida. Sin embargo,
cuando cd localiza un directorio utilizando tu CDPATH, imprime la ruta absoluta
en stdout para informarte de tu nuevo directorio actual:
$ CDPATH=/usr Set a CDPATH
$ cd /tmp No output: CDPATH wasn't consulted
$ cd bin cd consults CDPATH...
/usr/bin ...and prints the new working directory
Llena CDPATH con tus directorios padre más importantes o utilizados con
frecuencia, y podrás cd en cualquiera de sus subdirectorios desde
cualquier lugar del sistema de archivos, sin importar su profundidad, sin
tener que escribir la mayor parte de la ruta. Créeme, esto es increíble, y el
siguiente caso práctico debería demostrarlo.
Organiza tu directorio de inicio para una
navegación rápida
Utilicemos CDPATH para simplificar la forma en que navegas por tu
directorio personal. Con un poco de configuración, puedes hacer que
muchos directorios de tu directorio personal sean fácilmente accesibles
con un tecleo mínimo, sin importar en qué parte del sistema de archivos
te encuentres. Esta técnica funciona mejor si tu directorio raíz está bien
organizado, con al menos dos niveles de subdirectorios. La Figura 4-
1 muestra un ejemplo de una disposición de directorios bien organizada.
Figura 4-1. Dos niveles de subdirectorios en el directorio /home/smith
El truco está en configurar tu CDPATH para que incluya, por orden:
1. $HOME
2. Los subdirectorios de $HOME
3. La ruta relativa de un directorio padre, indicada por dos puntos
(..)
Incluyendo $HOME, puedes saltar inmediatamente a cualquiera de sus
subdirectorios(Familia, Finanzas, Linux, Música y Trabajo) desde
cualquier otro lugar del sistema de archivos sin escribir una ruta inicial:
$ pwd
/etc Begin outside your
home directory
$ cd Work
/home/smith/Work
$ cd Family/School You jumped 1 level
below $HOME
/home/smith/Family/School
Incluyendo subdirectorios de $HOME en tu CDPATH, puedes saltar
asussubdirectorios de una sola vez:
$ pwd
/etc Anywhere outside
your home directory
$ cd School
/home/smith/Family/School You jumped 2 levels
below $HOME
Todos los directorios de tu CDPATH hasta ahora son rutas absolutas
en$HOME y sus subdirectorios. Sin embargo, al incluir la ruta relativa ..,
potencias el nuevo comportamiento de cd en cada directorio. No importa
en qué parte del sistema de archivos te encuentres, puedes saltar a
cualquier directoriohermano(../sibling) por su nombre sin escribir los
dos puntos, porque cd buscará en su padre actual. Por ejemplo, si estás
en/usr/bin y quieres ir a /usr/lib, todo lo que necesitas es cd lib:
$ pwd
/usr/bin Your current
directory
$ ls ..
bin include lib src Your siblings
$ cd lib
/usr/lib You jumped to a
sibling
O, si eres un programador que trabaja con código que tiene
subdirectoriossrc, include y docs:
$ pwd
/usr/src/myproject
$ ls
docs include src
puedes saltar entre los subdirectorios de forma concisa:
$ cd docs Change your current
directory
$ cd include
/usr/src/myproject/include You jumped to a
sibling
$ cd src
/usr/src/myproject/src Again
Un CDPATH para el árbol de la Figura 4-1 podría contener seis elementos: tu
directorio personal, cuatro de sus subdirectorios y la ruta relativa de un
directorio padre:
# Place in a shell configuration file and source it:
export CDPATH=$HOME:$HOME/Work:$HOME/Family:$HOME/Linux:
$HOME/Music:..
Después de crear el archivo de configuración, puedes acceder a cd a un
gran número de directorios importantes sin tener que escribir largas
rutas de directorios, sólo nombres cortos de directorios. ¡Hurra!
Esta técnica funciona mejor si todos los subdirectorios bajo los
directoriosCDPATH tienen nombres únicos. Si tienen nombres duplicados,
como $HOME/Música y $HOME/Linux/Música, es posible que no obtengas
el comportamiento que deseas. El comando cd Music siempre
comprobará $HOMEantes que $HOME/Linux y, en consecuencia, no
localizará$HOME/Linux/Music mediante la búsqueda.
Para comprobar si hay nombres de subdirectorios duplicados en los dos
primeros niveles de$HOME, prueba este descarado one-liner. Enumera
todos los subdirectorios y subsubdirectorios de $HOME, aísla los nombres de
los subsubdirectorios con cut, ordena la lista y cuenta las apariciones
con uniq:
$ cd
$ (ls -d */ && ls -d */*/ | cut -d/ -f2-) | sort | uniq -c
| sort -nr | less
Puede que reconozcas esta técnica de comprobación de duplicados
de"Detectar archivos duplicados". Si la salida muestra algún recuento
superior a 1, tienes duplicados. Me doy cuenta de que este comando
incluye algunas funciones que aún no he cubierto. Aprenderás el doble
ampersand (&&) en " Técnica nº 1: Listas condicionales"y los
paréntesisen "Técnica nº 10: Subconjuntos explícitos".
Volver a los directorios con eficacia
Acabas de ver cómo visitar un directorio de forma eficaz. Ahora te
mostraré cómo volver a visitar un directorio rápidamente cuando
necesites volver atrás.
Alternar entre dos directorios con "cd -"
Supón que estás trabajando en un directorio profundo y ejecutas cd para
ir a otro sitio:
$ pwd
/home/smith/Finances/Bank/Checking/Statements
$ cd /etc
y luego piensa: "No, espera, quiero volver al directorio de
Declaracionesdonde acabo de estar". No vuelvas a escribir la ruta larga
del directorio. Simplemente ejecuta cd con un guión como argumento:
$ cd -
/home/smith/Finances/Bank/Checking/Statements
Este comando devuelve tu intérprete de comandos a su directorio anterior
e imprime su ruta absoluta para que sepas dónde estás.
Para saltar adelante y atrás entre un par de directorios, ejecuta
repetidamente cd -. Esto es un ahorro de tiempo cuando estás haciendo
trabajo concentrado en dos directorios en un solo intérprete de
comandos. Sin embargo, hay un inconveniente: el intérprete de comandos
sólo recuerda un directorio anterior cada vez. Por ejemplo, si estás
alternando entre /usr/local/bin y /etc:
$ pwd
/usr/local/bin
$ cd /etc The shell remembers
/usr/local/bin
$ cd - The shell remembers /etc
/usr/local/bin
$ cd - The shell remembers
/usr/local/bin
/etc
y ejecutas cd sin argumentos para saltar a tu directorio personal:
$ cd The shell remembers /etc
el intérprete de comandos ha olvidado ahora /usr/local/bin como
directorio anterior:
$ cd - The shell remembers your home
directory
/etc
$ cd - The shell remembers /etc
/home/smith
La siguiente técnica supera esta limitación.
Alternar entre varios directorios con pushd y
popd
El comando cd - alterna entre dos directorios, pero ¿qué ocurre si tienes
que controlar tres o más? Supón que estás creando un sitio web local en
tu ordenador Linux. Esta tarea suele implicar cuatro o más directorios:
La ubicación de las páginas web activas e implementadas,
como /var/www/html
El directorio de configuración del servidor web, a
menudo /etc/apache2
La ubicación de los certificados SSL, a menudo /etc/ssl/certs
Tu directorio de trabajo, como ~/Work/Projects/Web/src
Créeme, es tedioso seguir tecleando:
$ cd ~/Work/Projects/Web/src
$ cd /var/www/html
$ cd /etc/apache2
$ cd ~/Work/Projects/Web/src
$ cd /etc/ssl/certs
Si tienes una pantalla grande con ventanas, puedes aliviar la carga
abriendo una ventana de shell distinta para cada directorio. Pero si estás
trabajando en un único shell (por ejemplo, a través de una conexión SSH ),
aprovecha una función del shell llamada pila de directorios. Te permite
desplazarte rápidamente entre varios directorios con facilidad, utilizando
los comandos de shell incorporados pushd, popd, y dirs. La curva de
aprendizaje es de unos 15 minutos, y el enorme beneficio en velocidad
dura toda la vida.2
Una pila de directorios es una lista de directorios que has visitado en el
shell actual y de los que has decidido hacer un seguimiento. Manipulas la
pila realizando dos operaciones llamadas empujar y saltar. Empujar un
directorio lo añade al principio de la lista, lo que tradicionalmente se
llama la parte superior de la pila. Desplegar elimina el directorio más alto
de la pila.3 Inicialmente, la pila sólo contiene tu directorio actual, pero
puedes añadir (push) y eliminar (pop) directorios y rápidamente cd entre
ellos.
NOTA
Cada shell en ejecución mantiene su propia pila de directorios.
Empezaré con las operaciones básicas (empujar, reventar, ver) y luego
llegaré a lo bueno.
Introduce un directorio en la pila
El comando pushd (abreviatura de "empujar directorio") hace todo lo
siguiente:
1. Añade un directorio dado a la parte superior de la pila
2. Realiza un cd a ese directorio
3. Imprime la pila de arriba abajo para tu referencia
Construiré una pila de directorios de cuatro directorios, introduciéndolos
en la pila de uno en uno:
$ pwd
/home/smith/Work/Projects/Web/src
$ pushd /var/www/html
/var/www/html ~/Work/Projects/Web/src
$ pushd /etc/apache2
/etc/apache2 /var/www/html ~/Work/Projects/Web/src
$ pushd /etc/ssl/certs
/etc/ssl/certs /etc/apache2 /var/www/html
~/Work/Projects/Web/src
$ pwd
/etc/ssl/certs
El shell imprime la pila después de cada operación pushd. El directorio
actual es el directorio situado más a la izquierda (arriba).
Ver una pila de directorios
Imprime la pila de directorios de una shell con el comando dirs . No
modifica la pila:
$ dirs
/etc/ssl/certs /etc/apache2 /var/www/html
~/Work/Projects/Web/src
Si prefieres imprimir la pila de arriba abajo, utiliza la opción -p :
$ dirs -p
/etc/ssl/certs
/etc/apache2
/var/www/html
~/Work/Projects/Web/src
e incluso canalizar la salida al comando nl para numerar las líneas de
cero en adelante:
$ dirs -p | nl -v0
0 /etc/ssl/certs
1 /etc/apache2
2 /var/www/html
3 ~/Work/Projects/Web/src
Aún más sencillo, ejecuta dirs -v para imprimir la pila con líneas
numeradas:
$ dirs -v
0 /etc/ssl/certs
1 /etc/apache2
2 /var/www/html
3 ~/Work/Projects/Web/src
Si prefieres este formato descendente, considera la posibilidad de crear
un alias:
# Place in a shell configuration file and source it:
alias dirs='dirs -v'
Hacer saltar un directorio de la pila
El comando popd ("directorio pop") es el inverso de pushd. Hace todo
losiguiente:
1. Elimina un directorio de la parte superior de la pila
2. Realiza un cd al nuevo directorio superior
3. Imprime la pila de arriba abajo para tu referencia
Por ejemplo, si tu pila tiene cuatro directorios:
$ dirs
/etc/ssl/certs /etc/apache2 /var/www/html
~/Work/Projects/Web/src
entonces, si ejecutas repetidamente popd, recorrerás estos directorios de
arriba abajo:
$ popd
/etc/apache2 /var/www/html ~/Work/Projects/Web/src
$ popd
/var/www/html ~/Work/Projects/Web/src
$ popd
~/Work/Projects/Web/src
$ popd
bash: popd: directory stack empty
$ pwd
~/Work/Projects/Web/src
CONSEJO
Los comandos pushd y popd ahorran tanto tiempo que recomiendo crear alias
de dos caracteres tan rápidos de escribir como cd:
# Place in a shell configuration file and source it:
alias gd=pushd
alias pd=popd
Intercambiar directorios en la pila
Ahora que ya puedes construir y vaciar la pila de directorios,
centrémonos en casos prácticos de uso. pushd sin argumentos intercambia
los dos directorios superiores de la pila ynavega hasta el nuevo directorio
superior. Saltemos entre /etc/apache2 y tu directorio de trabajo varias
veces simplemente ejecutando pushd. Observa cómo el tercer
directorio /var/www/htmlpermanece en la pila mientras los dos primeros
directorios intercambian posiciones:
$ dirs
/etc/apache2 ~/Work/Projects/Web/src /var/www/html
$ pushd
~/Work/Projects/Web/src /etc/apache2 /var/www/html
$ pushd
/etc/apache2 ~/Work/Projects/Web/src /var/www/html
$ pushd
~/Work/Projects/Web/src /etc/apache2 /var/www/html
Observa que pushd se comporta de forma similar al comando cd -,
alternando entre dos directorios, pero no tiene la limitación de recordar
sólo un directorio.
Convierte un cd equivocado en un pushd
Supón que estás saltando entre varios directorios con pushd y
accidentalmente ejecutas cd en su lugar y pierdes un directorio:
$ dirs
~/Work/Projects/Web/src /var/www/html /etc/apache2
$ cd /etc/ssl/certs
$ dirs
/etc/ssl/certs /var/www/html /etc/apache2
Uy, el comando accidental cd sustituyó ~/Work/Projects/Web/srcen la pila
por /etc/ssl/certs. Pero no te preocupes. Puedes volver a añadir el
directorio que falta a la pila sin escribir su ruta larga. Sólo tienes que
ejecutar pushd dos veces, una con el argumento guión y otra sin él:
$ pushd -
~/Work/Projects/Web/src /etc/ssl/certs /var/www/html
/etc/apache2
$ pushd
/etc/ssl/certs ~/Work/Projects/Web/src /var/www/html
/etc/apache2
Analicemos por qué funciona:
El primer pushd vuelve al directorio anterior de tu
shell,~/Trabajo/Proyectos/Web/src, y lo empuja a la pila. pushd,
al igual que cd, acepta un guión como argumento para significar
"volver a mi directorio anterior".
El segundo comando pushd intercambia los dos directorios
superiores, devolviéndote a /etc/ssl/certs. El resultado final es
que has restaurado~/Trabajo/Proyectos/Web/src a la segunda
posición en la pila, exactamente donde habría estado si no
hubieras cometido tu error.
Este comando "ups, olvidé un pushd" es lo suficientemente útil como para
que merezca un alias. Yo lo llamo slurp porque en mi mente, "sorbe de
vuelta" un directorio que perdí por error:
# Place in a shell configuration file and source it:
alias slurp='pushd - && pushd'
Profundiza en la pila
¿Y si quieres cd entre directorios de la pila distintos de los dos
primeros? pushd y popd aceptan un argumento entero positivo o negativo
para operar más adentro en la pila. El comando
$ pushd +N
desplaza N los directorios de la parte superior de la pila a la parte inferior
y, a continuación, realiza un cd al nuevo directorio superior. Un
argumento negativo (-N) desplaza los directorios en sentido contrario, de
abajo a arriba, antes de realizar el cd.4
$ dirs
/etc/ssl/certs ~/Work/Projects/Web/src /var/www/html
/etc/apache2
$ pushd +1
~/Work/Projects/Web/src /var/www/html /etc/apache2
/etc/ssl/certs
$ pushd +2
/etc/apache2 /etc/ssl/certs ~/Work/Projects/Web/src
/var/www/html
De esta forma, puedes saltar a cualquier otro directorio de la pila con un
simple comando. Sin embargo, si tu pila es larga, puede resultar difícil
juzgar a simple vista la posición numérica de un directorio. Entonces,
imprime la posición numérica de cada directorio con dirs -v, como
hiciste en"Visualizar una pila de directorios":
$ dirs -v
0 /etc/apache2
1 /etc/ssl/certs
2 ~/Work/Projects/Web/src
3 /var/www/html
Para desplazar /var/www/html a la parte superior de la pila (y convertirlo
en tu directorio actual), ejecuta pushd +3.
Para saltar al directorio situado en la parte inferior de la
pila, ejecuta pushd -0(guión cero):
$ dirs
/etc/apache2 /etc/ssl/certs ~/Work/Projects/Web/src
/var/www/html
$ pushd -0
/var/www/html /etc/apache2 /etc/ssl/certs
~/Work/Projects/Web/src
También puedes eliminar directorios de la pila más allá del directorio
superior, utilizando popd con un argumento numérico. El comando:
$ popd +N
elimina el directorio en la posición N de la pila, contando hacia abajo
desde la parte superior. Un argumento negativo (-N) cuenta hacia arriba
desde la parte inferior de la pila. La cuenta empieza en cero, por lo
que popd +1elimina el segundo directorio desde arriba:
$ dirs
/var/www/html /etc/apache2 /etc/ssl/certs
~/Work/Projects/Web/src
$ popd +1
/var/www/html /etc/ssl/certs ~/Work/Projects/Web/src
$ popd +2
/var/www/html /etc/ssl/certs
Resumen
Todas las técnicas de este capítulo son fáciles de comprender con un poco
de práctica y te ahorrarán mucho tiempo y mecanografía. Las técnicas
que me han cambiado especialmente la vida son:
CDPATHpara una navegación rápida
pushd y popd para rendimientos rápidos
El comando ocasional cd -
1 Me lo he inventado, pero seguro que es verdad.
2 Una alternativa es abrir varias pantallas virtuales utilizando programas
de línea de comandos como screen y tmux, que se llaman multiplexores de
terminal. Son más difíciles de aprender que las pilas de directorios, pero
merece la pena echarles un vistazo.
3 Si conoces las pilas de informática, una pila de directorios es
precisamente una pila de nombres de directorio.
4 Los programadores pueden reconocer estas operaciones como girar la
pila.
Parte II. Habilidades del siguiente nivel
Ahora que ya conoces los conceptos básicos de los comandos, las tuberías,
el shell y la navegación, es hora de dar el siguiente paso. En los cinco
capítulos siguientes, te presentaré una gran cantidad de nuevos
programas Linux y algunos conceptos importantes del shell. Los aplicarás
para construir comandos complejos y abordar situaciones realistas en un
ordenador Linux.
Capítulo 5. Ampliar tu caja de
herramientas
Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y
comentarios: translation-feedback@[Link]
Los sistemas Linux vienen con miles de programas de línea de comandos.
Los usuarios experimentados suelen confiar en un subconjunto más
pequeño -una especie de caja de herramientas- al que vuelven una y otra
vez. El Capítulo 1 añadió seis comandos muy útiles a tu caja de
herramientas, y ahora te entregaré una docena más. Describiré
brevemente cada comando y te mostraré algunos ejemplos de uso. (Para
ver todas las opciones disponibles, consulta la página de manual de un
comando.) También te presentaré dos potentes comandos más difíciles de
aprender, pero que merecen la pena, llamados awk y sed. En general, los
comandos de este capítulo responden a cuatro necesidades comunes y
prácticas de las canalizaciones y otros comandos complejos:
Producir texto
Imprime fechas, horas, secuencias de números y letras, rutas de
archivos, cadenas repetidas y otros textos para poner en marcha tus
procesos.
Aislar texto
Extraer cualquier parte de un archivo de texto con una
combinación de grep, cut,head, tail, y una práctica función de awk.
Combinar texto
Combina archivos de arriba abajo con cat y tac, o uno al lado del
otro con echo y paste. También puedes intercalar archivos
con pastey diff.
Transformar texto
Convertir texto en otro texto utilizando comandos sencillos
como tr yrev, o comandos más potentes como awk y sed.
Este capítulo es una visión general rápida. Los capítulos posteriores
muestran los comandos en acción.
Producir texto
Todas las canalizaciones comienzan con un simple comando que imprime
en stdout. A veces es un comando como grep o cut que extrae datos
seleccionados de un archivo:
$ cut -d: -f1 /etc/passwd | sort Print all usernames
and sort them
o incluso cat, que resulta práctico para pasar el contenido completo de
varios archivos a otros comandos:
$ cat *.txt | wc -l Total the number of
lines
Otras veces, el texto inicial de una cadena procede de otras fuentes. Ya
conoces uno de estos comandos, ls, que imprime los nombres de archivos
y directorios y la información asociada. Echemos un vistazo a otros
comandos y técnicas de producción de texto:
date
Imprime fechas y horas en varios formatos
seq
Imprime una secuencia de números
Ampliación del corsé
Función del shell que imprime una secuencia de números o
caracteres
find
Imprime las rutas de los archivos
yes
Imprime repetidamente la misma línea
El comando de fecha
El comando date imprime la fecha y/o la hora actuales en varios formatos:
$ date Default formatting
Mon Jun 28 16:57:33 EDT 2021
$ date +%Y-%m-%d Year-Month-Day
format
2021-06-28
$ date +%H:%M:%S Hour:Minute:Seconds
format
16:57:33
Para controlar el formato de salida, proporciona un argumento que
empiece por un signo más (+)seguido de cualquier texto. El texto puede
contener expresiones especiales que empiecen por un signo de porcentaje
(%),como %Y para el año actual de cuatro dígitos y %H para la hora actual en
un reloj de 24 horas. Encontrarás una lista completa de expresiones en la
página de manual de date.
$ date +"I cannot believe it's already %A!" Day of
week
I cannot believe it's already Tuesday!
El comando seq
El comando seq imprime una secuencia de números en un rango.
Proporciona dos argumentos, los valores alto y bajo del rango,
y seqimprimirá todo el rango:
$ seq 1 5 Print all integers from 1 to
5, inclusive
1
2
3
4
5
Si proporcionas tres argumentos, el primero y el tercero definen el rango,
y el número del medio es el incremento:
$ seq 1 2 10 Increment by 2 instead of 1
1
3
5
7
9
Utiliza un incremento negativo como -1 para producir una secuencia
descendente:
$ seq 3 -1 0
3
2
1
0
o un incremento decimal para producir números en coma flotante:
$ seq 1.1 0.1 2 Increment by 0.1
1.1
1.2
⋮
1.3
2.0
Por defecto, los valores se separan con un carácter de nueva línea, pero
puedes cambiar el separador con la opción -s seguida de cualquier
cadena:
$ seq -s/ 1 5 Separate values with forward
slashes
1/2/3/4/5
La opción -w hace que todos los valores tengan la misma anchura (en
caracteres) añadiendo ceros a la izquierda según sea necesario:
$ seq -w 8 10
08
09
10
seq puede producir números en muchos otros formatos (consulta la
página de manual), pero mis ejemplos representan los usos más comunes.
Ampliación del corsé (una característica de Shell)
El shell proporciona su propia forma de imprimir una secuencia de
números, conocida comoexpansión de llaves. Empieza con una llave
izquierda, añade dos enteros separados por dos puntos, y termina con una
llave derecha:
$ echo {1..10} Forward from 1
1 2 3 4 5 6 7 8 9 10
$ echo {10..1} Backward from 10
10 9 8 7 6 5 4 3 2 1
$ echo {01..10} With leading zeros
(equal width)
01 02 03 04 05 06 07 08 09 10
De forma más general, la expresión shell{ x.. y.. z} genera los valores
de x ay, incrementados en z:
$ echo {1..1000..100} Count
by hundreds from 1
1 101 201 301 401 501 601 701 801 901
$ echo {1000..1..100}
Backward from 1000
1000 900 800 700 600 500 400 300 200 100
$ echo {01..1000..100} With
leading zeros
0001 0101 0201 0301 0401 0501 0601 0701 0801 0901
CORCHETES RIZADOS FRENTE A CORCHETES CUADRADOS
Los corchetes son un operador de concordancia de patrones para nombres de
archivo(Capítulo 2). En cambio, la expansión de las llaves rizadas no depende en
absoluto de los nombres de archivo. Simplemente se evalúa como una lista de
cadenas. Puedes utilizar la expansión de llaves para imprimir nombres de
archivos, pero no se produce ninguna coincidencia de patrones:
$ ls
file1 file2 file4
$ ls file[2-4] Matches existing filenames
file2 file4
$ ls file{2..4} Evaluates to: file2 file3 file4
ls: cannot access 'file3': No such file or directory
file2 file4
La expansión de llaves también puede producir secuencias de letras, cosa
que no puede hacer seq:
$ echo {A..Z}
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
La expansión de corchetes siempre produce la salida en una sola línea
separada por caracteres de espacio. Cambia esto canalizando la salida a
otros comandos, como tr (consulta "El comando tr"):
$ echo {A..Z} | tr -d ' ' Delete spaces
ABCDEFGHIJKLMNOPQRSTUVWXYZ
$ echo {A..Z} | tr ' ' '\n' Change spaces into newlines
A
B
⋮
C
Z
Crea un alias que imprima la enésimaletra del alfabeto inglés:
$ alias nth="echo {A..Z} | tr -d ' ' | cut -c"
$ nth 10
J
El comando buscar
El comando find lista los archivos de un directorio de forma recursiva,
descendiendo a los subdirectorios e imprimiendo las rutas
completas.1 Los resultados no son alfabéticos (canaliza la salida a sort si
es necesario):
$ find /etc -print List all of /etc recursively
/etc
/etc/[Link]
/etc/nanorc
/etc/apache2
/etc/apache2/sites-available
⋮
/etc/apache2/sites-available/[Link]
find tiene numerosas opciones que puedes combinar. Aquí tienes algunas
muy útiles. Limita la salida sólo a archivos o directorios con la opción -
type:
$ find . -type f -print Files only
$ find . -type d -print Directories only
Limita la salida a los nombres que coincidan con un patrón de nombre de
archivo con la opción-name. Cita o escapa el patrón para que el intérprete
de comandos no lo evalúe primero:
$ find /etc -type f -name "*.conf" -print Files
ending with .conf
/etc/[Link]
/etc/systemd/[Link]
⋮
/etc/systemd/[Link]
Haz que la coincidencia de nombres no distinga entre mayúsculas y
minúsculas con la opción -iname:
$ find . -iname "*.txt" -print
find tiene unos valores por defecto razonables. El directorio por defecto
es el directorio actual, y la acción por defecto es-print, por lo que los
siguientes comandos son equivalentes:
$ find . -iname "*.txt" -print
$ find -iname "*.txt"
find también puede ejecutar un comando Linux para cada ruta de
archivo de la salida, utilizando -exec. La sintaxis es un poco confusa:
1. Construye un comando find y omite -print.
2. Añade -exec seguido del comando a ejecutar. Utiliza la
expresión {} para indicar dónde debe aparecer la ruta del
archivo en el comando.
3. Termina con un punto y coma entrecomillado o escapado,
como ";" o \;.
Aquí tienes un ejemplo de juguete para imprimir un símbolo @ a cada lado
de la ruta del archivo:
$ find /etc -exec echo @ {} @ ";"
@ /etc @
@ /etc/[Link] @
⋮
@ /etc/nanorc @
Un ejemplo más práctico realiza un listado largo (ls -l) de todos los
[Link] de /etc y sus subdirectorios:
$ find /etc -type f -name "*.conf" -exec ls -l {} ";"
-rw-r--r-- 1 root root 703 Aug 21 2017
/etc/[Link]
-rw-r--r-- 1 root root 1022 Apr 20 2018
/etc/systemd/[Link]
-rw-r--r-- 1 root root 604 Apr 20 2018
⋮
/etc/systemd/[Link]
find -exec funciona bien para borrados masivos de archivos en toda una
jerarquía de directorios (¡pero ten cuidado!). Vamos a borrar los archivos
cuyos nombres terminan en tilde (~) dentro del directorio $HOME/tmp y
sus subdirectorios. Por seguridad, ejecuta primero el comando echo
rm para ver qué archivos se borrarían, y luego elimina echo para borrar de
verdad:
$ find $HOME/tmp -type f -name "*~" -exec echo rm {} ";"
echo for safety
rm /home/smith/tmp/file1~
rm /home/smith/tmp/junk/file2~
rm /home/smith/tmp/vm/vm-8.2.0b/lisp/[Link]~
$ find $HOME/tmp -type f -name "*~" -exec rm {} ";"
Delete for real
find también tiene algunas acciones incorporadas, como -ls para listar
archivos y-delete para eliminarlos, que son más eficientes que -exec
ls o-exec rm:
$ find /etc -type f -name "*.conf" -ls List .conf
files like "ls -l"
$ find $HOME/tmp -type f -name "*~" -delete Delete
tilde files
El comando sí
El comando yes imprime la misma cadena una y otra vez hasta que
termina:
$ yes Repeats "y" by default
y
y
y ^C Kill the command with Ctrl-C
$ yes woof! Repeat any other string
woof!
woof!
woof! ^C
¿Para qué sirve este curioso comportamiento? yes puede suministrar
entrada a programas interactivos para que puedan ejecutarse sin
supervisión. Por ejemplo, el programa fsck, que comprueba si hay errores
en un sistema de archivos de Linux, puede pedir al usuario que continúe
y esperar una respuesta de y o n. La salida del comando yes, cuando se
canaliza a fsck, responde a todos los avisos en tu nombre, de modo que
puedes alejarte y dejar que fsck se ejecute hasta el final.2
El uso principal de yes para nuestros propósitos es imprimir una cadena
un número específico de veces mediante la canalización
de yes a head (verás un ejemplo práctico en "Generación de archivos de
prueba"):
$ yes "Efficient Linux" | head -n3 Print a
string 3 times
Efficient Linux
Efficient Linux
Efficient Linux
Aislar texto
Cuando necesites sólo una parte de un archivo, los comandos más
sencillos de combinar y ejecutar son grep, cut, head y tail. Ya has visto los
tres primeros en el Capítulo 1: grep imprime las líneas que coinciden con
una cadena, cut imprime las columnas de un archivo y head imprime las
primeras líneas de un archivo. Un nuevo comando, tail, es lo contrario
de head e imprime las últimas líneas de un archivo. La Figura 5-1 muestra
estos cuatro comandos trabajando juntos.
Figura 5-1. head, grep, y tail extraen líneas, y cut extrae columnas. En este
ejemplo, grep coincide con las líneas que contienen la cadena "blandit".
En esta sección, profundizo en grep, que hace mucho más que emparejar
cadenas simples, y explico tail de manera más formal. También adelanto
una función del comando awk para extraer columnas de una forma
que cut no puede. Estos cinco comandos combinados pueden aislar
prácticamente cualquier texto utilizando una única canalización.
grep: Una mirada más profunda
Ya has visto que grep imprime las líneas de un archivo que coinciden con
una cadena dada:
$ cat frost
Whose woods these are I think I know.
His house is in the village though;
He will not see me stopping here
To watch his woods fill up with snow.
This is not the end of the poem.
$ grep his frost Print lines
containing "his"
To watch his woods fill up with snow.
This is not the end of the poem. "This"
matches "his"
grep también tiene algunas opciones muy útiles. Utiliza la opción -w para
coincidir sólo con palabras completas:
$ grep -w his frost Match the word
"his" exactly
To watch his woods fill up with snow.
Utiliza la opción -i para ignorar las mayúsculas y minúsculas:
$ grep -i his frost
His house is in the village though; Matches
"His"
To watch his woods fill up with snow. Matches
"his"
This is not the end of the poem. "This"
matches "his"
Utiliza la opción -l para imprimir sólo los nombres de los archivos que
contienen líneas coincidentes, pero no las líneas coincidentes en sí:
$ grep -l his * Which files contain the string
"his"?
frost
Sin embargo, la verdadera potencia de grep aparece cuando pasas de
buscar simples cadenas a buscar patrones, llamados expresiones
regulares.3 La sintaxis es diferente a la de los patrones de nombre de
archivo; en la Tabla 5-1 encontrarás una descripción parcial.
Utiliza esta
Para que coincida con esto: Ejemplo
sintaxis:
Comienzo de una línea ^ ^a= Línea que
empieza por a
Utiliza esta
Para que coincida con esto: Ejemplo
sintaxis:
Fin de línea $ !$ = Línea que
termina con un
signo de
exclamación
Cualquier carácter (excepto . …= Tres caracteres
nueva línea) consecutivos
cualesquiera
Un signo de intercalación \c \$= Un signo de
literal, un signo de dólar o dólar literal
cualquier otro carácter
especial c
Cero o más apariciones de la E* _*= Cero o más
expresión E guiones bajos
Cualquier carácter de un [characters] [aeiouAEIOU]=
conjunto Cualquier vocal
Cualquier carácter [^characters] [^aeiouAEIOU]=
individual que no esté en un Cualquier vocal no
conjunto vocal
Cualquier carácter en un [c1-c 2] [0-9] = Cualquier
rango determinado dígito
entre c 1 y c 2
Cualquier carácter que no [^c1-c 2] [^0-9] = Cualquier
esté en un rango no dígito
determinado entre c 1 y c 2
Cualquiera de las dos E1\|E2 para one\|two = O
expresiones E 1 o E 2 grep y sed bien one o bien two
Utiliza esta
Para que coincida con esto: Ejemplo
sintaxis:
E1|E 2 para one|two = O
awk bien one o bien two
Expresión de \(E\) para \(one\|two\)* =
agrupación E para la grep y sed b Cero o más
precedencia apariciones
de one o two
(E) para awk (one|two)* = Cero o
más apariciones
de one o two
a Los tres comandos también difieren en su tratamiento de las
expresiones regulares; la Tabla 5-1 presenta una lista parcial.
b En sed, esta sintaxis hace algo más que agrupar;
consulta "Comparación de subexpresiones con sed".
Tabla 5-1. Algunas sintaxis de expresiones regulares compartidas por grep, awk, y seda
Aquí tienes algunos ejemplos de comandos grep con expresiones
regulares. Coinciden con todas las líneas que empiezan por mayúscula:
$ grep '^[A-Z]' myfile
Haz coincidir todas las líneas que no estén en blanco (es decir, haz
coincidir las líneas en blanco y utiliza -v para omitirlas):
$ grep -v '^$' myfile
Empareja todas las líneas que contengan galleta o tarta:
$ grep 'cookie\|cake' myfile
Haz coincidir todas las líneas de al menos cinco caracteres:
$ grep '.....' myfile
Empareja todas las líneas en las que aparezca un símbolo menor que en
algún lugar antes de un símbolo mayor que, como las líneas de código
HTML:
$ grep '<.*>' [Link]
Las expresiones regulares son geniales, pero a veces estorban.
Supongamos que quieres buscar las dos líneas del archivo frost que
contienen un w seguido de un punto. El siguiente comando produce
resultados erróneos, porque un punto es una expresión regular que
significa "cualquier carácter":
$ grep w. frost
Whose woods these are I think I know.
He will not see me stopping here
To watch his woods fill up with snow.
Para solucionar este problema, puedes escapar el carácter especial:
$ grep 'w\.' frost
Whose woods these are I think I know.
To watch his woods fill up with snow.
pero esta solución se vuelve engorrosa si tienes muchos caracteres
especiales que escapar. Afortunadamente, puedes forzar a grep a que se
olvide de las expresiones regulares y busque literalmente cada carácter
de la entrada utilizando la opción -F ("fijo"). (También puedes ejecutar el
comando equivalentefgrep , pero está oficialmente obsoleto).
$ grep -F w. frost
Whose woods these are I think I know.
To watch his woods fill up with snow.
$ fgrep w. frost
Whose woods these are I think I know.
To watch his woods fill up with snow.
grep tiene muchas otras opciones; te presentaré sólo una más que
resuelve un problema habitual. Utiliza la opción -f (en minúsculas; no la
confundas con -F) para comparar con un conjunto de cadenas en lugar de
con una única cadena. Como ejemplo práctico, vamos a listar todas las
conchas que se encuentran en el archivo /etc/passwd, que introduje
en "Comando nº 5: ordenar". Como recordarás, cada línea
de /etc/passwd contiene información sobre un usuario, organizada en
campos separados por dos puntos. El último campo de cada línea es el
programa que se ejecuta cuando el usuario se conecta. Este programa
suele ser, aunque no siempre, un intérprete de comandos:
$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
7th field is a shell
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
⋮
7th field is not a shell
¿Cómo puedes saber si un programa es una shell? Pues bien, el
archivo/etc/shells enumera todos los shells de inicio de sesión válidos en
un sistema Linux:
$ cat /etc/shells
/bin/sh
/bin/bash
/bin/csh
Así, puedes hacer una lista de todos los shells válidos
en /etc/passwd extrayendo el séptimo campo con cut, eliminando los
duplicados con sort -u y cotejando los resultados con /etc/shells con grep
-f. También añado la opción -F para ser precavido, de modo que todas las
líneas de /etc/shells se tomen literalmente, aunque contengan caracteres
especiales:
$ cut -d: -f7 /etc/passwd | sort -u | grep -f /etc/shells -
F
/bin/bash
/bin/sh
El comando cola
El comando tail imprime las últimas líneas de un archivo-10 líneas por
defecto. Es un compañero del comando head. Supón que tienes un archivo
llamado alfabeto que contiene 26 líneas, una por letra:
$ cat alphabet
A is for aardvark
B is for bunny
⋮
C is for chipmunk
X is for xenorhabdus
Y is for yak
Z is for zebu
Imprime las tres últimas líneas con tail. La opción -n establece el número
de líneas que se imprimirán, al igual que head:
$ tail -n3 alphabet
X is for xenorhabdus
Y is for yak
Z is for zebu
Si precedes el número con un signo más (+), la impresión de comienza en
ese número de línea y continúa hasta el final del archivo. El siguiente
comando comienza en la línea 25 del archivo:
$ tail -n+25 alphabet
Y is for yak
Z is for zebu
Combina tail y head para imprimir cualquier rango de líneas de un
archivo. Para imprimir sólo la cuarta línea, por ejemplo, extrae las cuatro
primeras líneas y aísla la última:
$ head -n4 alphabet | tail -n1
D is for dingo
En general, para imprimir las líneas M a N, extrae las primeras Nlíneas
con head, y luego aísla las últimas N-M+1 líneas contail. Imprime las
líneas seis a ocho del archivo alfabético:
$ head -n8 alphabet | tail -n3
F is for falcon
G is for gorilla
H is for hawk
CONSEJO
head y admiten una sintaxis más sencilla para especificar un número de líneas
sin . Esta sintaxis es antigua, no está documentada y está obsoleta, pero
probablemente seguirá soportándose para siempre: tail -n
$ head -4 alphabet Same as head -n4 alphabet
$ tail -3 alphabet Same as tail -n3 alphabet
$ tail +25 alphabet Same as tail -n+25 alphabet
El comando awk {print} Comando
El comando awk es un procesador de texto de uso general con cientos de
usos. Veamos una pequeña función, print, que extrae columnas de un
archivo de formas que cut no puede. Considera el archivo de
sistema/etc/hosts, que incluye direcciones IP y nombres de host
separados por cualquier cantidad de espacios en blanco:
$ less /etc/hosts
[Link] localhost
[Link] myhost [Link]
[Link] frodo
[Link] gollum
[Link] gandalf
Supón que quieres aislar los nombres de host imprimiendo la segunda
palabra de cada línea. El problema es que cada nombre de host va
precedido de una cantidad arbitraria de espacios en blanco. cut necesita
que sus columnas estén bien alineadas por número de columna (-c) o
separadas por un único carácter coherente (-f). Necesitas un comando
para imprimir la segunda palabra de cada línea, que awk proporciona con
facilidad:
$ awk '{print $2}' /etc/hosts
localhost
myhost
frodo
gollum
gandalf
awk se refiere a cualquier columna mediante un signo de dólarseguido del
número de columna: por ejemplo, $7 para la séptima columna. Si el
número de columna tiene más de un dígito, rodea el número con
paréntesis: por ejemplo, $(25).Para referirte al último campo,
utiliza $NF ("número de campos"). Para referirte a toda la línea, utiliza $0.
awk no imprime por defecto los espacios en blanco entre los valores. Si
quieres espacios en blanco, separa los valores con comas:
$ echo Efficient fun Linux | awk '{print $1 $3}'
No whitespace
EfficientLinux
$ echo Efficient fun Linux | awk '{print $1, $3}'
Whitespace
Efficient Linux
awk's print es ideal para procesar la salida de comandos que se sale de las
columnas ordenadas. Un ejemplo es df, que imprime la cantidad de
espacio libre y usado en disco en un sistema Linux:
$ df / /data
Filesystem 1K-blocks Used Available Use%
Mounted on
/dev/sda1 1888543276 902295944 890244772 51% /
/dev/sda2 7441141620 1599844268 5466214400 23% /data
La ubicación de las columnas puede variar en función de la longitud de
las rutas deFilesystem, los tamaños de disco y las opciones que pases a df,
por lo que no puedes extraer valores de forma fiable con cut. Con awk, sin
embargo, puedes aislar fácilmente (digamos) el cuarto valor de cada línea,
que representa el espacio disponible en disco:
$ df / /data | awk '{print $4}'
Available
890244772
5466214400
e incluso eliminar la primera línea (la cabecera) al mismo tiempo con un
poco de magiaawk, imprimiendo sólo los números de línea mayores que 1:
$ df / /data | awk 'FNR>1 {print $4}'
890244772
5466214400
Si encuentras entradas separadas por algo distinto de caracteres de
espacio, awk puede cambiar su separador de campos por cualquier
expresión regular con la opción -F:
$ echo efficient:::::linux | awk -F':*' '{print $2}'
Any number of colons
linux
Aprenderás más detalles sobre awk en "awk essentials".
Combinar texto
Ya conoces varios comandos que combinan texto de distintos archivos. El
primero es cat, que imprime el contenido de varios archivos en la salida
estándar. Es un unificador de archivos de arriba abajo. De ahí viene su
nombre:concatenaarchivos:
$ cat poem1
It is an ancient Mariner,
And he stoppeth one of three.
$ cat poem2
'By thy long grey beard and glittering eye,
$ cat poem3
Now wherefore stopp'st thou me?
$ cat poem1 poem2 poem3
It is an ancient Mariner,
And he stoppeth one of three.
'By thy long grey beard and glittering eye,
Now wherefore stopp'st thou me?
El segundo comando que has visto para combinar texto es echo, el shell
builtin que imprime los argumentos que le des, separados por un solo
carácter de espacio. Combina cadenas una al lado de la otra:
$ echo efficient linux in $HOME
efficient linux in /home/smith
Examinemos algunos comandos más que combinan texto:
tac
Un combinador de archivos de texto de abajo a arriba
paste
Un combinador paralelo de archivos de texto
diff
Un comando que intercala el texto de dos archivos imprimiendo sus
diferencias
El comando tac
El comando tac invierte un archivo línea por línea. Su nombre
es catescrito al revés.
$ cat poem1 poem2 poem3 | tac
Now wherefore stopp'st thou me?
'By thy long grey beard and glittering eye,
And he stoppeth one of three.
It is an ancient Mariner,
Observa que he concatenado tres archivos antes de invertir el texto. Si en
cambio proporciono varios archivos a tac como argumentos, invierte las
líneas de cada archivo sucesivamente, produciendo una salida diferente:
$ tac poem1 poem2 poem3
And he stoppeth one of three. First
file reversed
It is an ancient Mariner,
'By thy long grey beard and glittering eye, Second
file
Now wherefore stopp'st thou me? Third
file
tac es estupendo para procesar datos que ya están en orden
cronológico pero que no se pueden invertir con el comandosort -r . Un
caso típico es invertir un archivo de registro de un servidor web para
procesar sus líneas de la más reciente a la más antigua:
[Link] - - [30/Nov/2021:23:37:39 -0500] "GET /
HTTP/1.1" ...
[Link] - - [01/Dec/2021:00:02:11 -0500] "GET
/[Link] HTTP/1.1" ...
[Link] - - [01/Dec/2021:00:04:30 -0500] "GET
/[Link] HTTP/1.1" ...
Las líneas están en orden cronológico con marcas de tiempo, pero no
están en orden alfabético ni numérico, por lo que el comando sort -r no
es útil. El comando tac puede invertir estas líneas sin necesidad de tener
en cuenta las marcas de tiempo.
El comando pegar
El comando paste combina archivos uno al lado del otro en columnas
separadas por un único carácter de tabulación. Es un compañero del
comando cut, que extrae columnas de un archivo separado por
tabuladores:
$ cat title-words1
EFFICIENT
AT
COMMAND
$ cat title-words2
linux
the
line
$ paste title-words1 title-words2
EFFICIENT linux
AT the
COMMAND line
$ paste title-words1 title-words2 | cut -f2 cut &
paste are complementary
linux
the
line
Cambia el separador por otro carácter, como una coma, con la opción -
d (que significa "delimitador"):
$ paste -d, title-words1 title-words2
EFFICIENT,linux
AT,the
COMMAND,line
Transpone la salida, produciendo filas pegadas en lugar de columnas
pegadas, con laopción-s:
$ paste -d, -s title-words1 title-words2
EFFICIENT,AT,COMMAND
linux,the,line
paste también intercala datos de dos o más archivos si cambias el
separador por un carácter de nueva línea (\n):
$ paste -d "\n" title-words1 title-words2
EFFICIENT
linux
AT
the
COMMAND
line
El comando diff
diff compara dos archivos línea por línea e imprime un informe escueto
sobre sus diferencias:
$ cat file1
Linux is all about efficiency.
I hope you will enjoy this book.
$ cat file2
MacOS is all about efficiency.
I hope you will enjoy this book.
Have a nice day.
$ diff file1 file2
1c1
< Linux is all about efficiency.
---
> MacOS is all about efficiency.
2a3
> Have a nice day.
La notación 1c1 representa un cambio o diferencia entre los archivos.
Significa que la línea 1 del primer archivo difiere de la línea 1 del segundo
archivo. Esta notación va seguida de la línea correspondientedel
archivo1, un separador de tres guiones (---), y la línea correspondiente
del archivo2. El símbolo inicial< siempre indica una línea del primer
archivo, y> indica una línea del segundo archivo.
La notación 2a3 representa una adición. Significa que el archivo2 tiene
una tercera línea que no está presente después de la segunda línea
del archivo1. Esta notación va seguida de la línea extra del archivo2,
"Que tengas un buen día".
diff La salida puede contener otra notación y adoptar otras formas. Sin
embargo, esta breve explicación es suficiente para nuestro objetivo
principal, que es utilizar diff como un procesador de texto que intercala
líneas de dos archivos. Muchos usuarios no piensan en diff de esta
manera, pero es estupendo para formar cadenas que resuelvan ciertos
tipos de problemas. Por ejemplo, puedes aislar las líneas diferentes
con grep y cut:
$ diff file1 file2 | grep '^[<>]'
< Linux is all about efficiency.
> MacOS is all about efficiency.
> Have a nice day.
$ diff file1 file2 | grep '^[<>]' | cut -c3-
Linux is all about efficiency.
MacOS is all about efficiency.
Have a nice day.
Verás ejemplos prácticos en "Técnica nº 4: Sustitución de
procesos" y"Comprobación de pares de archivos coincidentes".
Transformar texto
El Capítulo 1 introdujo varios comandos que leen texto de la entrada
estándar y lo transforman en otra cosa en la salida estándar. wc imprime
un recuento de líneas, palabras y caracteres; sort ordena las líneas por
orden alfabético o numérico; y uniq consolida las líneas duplicadas.
Vamos a discutir varios comandos más que transforman su entrada:
tr
Traduce caracteres a otros caracteres
rev
Invierte los caracteres de una línea
awk y sed
Transformadores de uso general
El comando tr
tr traduce un conjunto de caracteres a otro. En el Capítulo 2 te mostré un
ejemplo de traducción de dos puntos a caracteres de nueva línea para
imprimir el shell PATH:
$ echo $PATH | tr : "\n" Translate colons
into newlines
/home/smith/bin
/usr/local/bin
/usr/bin
/bin
/usr/games
/usr/lib/java/bin
tr toma dos conjuntos de caracteres como argumentos, y convierte los
miembros del primer conjunto en los miembros correspondientes del
segundo. Los usos más comunes son la conversión de texto a mayúsculas
o minúsculas:
$ echo efficient | tr a-z A-Z Translate a into A,
b into B, etc.
EFFICIENT
$ echo Efficient | tr A-Z a-z
efficient
convertir espacios en nuevas líneas:
$ echo Efficient Linux | tr " " "\n"
Efficient
Linux
y borrando los espacios en blanco con la opción -d (borrar):
$ echo efficient linux | tr -d ' \t' Remove spaces
and tabs
efficientlinux
El comando rev
El comando rev invierte los caracteres de cada línea de entrada:4
$ echo Efficient Linux! | rev
!xuniL tneiciffE
Más allá del evidente valor de entretenimiento, rev es útil para extraer
información complicada de archivos. Supón que tienes un archivo de
nombres de famosos:
$ cat celebrities
Jamie Lee Curtis
Zooey Deschanel
Zendaya Maree Stoermer Coleman
Rihanna
y quieres extraer la palabra final de cada línea (Curtis, Deschanel,
Coleman, Rihanna). Esto sería fácil con cut -f si cada línea tuviera el
mismo número de campos, pero el número varía. Con rev, puedes invertir
todas las líneas, cortar el primer campo e invertir de nuevo para
conseguir tu objetivo:5
$ rev celebrities
sitruC eeL eimaJ
lenahcseD yeooZ
nameloC remreotS eeraM ayadneZ
annahiR
$ rev celebrities | cut -d' ' -f1
sitruC
lenahcseD
nameloC
annahiR
$ rev celebrities | cut -d' ' -f1 | rev
Curtis
Deschanel
Coleman
Rihanna
Los comandos awk y sed
awky sed son "supercomandos" de uso general para procesar texto.
Pueden hacer casi todo lo que hacen los demás comandos de este capítulo,
pero con una sintaxis de aspecto más críptico. Como ejemplo sencillo,
pueden imprimir las 10 primeras líneas de un archivo como hace head:
$ sed 10q myfile Print 10 lines and quit
(q)
$ awk 'FNR<=10' myfile Print while line number
is ≤ 10
También pueden hacer cosas que nuestros otros comandos no pueden,
como sustituir o intercambiar cadenas:
$ echo [Link] | sed 's/\.jpg/.png/'
Replace .jpg by .png
[Link]
$ echo "linux efficient" | awk '{print $2, $1}'
Swap two words
efficient linux
awk y sed son más difíciles de aprender que los otros comandos que he
tratado, porque cada uno de ellos lleva incorporado un lenguaje de
programación en miniatura. Tienen tantas posibilidades que se han
escrito libros enteros sobre ellos.6 Te recomiendo encarecidamente que
dediques tiempo a aprender ambos comandos (o al menos uno de ellos).
Para comenzar tu viaje, cubro los principios básicos de cada comando y
demuestro algunos usos comunes. También te recomiendo varios
tutoriales en línea para aprender más sobre estos comandos potentes y
cruciales.
No te preocupes por memorizar todas las funciones de awk o sed. Tener
éxito con estos comandos significa realmente:
Comprender los tipos de transformaciones que hacen posibles,
para que puedas pensar: "¡Ah! ¡Éste es un trabajo
para awk (o sed)!" y aplicarlos en el momento que lo necesites.
Aprender a leer sus páginas de manual y a encontrar soluciones
completas enStack Exchange y otros recursos en línea
fundamentos de awk
awk transforma líneas de texto de archivos (o stdin) en cualquier otro
texto, mediante una secuencia de instrucciones llamada programa
awk.7 Cuanto más hábil seas escribiendo programas awk, más
flexiblemente podrás manipular el texto. Puedes proporcionar el
programa awk en la línea de comandos:
$ awk program input-files
También puedes almacenar uno o varios programas awk en archivos y
referirte a ellos con laopción-f, y los programas se ejecutarán en
secuencia:
$ awk -f program-file1 -f program-file2 -f program-file3
input-files
Un programa awk incluye una o varias acciones, como calcular valores o
imprimir texto, que se ejecutan cuando una línea de entrada coincide con
unpatrón. Cada instrucción del programa tiene la forma
pattern {action}
Los patrones típicos incluyen:
La palabra BEGIN
Su acción se ejecuta una sola vez, antes de que awk procese ninguna
entrada.
La palabra END
Su acción se ejecuta una sola vez, después de que awk haya
procesado todas las entradas.
Una expresión regular (ver Tabla 5-1) rodeada de barras diagonales
Un ejemplo es /^[A-Z]/ para que coincida con las líneas que
empiezan por mayúscula.
Otras expresiones específicas de awk
Por ejemplo, para comprobar si el tercer campo de una línea de
entrada ($3) empieza por mayúscula, un patrón sería $3~/^[A-Z]/.
Otro ejemplo es FNR>5, que indica a awk que se salte las cinco
primeras líneas de entrada.
Una acción sin patrón se ejecuta para cada línea de entrada. (Varios
programas awk de "El comando awk {print}" eran de este tipo.) Como
ejemplo, awk resuelve elegantemente el problema de "imprimir el apellido
del famoso" de "El comando rev" imprimiendo directamente la última
palabra de cada línea:
$ awk '{print $NF}' celebrities
Curtis
Deschanel
Coleman
Rihanna
CONSEJO
Cuando proporciones un programa awk en la línea de comandos, enciérralo
entre comillas para evitar que el shell evalúe los caracteres especiales de awk's.
Utiliza comillas simples o dobles según sea necesario.
Un patrón sin acción ejecuta la acción por defecto {print}, que sólo
imprime sin cambios las líneas de entrada que coincidan:
$ echo efficient linux | awk '/efficient/'
efficient linux
Para una demostración más completa, procesa el archivo separado por
[Link] del Ejemplo 1-1 para producir una bibliografía
ordenada, convirtiendo las líneas de este formato:
python Programming Python 2010 Lutz, Mark
a este formato:
Lutz, Mark (2010). "Programming Python"
Esta hazaña requiere reordenar tres campos y añadir algunos caracteres
como paréntesis y comillas dobles. El siguiente programa awk hace el
truco, empleando la opción -F para cambiar el separador de entrada de
espacios a tabuladores (\t):
$ awk -F'\t' '{print $4, "(" $3 ").", "\"" $2 "\""}'
[Link]
Lutz, Mark (2010). "Programming Python"
Barrett, Daniel (2005). "SSH, The Secure Shell"
Schwartz, Randal (2012). "Intermediate Perl"
Bell, Charles (2014). "MySQL High Availability"
Siever, Ellen (2009). "Linux in a Nutshell"
Boney, James (2005). "Cisco IOS in a Nutshell"
Roman, Steven (1999). "Writing Word Macros"
Añade una expresión regular para procesar sólo el libro "caballo":
$ awk -F'\t' '/^horse/{print $4, "(" $3 ").", "\"" $2
"\""}' [Link]
Siever, Ellen (2009). "Linux in a Nutshell"
O procesa sólo libros de 2010 o posteriores, comprobando si el
campo$3 coincide con ^201:
$ awk -F'\t' '$3~/^201/{print $4, "(" $3 ").", "\"" $2
"\""}' [Link]
Lutz, Mark (2010). "Programming Python"
Schwartz, Randal (2012). "Intermediate Perl"
Bell, Charles (2014). "MySQL High Availability"
Por último, añade una instrucción BEGIN para imprimir un
encabezamiento amigable, unos guiones para la indentación y una
instrucción END para dirigir al lector a más información:
$ awk -F'\t' \
'BEGIN {print "Recent books:"} \
$3~/^201/{print "-", $4, "(" $3 ").", "\"" $2 "\""} \
END {print "For more books, search the web"}' \
[Link]
Recent books:
- Lutz, Mark (2010). "Programming Python"
- Schwartz, Randal (2012). "Intermediate Perl"
- Bell, Charles (2014). "MySQL High Availability"
For more books, search the web
awk hace mucho más que imprimir: también puede realizar cálculos,como
sumar los números del 1 al 100:
$ seq 1 100 | awk '{s+=$1} END {print s}'
5050
Para aprender awk más allá de lo que se puede abarcar en unas pocas
páginas de libro, haz un tutorial
deawk en [Link]/awk [Link]/awk o busca en la web
"awk tutorial". Te alegrarás de haberlo hecho.
Mejorar el detector de archivos duplicados
En "Detectar archivos duplicados", construiste una canalización que
detecta y cuenta los archivos JPEG duplicados por suma de comprobación,
pero no era lo suficientemente potente como para imprimir los nombres
de los archivos:
$ md5sum *.jpg | cut -c1-32 | sort | uniq -c | sort -nr |
grep -v " 1 "
3 f6464ed766daca87ba407aede21c8fcc
2 c7978522c58425f6af3f095ef1de1cd5
2 146b163929b6533f02e91bdf21cb9563
Ahora que conoces awk, tienes las herramientas para imprimir también los
nombres de los archivos. Construyamos un nuevo comando que lea cada
línea de la salida md5sum :
$ md5sum *.jpg
146b163929b6533f02e91bdf21cb9563 [Link]
63da88b3ddde0843c94269638dfa6958 [Link]
⋮
146b163929b6533f02e91bdf21cb9563 [Link]
y no sólo cuenta las apariciones de cada suma de comprobación, sino que
también almacena los nombres de los archivos para imprimirlos.
Necesitarás dos funciones adicionales de awkllamadas matrices y bucles.
Una matriz es una variable que contiene una colección de valores. Si la
matriz se llama A y contiene siete valores, se podría acceder a los valores
como A[1], A[2] , A[3], hasta A[7]. Los valores del 1 al 7 se
llaman claves de la matriz, y A[1] a A[7] se llaman elementos de la
matriz. Sin embargo, puedes crear las claves que quieras. Si prefieres
acceder a los siete elementos de tu matriz utilizando los nombres de los
personajes de Disney, sigue adelante y
nómbralos A["Doc"],A["Grumpy"], A["Bashful"], hasta A["Dopey"].
Para contar las imágenes duplicadas, crea una matriz llamada counts con
un elemento por cada suma de comprobación. Cada clave de la matriz es
una suma de comprobación, y el elemento asociado contiene el número
de veces que esa suma de comprobación aparece en la entrada. Por
ejemplo, el elemento de la
matrizcounts["f6464ed766daca87ba407aede21c8fcc"] podría tener el valor 3.
El siguiente script awk examina cada línea de la salida md5sum, aísla la
suma de comprobación ($1) y la utiliza como clave para la matriz counts.
El operador ++ incrementa un elemento en 1 cada vez que awkencuentra
su suma de comprobación asociada:
$ md5sum *.jpg | awk '{counts[$1]++}'
Hasta ahora, el script awk no produce ninguna salida: sólo cuenta cada
suma de comprobación y sale. Para imprimir los recuentos, necesitas una
segunda función de awkllamada bucle for. Un bucle for recorre un array,
clave a clave, y procesa cada elemento en secuencia, utilizando
estasintaxis:
for (variable in array) do something with array[variable]
Por ejemplo, imprime cada elemento de la matriz por su clave:
for (key in counts) print array[key]
Coloca este bucle en la instrucción END para que se ejecute después de
calcular todos los recuentos.
$ md5sum *.jpg \
| awk '{counts[$1]++} \
END {for (key in counts) print counts[key]}'
1
2
⋮
2
A continuación, añade las sumas de comprobación a la salida. Cada clave
de la matriz es una suma de comprobación, así que imprime la clave
después del recuento:
$ md5sum *.jpg \
| awk '{counts[$1]++} \
END {for (key in counts) print counts[key] " "
key}'
1 714eceeb06b43c03fe20eb96474f69b8
2 146b163929b6533f02e91bdf21cb9563
⋮
2 c7978522c58425f6af3f095ef1de1cd5
Para recoger e imprimir los nombres de los archivos, utiliza una segunda
matriz, names, también con sumas de comprobación como claves. A
medida que awk procesa cada línea de salida, añade el nombre del archivo
($2) al elemento correspondiente de la matriz names, junto con un espacio
como separador. En el bucle END, después de imprimir la suma de
comprobación (key), imprime dos puntos y los nombres de archivo
recopilados para esa suma de comprobación:
$ md5sum *.jpg \
| awk '{counts[$1]++; names[$1]=names[$1] " " $2} \
END {for (key in counts) print counts[key] " " key
":" names[key]}'
1 714eceeb06b43c03fe20eb96474f69b8: [Link]
2 146b163929b6533f02e91bdf21cb9563: [Link]
[Link]
2 c7978522c58425f6af3f095ef1de1cd5: [Link]
⋮
[Link]
Las líneas que empiezan por 1 representan sumas de comprobación que
sólo aparecen una vez, por lo que no son duplicados. Pasa la salida a grep
-v para eliminar estas líneas, luego ordena los resultados numéricamente,
de mayor a menor, con sort -nry tendrás la salida deseada:
$ md5sum *.jpg \
| awk '{counts[$1]++; names[$1]=names[$1] " " $2} \
END {for (key in counts) print counts[key] " " key
":" names[key]}' \
| grep -v '^1 ' \
| sort -nr
3 f6464ed766daca87ba407aede21c8fcc: [Link]
[Link] [Link]
2 c7978522c58425f6af3f095ef1de1cd5: [Link]
[Link]
2 146b163929b6533f02e91bdf21cb9563: [Link]
[Link]
lo esencial del sed
sed, como awk, transforma el texto de los archivos (o stdin) en cualquier
otro texto, utilizando una secuencia de instrucciones llamada script
sed.8 Los scripts sed son bastante crípticos a primera vista. Un ejemplo
es s/Windows/Linux/g, que significa sustituir cada aparición de la
cadena Windows por Linux. La palabrascript aquí no significa un archivo
(como un script de shell), sino una cadena.9 Invoca sed con un único script
en la línea de comandos:
$ sed script input-files
o utiliza la opción -e para suministrar varios guiones que procesen la
entrada en secuencia:
$ sed -e script1 -e script2 -e script3 input-files
También puedes guardar los guiones sed en archivos y referirte a ellos
con la opción-f , y se ejecutarán en secuencia:
$ sed -f script-file1 -f script-file2 -f script-file3
input-files
Al igual que con awk, la utilidad de sed depende de tu habilidad para crear
scripts sed. El tipo de script más común es un script de sustituciónque
sustituye cadenas por otras cadenas. La sintaxis es
s/regexp/replacement/
donde regexp es una expresión regular que debe coincidir con cada línea
de entrada (ver Tabla 5-1), y replacement es una cadena para sustituir el
texto coincidente. Como ejemplo sencillo, cambia una palabra por otra:
$ echo Efficient Windows | sed "s/Windows/Linux/"
Efficient Linux
CONSEJO
Cuando proporciones un script sed en la línea de comandos, enciérralo entre
comillaspara evitar que el shell evalúe los caracteres especiales de sed's. Utiliza
comillas simples o dobles según sea necesario.
sed resuelve fácilmente el problema de "imprimir el apellido del famoso"
de"El comando rev" con una expresión regular. Sólo tienes que hacer
coincidir todos los caracteres (.*) hasta el último espacio y sustituirlos por
nada:
$ sed 's/.* //' celebrities
Curtis
Deschanel
Coleman
Rihanna
SUSTITUCIÓN Y BARRAS OBLICUAS
Las barras inclinadas en una sustitución pueden sustituirse por cualquier otro
carácter conveniente. Esto es útil cuando una expresión regular incluye barras
inclinadas (que de otro modo habría que escapar). Estos tres scripts sed son
equivalentes:
s/one/two/ s_one_two_ s@one@two@
Puedes seguir una sustitución con varias opciones para afectar a su
comportamiento. La opción i hace que las coincidencias no distingan
entre mayúsculas y minúsculas:
$ echo Efficient Stuff | sed "s/stuff/linux/" Case
sensitive; no match
Efficient Stuff
$ echo Efficient Stuff | sed "s/stuff/linux/i"
Case-insensitive match
Efficient linux
La opción g ("global") sustituye todas las apariciones de la expresión
regular en lugar de sólo la primera:
$ echo efficient stuff | sed "s/f/F/" Replaces
just the first "f"
eFficient stuff
$ echo efficient stuff | sed "s/f/F/g" Replaces all
occurrences of "f"
eFFicient stuFF
Otro tipo común de script sed es el script de borrado. Elimina líneas por
su número de línea:
$ seq 10 14 | sed 4d Remove the 4th line
10
11
12
14
o líneas que coincidan con una expresión regular:
$ seq 101 200 | sed '/[13579]$/d' Delete lines ending
in an odd digit
102
104
⋮
106
200
Comparar subexpresiones con sed
Supongamos que en tienes algunos nombres de archivo:
$ ls
[Link].1 [Link].2 [Link].3
y quieres producir nuevos
nombres, [Link], [Link] [Link]. sed puede dividir los
nombres de archivo en partes y reordenarlas mediante una función
llamada subexpresiones. Primero, crea una expresión regular que
coincida con los nombres de archivo:
image\.jpg\.[1-3]
Quieres mover el último dígito antes en el nombre del archivo, así que
aísla ese dígito rodeándolo con los símbolos \( y \). Esto define una
subexpresión, una parte designada de una expresión regular:
image\.jpg\.\([1-3]\)
sed puede referirse a las subexpresiones mediante un número y
manipularlas. Sólo has creado una subexpresión, por lo que su nombre
es \[Link] segunda subexpresión sería \2, y así sucesivamente, hasta un
máximo de\9. Tus nuevos nombres de archivo tendrían la forma image\
[Link]. Por tanto, tu script sed sería:
$ ls | sed "s/image\.jpg\.\([1-3]\)/image\[Link]/"
[Link]
[Link]
[Link]
Para complicar más las cosas, supón que los nombres de los archivos
tuvieran más variaciones, consistentes en palabras en minúsculas:
$ ls
[Link].1 [Link].2 [Link].3
Crea tres subexpresiones para capturar el nombre de archivo base, la
extensión y el dígito final:
\([a-z][a-z]*\) \1 = Base filename of one
letter or more
\([a-z][a-z][a-z]\) \2 = File extension of
three letters
\([0-9]\) \3 = A digit
Conéctalos con puntos suspensivos (\.) para formar esta expresión
regular:
\([a-z][a-z]*\)\.\([a-z][a-z][a-z]\)\.\([0-9]\)
Representa los nombres de archivo recién transformados a sed como \
1\3.\2, y la sustitución final con sed se convierte en:
$ ls | sed "s/\([a-z][a-z]*\)\.\([a-z][a-z][a-z]\)\.\([0-
9]\)/\1\3.\2/"
[Link]
[Link]
[Link]
Este comando no cambia el nombre de los archivos, sólo imprime los
nuevos nombres. La sección "Insertar un nombre de archivo en una
secuencia" muestra un ejemplo similar que también realiza el cambio de
nombre.
Para aprender sed más allá de lo que se puede abarcar en unas pocas
páginas de libro, haz un tutorial
desed [Link]/sed [Link]/Unix/[Link] o busca en
la web "sed tutorial".
Hacia una caja de herramientas aún
mayor
La mayoría de los sistemas Linux vienen con miles de programas de línea
de comandos, y la mayoría de ellos tienen numerosas opciones que
cambian su comportamiento. No es probable que puedas aprenderlos y
recordarlos todos. Así que, en un momento de necesidad, ¿cómo localizas
un programa nuevo -o adaptas un programa que ya conoces- para lograr
tus objetivos?
Tu primer paso (obvio) es un buscador web. Por ejemplo, si necesitas un
comando que limite la anchura de las líneas en un archivo de texto,
envolviendo las líneas que sean demasiado largas, busca en la web
(digamos) "comando Linux envolver líneas" y se te indicará el
comando fold :
$ cat [Link]
This book is titled "Efficient Linux at the Command Line"
$ fold -w40 [Link]
This book is titled "Efficient Linux at
the Command Line"
Para descubrir comandos que ya están instalados en tu sistema Linux,
ejecuta el comando man -k (o equivalentemente, apropos). Dada una
palabra,man -k busca esa palabra en las breves descripciones de la parte
superior de las páginas de manual:
$ man -k width
DisplayWidth (3) - image format functions and macros
DisplayWidthMM (3) - image format functions and macros
fold (1) - wrap each input line to fit in
⋮
specified width
man -k acepta expresiones regulares del estilo awk en las cadenas de
búsqueda (consultala Tabla 5-1):
$ man -k "wide|width"
Es posible que un comando que no esté instalado en tu sistema se pueda
instalar a través del gestor de paquetes de tu sistema. Un gestor de
paquetes es un software para instalar programas Linux compatibles con
tu sistema. Algunos gestores de paquetes populares
son apt, dnf , emerge ,pacman , rpm , yum , y zypper. Utiliza el
comando man para averiguar qué gestor de paquetes está instalado en tu
sistema y aprender a buscar paquetesdesinstalados. A menudo se trata de
una secuencia de dos comandos: un comando para copiar los datos más
recientes sobre los paquetes disponibles ("metadatos") de Internet a tu
sistema, y otro para buscar los metadatos. Por ejemplo, para los sistemas
basados en Ubuntu o Debian Linux, los comandos son:
$ sudo apt update Download the latest
metadata
$ apt-file search string Search for a string
Si, después de mucho buscar, no puedes localizar o construir un comando
adecuado a tus necesidades, considera la posibilidad de pedir ayuda a en
un foro online. Un buen punto de partida para hacer preguntas eficaces
es la página de ayuda "¿Cómo hago una buena pregunta?" de Stack
Overflow. En general, presenta tus preguntas de forma respetuosa con el
tiempo de los demás, y los expertos estarán más dispuestos a responder.
Eso significa que tu pregunta debe ser breve y directa, incluyendo
cualquier mensaje de error u otros resultados palabra por palabra, y
explicando lo que has intentado hasta ahora por tu cuenta. Dedica tiempo
a hacer una pregunta de calidad: no sólo aumentarás tus posibilidades de
obtener una respuesta útil, sino que además, si el foro es público y se
pueden hacer búsquedas, una pregunta y una respuesta claras pueden
ayudar a otras personas que tengan un problema similar.
Resumen
Ya has superado la pequeña caja de herramientas delCapítulo 1 y estás
preparado para enfrentarte a problemas empresariales más complejos en
la línea de comandos. Los próximos capítulos están llenos de ejemplos
prácticos sobre el uso de tus nuevos comandos en todo tipo de situaciones.
1 El comando relacionado ls -R produce la salida en un formato menos
conveniente para las tuberías.
2 Actualmente, algunas implementaciones de fsck tienen las opciones -
y y -n para responder sí o no, respectivamente, a cada consulta, por lo que
el comando yes es innecesario aquí.
3 El nombre grep es la abreviatura de "obtener expresión regular e
imprimir".
4 Cuestionario: ¿qué hace el oleoducto rev myfile | tac | rev | tac?
5 En breve verás soluciones más sencillas con awk y sed, pero este truco del
doblerev es útil conocerlo.
6 Incluido el libro sed & awk de O'Reilly.
7 El nombre awk es un acrónimo de Aho, Weinberger y Kernighan, los
creadores del programa.
8 El nombre sed es la abreviatura de "editor de flujo", porque edita un
flujo de texto.
9 Si estás familiarizado con los editores vi, vim, ex , o ed, la sintaxis del
script sed puede resultarte familiar.
Capítulo 6. Padres, hijos y entornos
Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y
comentarios: translation-feedback@[Link]
El propósito del intérprete de comandos -ejecutar comandos- es tan
fundamental para Linux que podrías pensar que el intérprete de
comandos está integrado en Linux de alguna manera especial. Pero no es
así. Un intérprete de comandos no es más que un programa ordinario
como lso cat. Está programado para repetir los siguientes pasos una y
otra vez...
1. Imprime un aviso.
2. Lee una orden de la entrada estándar.
3. Evalúa y ejecuta el comando.
Linux hace un gran trabajo ocultando el hecho de que un shell es un
programa ordinario. Cuando te conectas, Linux ejecuta automáticamente
una instancia del intérprete de comandos por ti, conocida como
tu intérprete de comandos de inicio de sesión. Se ejecuta tan
perfectamente que parece Linux, cuando en realidad es sólo un programa
que se ejecuta en tu nombre para interactuar con Linux.
¿DÓNDE ESTÁ TU SHELL DE INICIO DE SESIÓN?
Si inicias sesión en un terminal no gráfico, por ejemplo, utilizando un programa
cliente SSH, el shell de inicio de sesión es el shell inicial con el que interactúas.
Imprime el primer prompt y espera tu comando.
Alternativamente, si estás en la consola del ordenador con una pantalla gráfica,
tu shell de inicio de sesión se ejecuta entre bastidores. Inicia un entorno de
escritorio como GNOME, Unity, Cinnamon, o KDE Plasma. Luego puedes abrir
ventanas de terminal para ejecutar shells interactivos adicionales.
Cuanto más entiendas sobre el shell, más eficazmente podrás trabajar con
Linux y menos supersticiones desarrollarás sobre su funcionamiento
interno. Este capítulo explora los siguientes misterios del intérprete de
comandos más profundamente de lo que lo hizo el Capítulo 2:
Dónde se encuentran los programas shell
Cómo pueden relacionarse entre sí las distintas instancias del
shell
Por qué diferentes instancias del shell pueden tener las mismas
variables, valores, alias y otros contextos
Cómo cambiar el comportamiento por defecto de un intérprete
de comandos editando los archivos de configuración
Al final, espero que descubras que estos misterios no son tan misteriosos
después de todo.
Los shells son archivos ejecutables
El shell por defecto en la mayoría de los sistemas Linux es bash,1 y es un
programa ordinario -un archivo ejecutable- ubicado en el directorio del
sistema/bin junto a cat, ls, grep, y otros comandos familiares:
$ cd /bin
$ ls -l bash cat ls grep
-rwxr-xr-x 1 root root 1113504 Jun 6 2019 bash
-rwxr-xr-x 1 root root 35064 Jan 18 2018 cat
-rwxr-xr-x 1 root root 219456 Sep 18 2019 grep
-rwxr-xr-x 1 root root 133792 Jan 18 2018 ls
bash lo más probable es que tampoco sea la única shell de tu sistema. Los
intérpretes de comandos válidos suelen aparecer, uno por línea, en el
archivo /etc/shells:
$ cat /etc/shells
/bin/sh
/bin/bash
/bin/csh
/bin/zsh
Para ver qué shell estás ejecutando, echo la variable de shell SHELL:
$ echo $SHELL
/bin/bash
En teoría, un sistema Linux puede tratar cualquier programa como un
intérprete de comandos de inicio de sesión válido, si una cuenta de
usuario está configurada para invocarlo al iniciar sesión y aparece
en /etc/shells (si es necesario en tu sistema). Con privilegios de
superusuario, puedes incluso escribir e instalar tu propia shell, como el
script del Ejemplo 6-1. Lee cualquier comando y responde: "Lo siento, me
temo que no puedo hacerlo". Este shell personalizado es
intencionadamente tonto, pero demuestra que otros programas pueden
ser un shell tan legítimo como /bin/bash.
Ejemplo 6-1. halshell: Un shell que se niega a ejecutar tus comandos
#!/bin/bash
# Print a prompt
echo -n '$ '
# Read the user's input in a loop. Exit when the user presses Ctrl-D.
while read line; do
# Ignore the input $line and print a message
echo "I'm sorry, I'm afraid I can't do that"
# Print the next prompt
echo -n '$ '
done
Como bash es sólo un programa, puedes ejecutarlo manualmente como
cualquier otro comando:
$ bash
Si lo haces, sólo verás otro indicador, como si tu comando no hubiera
tenido efecto:
Pero en realidad, has ejecutado una nueva instancia de bash. Esta nueva
instancia imprime un prompt y espera tu orden. Para que la nueva
instancia sea más visible, cambia su prompt (digamos, a %%) estableciendo
la variable de shell PS1, y ejecuta algunos comandos:
$ PS1="%% "
%% ls The prompt has
changed
[Link]
%% echo "This is a new shell"
This is a new shell
Ahora ejecuta exit para terminar la nueva instancia de bash. Volverás al
intérprete de comandos original, que tiene un indicador con el signo del
dólar:
%% exit
$
Debo subrayar que el cambio de %% a $ no fue un cambio de prompt. Fue
un cambio de shell completo. La nueva instancia de bashha terminado, así
que el intérprete de comandos original te pide el siguiente comando.
Ejecutar bash a mano no es sólo un entretenimiento. En el Capítulo
7 utilizarás shells invocados manualmente en tu beneficio.
Procesos Padres e Hijos
Cuando una instancia del shell invoca a otra, como acabo de demostrar, el
shell original se llama padre y la nueva instancia se llama hijo. Lo mismo
ocurre con cualquier programa Linux que invoque a otro programa
Linux. El programa invocador es el padre, y el programa invocado es su
hijo. Un programa Linux en ejecución se conoce como proceso, por lo que
también verás los términos proceso padrey proceso hijo. Un proceso
puede invocar a cualquier número de hijos, pero cada hijo sólo tiene un
padre.
Cada proceso tiene su propio entorno. Un entorno, que quizá recuerdes
de "Entornos y archivos de inicialización, la versión abreviada", incluye
un directorio actual, una ruta de búsqueda, el prompt del shell y otra
información importante contenida en variables del shell. Cuando se crea
un hijo, su entorno es en gran medida una copia del entorno de su padre.
(Te lo explicaré mejor en"Variables de entorno").
Cada vez que ejecutas un comando simple, creas un proceso hijo. Este
es un punto tan importante para entender Linux que lo repetiré: incluso
cuando ejecutas un comando simple como ls, ese comando se ejecuta
secretamente dentro de un nuevo proceso hijo con su propio entorno
(copiado). Eso significa que cualquier cambio que realices en un proceso
hijo, como cambiar la variable prompt PS1 en un shell hijo, sólo afecta al
hijo y se pierde cuando éste sale. Del mismo modo, los cambios que se
realicen en el padre no afectarán a los hijos que ya se estén ejecutando.
Sin embargo, los cambios en el padre pueden afectar a sus futuros hijos,
porque el entorno de cada hijo se copia del entorno de su padre al
iniciarse.
¿Por qué importa que los comandos se ejecuten en procesos hijos? Para
empezar, significa que cualquier programa que ejecutes puede cd por
todo el sistema de archivos, pero cuando sale, tu shell actual (el padre) no
ha cambiado su directorio actual. He aquí un experimento rápido para
demostrarlo. Crea un script de shell llamado cdtest en tu directorio
principal que contenga un comando cd:
#!/bin/bash
cd /etc
echo "Here is my current directory:"
pwd
Hazlo ejecutable:
$ chmod +x cdtest
Imprime el nombre de tu directorio actual y luego ejecuta el script:
$ pwd
/home/smith
$ ./cdtest
Here is my current directory:
/etc
Ahora comprueba tu directorio actual:
$ pwd
/home/smith
Tu directorio actual no ha cambiado, aunque el script cdtest viajó al
directorio /etc. Esto se debe a que cdtest se ejecutó dentro de un proceso
hijo con su propio entorno. Los cambios en el entorno del proceso hijo no
pueden afectar al entorno del proceso padre, por lo que el directorio
actual del proceso padre no ha cambiado. Lo mismo ocurre cuando
ejecutas un programa ejecutable como cat o grep-se ejecuta en un proceso
hijo que sale tras finalizar el programa, llevándose consigo cualquier
cambio en el entorno.
POR QUÉ CD DEBE SER UN SHELL INCORPORADO
Si los programas de Linux no pueden cambiar el directorio actual de tu shell,
entonces ¿cómo consigue cambiarlo el comando cd? Bueno, cd no es un
programa. Es una función integrada en el intérprete de comandos (también
conocida como función integrada del intérprete de comandos). Si cd fuera un
programa externo al shell, los cambios de directorio serían imposibles: se
ejecutarían en un proceso hijo y no podrían afectar al shell original (padre).
Las canalizaciones lanzan varios procesos hijos: uno por cada comando
de la canalización. Este comando de la sección "Comando nº 6: uniq" lanza
seisprocesos hijos:
$ cut -f1 grades | sort | uniq -c | sort -nr | head -n1 |
cut -c9
Variables de entorno
Cada instancia del intérprete de comandos tiene una colección de
variables, como aprendiste en"Evaluación de variables". Algunas
variables son locales a un único intérprete de comandos. Se
denominan variables locales. Otras variables se copian
automáticamentede un shell determinado a cada hijo que invoca. Estas
variables se denominanvariables de entorno, y forman colectivamente el
entorno del intérprete de comandos. Algunos ejemplos de variables de
entorno y sus usos son:
HOME
La ruta a tu directorio personal. Su valor lo establece
automáticamente tu shell de inicio de sesión cuando te conectas.
Los editores de texto como vim y emacsleen la variable HOME para
poder localizar y leer sus archivos de
configuración ($HOME/.vim y $HOME/.emacs, respectivamente).
PWD
El directorio actual de tu shell. Su valor lo establece y mantiene
automáticamente el shell cada vez que cd pasa a otro directorio. El
comando pwd lee la variable PWD para imprimir el nombre del
directorio actual de tu shell.
EDITOR
El nombre (o la ruta) de tu editor de texto preferido en.
Generalmente, su valor lo estableces tú en un archivo de
configuración del intérprete de comandos. Otros programas leen
esta variable para lanzar un editor apropiado en tu nombre.
Visualiza las variables de entorno de un intérprete de comandos con el
comando printenv . La salida es una variable por línea, sin ordenar, y
puede ser bastante larga, así que canalízala a través de sort y less para
una visualización más amigable:2
⋮
$ printenv | sort -i | less
DISPLAY=:0
EDITOR=emacs
HOME=/home/smith
LANG=en_US.UTF-8
PWD=/home/smith/Music
SHELL=/bin/bash
TERM=xterm-256color
⋮
USER=smith
Las variables locales no aparecen en la salida de printenv. Muestra sus
valores precediendo el nombre de la variable con un signo de dólar e
imprimiendo el resultado con echo:
$ title="Efficient Linux"
$ echo $title
Efficient Linux
$ printenv title (produces
no output)
Crear variables de entorno
Para convertir una variable local en una variable de entorno, utiliza el
comandoexport :
$ MY_VARIABLE=10 A local variable
$ export MY_VARIABLE Export it to become an
environment variable
$ export ANOTHER_VARIABLE=20 Or, set and export in a
single command
export especifica que la variable y su valor se copiarán del shell actual a
los hijos futuros. Las variables locales no se copian a los futuros hijos:
$ export E="I am an environment variable" Set an
environment variable
$ L="I am just a local variable" Set a local
variable
$ echo $E
I am an environment variable
$ echo $L
I am just a local variable
$ bash Run a child
shell
$ echo $E Environment
variable was copied
I am an environment variable
$ echo $L Local
variable was not copied
Empty string
is printed
$ exit Exit the
child shell
Recuerda que las variables de un hijo son [Link] cambio en la
copia no afecta al shell padre:
$ export E="I am the original value" Set an
environment variable
$ bash Run a child
shell
$ echo $E
I am the original value Parent's
value was copied
$ E="I was modified in a child" Change the
child's copy
$ echo $E
I was modified in a child
$ exit Exit the
child shell
$ echo $E
I am the original value Parent's
value is unchanged
Inicia una nueva shell en cualquier momento y cambia cualquier cosa de
su entorno, y todos los cambios desaparecerán cuando salgas de la shell.
Esto significa que puedes experimentar con las funciones del intérprete
de comandos de forma segura: sólo tienes que ejecutar un intérprete de
comandos manualmente, creando un hijo, y terminarlo cuandohayas
terminado.
Alerta de superstición: Variables "globales
A veces Linux oculta demasiado bien su funcionamiento interno. Un gran
ejemplo es el comportamiento de las variables de entorno. De alguna
manera, como por arte de magia, variables como HOME y PATH tienen cada
una un valor consistente en todas tus instancias del shell. Parecen
"variables globales" en cierto sentido. (Incluso he visto esta afirmación en
otros libros de Linux, no publicados por O'Reilly.) Pero una variable de
entorno no es global. Cada instancia del shell tiene su propia copia.
Modificar una variable de entorno en una shell no puede cambiar el valor
en ninguna otra shell en ejecución. Las modificaciones sólo afectan a los
futuros hijos de esa shell (aún no invocados).
Si es así, ¿cómo parece que una variable como HOME o PATH mantiene su
valor en todas tus instancias del shell? Hay dos vías para que esto ocurra,
que se ilustran en la Figura 6-1. Resumiendo:
Los niños copian de sus padres.
Para variables como HOME, los valores suelen ser establecidos y
exportados por tu shell de inicio de sesión. Todos los shells futuros
(hasta que te desconectes) son hijos del shell de inicio de sesión, por
lo que reciben una copia de la variable y su valor. Este tipo de
variables de entorno definidas por el sistema se modifican tan
raramente en el mundo real que parecen globales, pero no son más
que variables ordinarias que se rigen por las reglas ordinarias.
(Incluso puedes cambiar sus valores en un intérprete de comandos
en ejecución, pero podrías alterar el comportamiento esperado de
ese intérprete y de los programas que lanza).
Las distintas instancias leen los mismos archivos de configuración.
Las variables locales, que no se copian a los hijos, pueden tener sus
valores establecidos en un archivo de configuración de
Linux como $HOME/.bashrc (consulta más detalles en "Configurar
tu entorno"). Cada instancia del shell, al invocarla, lee y ejecuta los
archivos de configuración apropiados. Como resultado, estas
variables locales parecen copiarse de shell a shell. Lo mismo ocurre
con otras funciones no exportadas del shell, como los alias.
Este comportamiento lleva a algunos usuarios a creer que el
comando export crea una variable global. No es así. El comando export
WHATEVERsimplemente declara a que la variableWHATEVER se copiará del
shell actual a cualquier hijo futuro.
Figura 6-1. Los shells pueden compartir variables y valores mediante la exportación o la lectura de
los mismos archivos de configuración
Cáscaras infantiles frente a subcáscaras
Un hijo es una copia parcial de su padre. Incluye copias de las variables de
entorno de su padre, por ejemplo, pero no las variables locales (no
exportadas) ni los alias de su padre:
$ alias List aliases
alias gd='pushd'
alias l='ls -CF'
alias pd='popd'
$ bash --norc Run a child shell and
ignore bashrc files
$ alias List aliases - none are
known
$ echo $HOME Environment variables are
known
/home/smith
$ exit Exit the child shell
Si alguna vez te has preguntado por qué tus alias no están disponibles en
los scripts de shell,ahora ya lo sabes. Los scripts shell se ejecutan en un
hijo, que no recibe copias de los alias del padre.
Una subestructura, en cambio, es una copia completa de su
padre.3 Incluye todas las variables, alias, funciones y demás del padre.
Para lanzar un comando en una subshell, encierra el comando entre
paréntesis:
$ (ls -l) Launches ls
-l in a subshell
-rw-r--r-- 1 smith smith 325 Oct 13 22:19 [Link]
$ (alias) View
aliases in a subshell
alias gd=pushd
alias l=ls -CF
⋮
alias pd=popd
$ (l) Run an
alias from the parent
[Link]
Para comprobar si una instancia del shell es un subshell, imprime la
variableBASH_SUBSHELL. El valor es distinto de cero en los subshells, cero en
caso contrario:
$ echo $BASH_SUBSHELL Check the current shell
0 Not a subshell
$ bash Run a child shell
$ echo $BASH_SUBSHELL Check the child shell
0 Not a subshell
$ exit Exit the child shell
$ (echo $BASH_SUBSHELL) Run an explicit subshell
1 Yes, it's a subshell
Trataré algunos usos prácticos de las subcápsulas en"Técnica nº 10:
Subcápsulas explícitas". Por ahora, ten en cuenta que puedes crearlas y
que copian los alias del padre.
Configurar tu entorno
Cuando se ejecuta bash, se configura leyendo una secuencia de archivos,
llamados archivos de configuración, y ejecutando su contenido. Estos
archivos definen variables, alias, funciones y otras características del
shell, y pueden incluir cualquier comando de Linux. (Algunos archivos de
configuración los define el administradordel sistema y se aplican a todos
los usuarios del sistema. Se encuentran en el directorio /etc. Otros
archivos de configuración pertenecen a usuarios individuales y pueden
ser modificados por ellos. Se encuentran en el directorio personal del
usuario. La Tabla 6-1 enumera los archivos de configuración estándar
de bash. Los hay de varios tipos:
Archivos de inicio
Archivos de configuración que se ejecutan
automáticamente cuando inicias sesión, es decir, sólo se aplican a
tu shell de inicio de sesión. Un ejemplo de comando en este archivo
podría establecer y exportar una variable de entorno. Sin embargo,
definir un alias en este archivo sería menos útil, porque los alias no
se copian a los hijos.
Ficheros de inicialización ("init")
Archivos de configuración que se ejecutan para cada instancia de
shell que no sea un shell de inicio de sesión; por ejemplo, cuando
ejecutas un shell interactivo a mano o un script de shell (no
interactivo).Un ejemplo de comando de archivo de inicialización
podría establecer una variable o definir un alias.
Limpiar archivos
Archivos de configuración que ejecutan inmediatamente antes de
que salga tu shell de inicio de sesión. Un ejemplo de comando en
este archivo podría ser clear para poner en blanco tu pantalla al
cerrar la sesión.
Tipo Ubicaciones de
de Dirigi Ubicación en todo el archivos personales
arch do por sistema (en orden de
ivo invocación)
Arch Conch /etc/perfil $HOME/. bash_profile,
ivos as de $HOME/.bash_login y
de inicio $HOME/.profile
inici de
o sesión,
al ser
invoca
das
Arch Conch /etc/bashrc o /etc/ $HOME/.bashrc
ivos as [Link], según la
Init interac distro (mira
tivas en /etc para
(sin comprobarlo)
inicio
de
sesión)
, al
invoca
rlas
Scripts Establece la Establece la
de variable BASH_ENV con variable BASH_ENV con
shell, la ruta absoluta a un la ruta absoluta a un
al archivo de archivo de
invoca inicialización inicialización
rlos (ejemplo: BASH_ENV=/usr (ejemplo: BASH_ENV=/usr
/local/etc/bashrc) /local/etc/bashrc)
Limp Conch /etc/bash.bash_logout $HOME/.bash_logout
iar as de
archi inicio
vos de
sesión,
al salir
Tipo Ubicaciones de
de Dirigi Ubicación en todo el archivos personales
arch do por sistema (en orden de
ivo invocación)
Tabla 6-1. Ficheros de configuración estándar originados por bash
Observa que tienes tres opciones para los archivos de inicio personales en
tu directorio personal ( .bash_profile, .bash_login [Link]). La mayoría
de los usuarios pueden elegir uno y quedarse con él. Tu distribución
Linux probablemente ya te proporcione uno de ellos, precargado con
comandos (idealmente) útiles. Las cosas son un poco diferentes si ejecutas
otros shells como Bourne shell(/bin/sh) y Korn shell(/bin/ksh). Estos shells
también leen .profile y pueden fallar si se les dabash-comandos específicos
para ejecutar. Coloca los comandos específicos
de bash en.bash_profile o .bash_login (de nuevo, elige uno).
A veces, los usuarios encuentran confusa la separación entre los archivos
de inicio personales y el archivo de inicialización personal. ¿Por qué
querrías que tu intérprete de comandos de inicio de sesión se comportara
de forma diferente a otros intérpretes de comandos, digamos, que abres
en varias ventanas? La respuesta es que, en muchos casos, no necesitas
que se comporten de forma diferente. Tu archivo de inicio personal
podría hacer poco más que originar tu archivo de inicialización
personal, $HOME/.bashrc, de modo que todos los shells interactivos (de
inicio de sesión o no) tendrían en gran medida la misma configuración.
En otros casos, puede que prefieras dividir las responsabilidades entre tus
archivos de inicio y de inicialización. Por ejemplo, tu archivo de inicio
personal podría establecer y exportar tus variables de entorno para
copiarlas a futuros hijos, mientras que $HOME/.bashrc podría definir
todos tus alias (que no se copian a los hijos).
Otra consideración es si inicias sesión en un entorno de escritorio gráfico
con ventanas (GNOME, Unity, KDE Plasma, etc.) donde tu shell de inicio de
sesión puede estar oculto. En este caso, puede que no te importe cómo se
comporta el intérprete de comandos de inicio de sesión porque sólo
interactúas con sus hijos, por lo que podrías poner la mayor parte o toda
tu configuración en $HOME/.bashrc.4 Por otro lado, si te conectas
principalmente desde un programa terminal no gráfico, como un cliente
SSH, entonces interactúas directamente con tu shell de inicio de sesión,
por lo que su configuración importa mucho.
En cada uno de estos casos, suele valer la pena que tu archivo de inicio
personal sea la fuente de tu archivo de inicialización personal:
# Place in $HOME/.bash_profile or other personal startup file
if [ -f "$HOME/.bashrc" ]
then
source "$HOME/.bashrc"
fi
Hagas lo que hagas, intenta no colocar comandos de configuración
idénticos en dos archivos de configuración diferentes. Es una receta para
la confusión, y es difícil de mantener, porque cualquier cambio que hagas
en un archivo debes acordarte de duplicarlo en el otro (y lo olvidarás,
créeme). En lugar de eso, crea un archivo a partir del otro como te he
mostrado.
Releer un archivo de configuración
Cuando modifiques cualquier archivo de inicio oinicialización, puedes
forzar a un intérprete de comandos en ejecución a releerlo mediante la
búsqueda de fuentes en el archivo, como se explica en"Entornos y
archivos de inicialización, la versión abreviada":
$ source ~/.bash_profile Uses the builtin
"source" command
$ . ~/.bash_profile Uses a dot
POR QUÉ EXISTE EL MANDO FUENTE
¿Por qué creas un archivo de configuración en lugar de hacerlo ejecutable
con chmod y ejecutarlo como un script de shell? Porque un script se ejecuta en
un proceso hijo. Cualquier comando del script no afectaría a tu shell (padre).
Sólo afectaría al proceso hijo, que se cierra sin que hayas cambiado nada.
Viajar con tu entorno
Si utilizas muchas máquinas Linux en varias ubicaciones, es posible que
en algún momento quieras instalar tus archivos de configuración
cuidadosamente elaborados en más de una máquina. No copies archivos
individuales de una máquina a otra, ya que ese método acaba provocando
confusión. En su lugar, almacena y mantén los archivos en una cuenta
gratuita en GitHubo en un servicio similar de desarrollo de software con
control de versiones. Así podrás descargar, instalar y actualizar tus
archivos de configuración de forma cómoda y coherente en cualquier
máquina Linux. Si cometes un error editando un archivo de
configuración, puedes volver a una versión anterior ejecutando uno o dos
comandos. El control de versiones va más allá del alcance de este libro;
consulta "Aplicar el control de versiones a los archivos cotidianos" para
saber más sobre él.
Si no te sientes cómodo con sistemas de control de versiones como Git o
Subversion, almacena los archivos de configuración en un servicio de
archivos sencillo como Dropbox, Google Drive o OneDrive. Las
actualizaciones de tus archivos de configuración serán menos cómodas,
pero al menos los archivos estarán fácilmente disponibles para copiarlos
en otros sistemas Linux.
Resumen
He conocido a muchos usuarios de Linux a los que les desconciertan (o
desconocen) los procesos padre e hijo, los entornos y los propósitos de los
numerosos archivos de configuración del shell. Después de leer este
capítulo, espero que tengas una idea más clara de todas estas cosas. En
elCapítulo 7 entran en juego como poderosas herramientas para ejecutar
comandos de forma flexible.
1 Si utilizas un intérprete de comandos diferente, consulta también el
Apéndice B.
2 He recortado la salida selectivamente para mostrar las variables de
entorno más comunes. Es probable que tu salida sea mucho más larga y
esté llena de nombres de variables oscuros.
3 Está completo excepto para las trampas, que "se restablecen a los valores
que el intérprete de comandos heredó de su padre en la invocación" ( man
bash). No hablo más de las trampas en este libro.
4 Para hacer las cosas un poco más confusas, algunos entornos de
escritorio tienen sus propios archivos de configuración del shell. Por
ejemplo, GNOME tiene $HOME/ .gnomerc, y el sistema de ventanas X
subyacente tiene $HOME/.xinitrc.
Capítulo 7. 11 formas más de ejecutar
un comando
Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y
comentarios: translation-feedback@[Link]
Ahora que tienes un montón de comandos en tu caja de herramientas y
un conocimiento profundo del shell, es hora de aprender... a ejecutar
comandos. Espera un momento, ¿no has estado ejecutando comandos
desde el principio del libro? Pues sí, pero sólo de dos formas. La primera
es la ejecución ordinaria de un comando simple:
$ grep Nutshell [Link]
La segunda es una tubería de comandos sencillos, como se explica enel
Capítulo 1:
$ cut -f1 grades | sort | uniq -c | sort -nr
En este capítulo, te mostraré 11 formas más de ejecutar un comando y por
qué debería importarte aprenderlas. Cada técnica tiene sus pros y sus
contras, y cuantas más técnicas conozcas, más flexible y eficazmente
podrás interactuar con Linux. De momento me ceñiré a los aspectos
básicos de cada técnica; verás ejemplos más intrincados en los dos
capítulos siguientes.
Lista de técnicas
Una lista es una secuencia de comandos en una sola línea de comandos.
Ya has visto un tipo de lista -una tubería-, pero el shell admite otras con
un comportamiento diferente:
Listas condicionales
Cada orden depende del éxito o fracaso de la anterior.
Listas incondicionales
Los comandos simplemente se ejecutan uno tras otro.
Técnica nº 1: Listas condicionales
Supongamos que quieres crear un archivo [Link] en un directorio dir.
Una secuencia típica de comandos podría ser:
$ cd dir Enter the directory
$ touch [Link] Make the file
Observa cómo el segundo comando depende del éxito del primero. Si el
directorio dir no existe, no tiene sentido ejecutar el comandotouch. El shell
te permite hacer explícita esta dependencia. Si colocas el
operador && (pronunciado "y") entre los dos comandos de una misma
línea:
$ cd dir && touch [Link]
entonces el segundo comando (touch) sólo se ejecuta si el primero (cd)
tiene éxito. El ejemplo anterior es una lista condicional de dos comandos.
(Para saber qué significa que un comando "tenga éxito", consulta"Los
códigos de salida indican éxito o fracaso").
Es muy probable que todos los días ejecutes comandos que dependen de
otros anteriores. Por ejemplo, ¿alguna vez has hecho una copia de
seguridad de un archivo para guardarlo, has modificado el original y has
borrado la copia de seguridad al terminar?
$ cp [Link] [Link] Make a backup copy
$ nano [Link] Change the original
$ rm [Link] Delete the backup
Cada una de estas órdenes sólo tiene sentido si la anterior tiene éxito. Por
lo tanto, esta secuencia es candidata a lista condicional:
$ cp [Link] [Link] && nano [Link] && rm
[Link]
Como otro ejemplo, si utilizas el sistema de control de versiones Git para
mantener archivos, probablemente estés familiarizado con la siguiente
secuencia de comandos después de cambiar algunos archivos: ejecuta git
add para preparar los archivos para una confirmación, luego git commit, y
finalmente git push para compartir tus cambios confirmados. Si alguno
de estos comandos fallara, no ejecutarías el resto (hasta que solucionaras
la causa del fallo). Por lo tanto, estos tres comandos funcionan bien como
una lista condicional:
$ git add . && git commit -m"fixed a bug" && git push
Al igual que el operador && ejecuta un segundo comando sólo si el primero
tiene éxito, el operador relacionado || (pronunciado "o") ejecuta un
segundo comando sólo si el primero falla. Por ejemplo, el siguiente
comando intenta introducirdir, y si no lo consigue, creadir:1
$ cd dir || mkdir dir
Verás habitualmente el operador || en los scripts, que hace que el script
salga si se produce un error:
# If a directory can't be entered, exit with an error code of 1
cd dir || exit 1
Combina los operadores && y || para configurar acciones más complicadas
para el éxito y el fracaso. El siguiente comando intenta entrar en el
directorio dir, y si falla, crea el directorio y entra en él. Si todo falla, el
comando imprime un mensaje de error:
$ cd dir || mkdir dir && cd dir || echo "I failed"
Los comandos de una lista condicional no tienen por qué ser comandos
simples; también pueden ser canalizaciones y otros comandos
combinados.
LOS CÓDIGOS DE SALIDA INDICAN ÉXITO O FRACASO
¿Qué significa para que un comando Linux tenga éxito o falle? Cada
comando Linux produce un resultado cuando termina, llamado código
de salida. Por convención, un código de salida cero significa éxito y
cualquier valor distinto de cero significa fallo.2 Para ver el código de
salida del último comando completado de un intérprete de comandos,
imprime la variable especial del intérprete de comandos cuyo nombre es
un signo de interrogación (?):
$ ls [Link]
[Link]
$ echo $? Print the value of
the ? variable
0 ls succeeded
$ cp [Link] [Link]
cp: cannot stat '[Link]': No such file or
directory
$ echo $?
1 cp failed
Técnica nº 2: Listas incondicionales
Los comandos de una lista no tienen por qué depender unos de otros. Si
separas los comandos con punto y coma,simplemente se ejecutan en
orden. El éxito o fracaso de una orden no afecta a las posteriores de la
lista.
Me gustan las listas incondicionales para lanzar comandos ad hoc después
de haber dejado el trabajo por un día. Aquí tienes una que duerme (no
hace nada) durante dos horas (7.200 segundos) y luego hace una copia de
seguridad de mis archivos importantes:
$ sleep 7200; cp -a ~/important-files /mnt/backup_drive
Aquí tienes un comando similar que funciona como un primitivo sistema
de recordatorio, durmiendo durante cinco minutos y enviándome
después un correo electrónico:3
$ sleep 300; echo "remember to walk the dog" | mail -s
reminder $USER
Las listas incondicionales son una función práctica: producen los mismos
resultados (en su mayoría) que escribir los comandos individualmente y
pulsar Intro después de cada uno. La única diferencia significativa se
refiere a los códigos de salida. En una lista incondicional, los códigos de
salida de los comandos individuales se desechan excepto el último. Sólo el
código de salida del último comando ejecutado en la lista se asigna a la
variable del shell ?:
$ mv file1 file2; mv file2 file3; mv file3 file4
$ echo $?
0 The exit code for "mv file3
file4"
Técnicas de sustitución
Sustituir significa reemplazar automáticamente el texto de un comando
por otro texto. Te mostraré dos tipos con potentes posibilidades:
Sustitución de comandos
Una orden se sustituye por su salida.
Sustitución de procesos
Un comando se sustituye por un archivo (más o menos).
Técnica nº 3: Sustitución de órdenes
Supón que tienes unos miles de archivos de texto que representan
canciones. Cada archivo incluye el título de la canción, el nombre del
artista, el título del álbum y la letra de la canción:
Title: Carry On Wayward Son
Artist: Kansas
Album: Leftoverture
Carry on my wayward son
There'll be peace when you are done
⋮
Te gustaría organizar los archivos en subdirectorios por artista. Para
realizar esta tarea a mano, podrías buscar todos los archivos de canciones
por Kansas utilizando grep:
$ grep -l "Artist: Kansas" *.txt
carry_on_wayward_son.txt
dust_in_the_wind.txt
[Link]
y luego mueve cada archivo a un directorio kansas:
$ mkdir kansas
$ mv carry_on_wayward_son.txt kansas
$ mv dust_in_the_wind.txt kansas
$ mv [Link] kansas
Tedioso, ¿verdad? ¿No sería genial poder decirle al shell: "Mueve todos los
archivos que contengan la cadena Artista: Kansas al directoriokansas".
En términos de Linux, te gustaría coger la lista de nombres del
comando grep -l anterior y pasársela a mv. Pues bien, puedes hacerlo
fácilmente con la ayuda de una función del shell llamada sustitución de
comandos:
$ mv $(grep -l "Artist: Kansas" *.txt) kansas
La sintaxis:
$(any command here)
ejecuta el comando dentro del paréntesis y lo sustituye por su salida. Así,
en la línea de comandos anterior, el comando grep -l se sustituye por la
lista de nombres de archivo que imprime, como si hubieras tecleado los
nombres de archivo así:
$ mv carry_on_wayward_son.txt dust_in_the_wind.txt
[Link] kansas
Siempre que te encuentres copiando la salida de un comando en una línea
de comandos posterior, normalmente puedes ahorrar tiempo con la
sustitución de comandos. Incluso puedes incluir alias en la sustitución de
comandos, porque su contenido se ejecuta en un subshell, que incluye
copias de los alias de su padre.
CARACTERES ESPECIALES Y SUSTITUCIÓN DE COMANDOS
El ejemplo anterior con grep -l funciona muy bien para la mayoría de los
nombres de archivo de Linux, pero no para los nombres de archivo que
contienen espacios u otros caracteres especiales. El shell evalúa estos caracteres
antes de pasar la salida a mv, lo que puede producir resultados inesperados. Por
ejemplo, si grep -l imprimiera polvo en el viento. txt, el shell trataría los
espacios como separadores, y mv intentaría mover cuatro archivos inexistentes
llamados polvo, en, el y [Link].
He aquí otro ejemplo. Supón que tienes varios años de extractos bancarios
descargados en formato PDF. Los archivos descargados tienen nombres
que incluyen el año, mes y día del extracto, comoeStmt_2021-08-
[Link] para la fecha 26 de agosto de 2021. 4 Te gustaría ver el extracto más
reciente en el directorio actual. Podrías hacerlo manualmente: listar el
directorio, localizar el archivo con la fecha más reciente (que será el
último archivo del listado), y mostrarlo con un visor de PDF de Linux
como okular. Pero, ¿para qué hacer todo ese trabajo manual? Deja que la
sustitución de comandos te facilite el camino. Crea un comando que
imprima el nombre del último archivo PDF del directorio:
$ ls eStmt*pdf | tail -n1
y proporcionarla a okular utilizando la sustitución de comandos:
$ okular $(ls eStmt*pdf | tail -n1)
El comando ls enumera todos los archivos de sentencias, y tail imprime
sólo el último, como eStmt_2021-[Link]. La sustitución de comandos
coloca ese único nombre de archivo directamente en la línea de
comandos, como si hubieras escrito okular eStmt_2021-[Link].
NOTA
La sintaxis original para la sustitución de comandos era entre comillas
(backquotes). Los dos comandos siguientes son equivalentes:
$ echo Today is $(date +%A).
Today is Saturday.
$ echo Today is `date +%A`.
Today is Saturday.
La mayoría de los intérpretes de comandos admiten los palillos traseros. Sin
embargo, la sintaxis $() es más sencilla de anidar:
$ echo $(date +%A) | tr a-z A-Z Single
SATURDAY
echo Today is $(echo $(date +%A) | tr a-z A-Z)! Nested
Today is SATURDAY!
En los guiones, un uso común de la sustitución de comandos es almacenar
la salida de un comando en una variable:
VariableName=$(some command here)
Por ejemplo, para obtener los nombres de los archivos que contienen las
canciones de Kansas y almacenarlos en una variable, utiliza la sustitución
de comandos de la forma siguiente:
$ kansasFiles=$(grep -l "Artist: Kansas" *.txt)
La salida puede tener varias líneas, así que para conservar los caracteres
de nueva línea, asegúrate de entrecomillar el valor siempre que lo
utilices:
$ echo "$kansasFiles"
Técnica nº 4: Sustitución de procesos
La sustitución de comandos, que acabas de ver, sustituye un comando por
su salida en su lugar, como una cadena. La sustitución de
procesos también sustituye una orden por su salida, pero trata la salida
como si estuviera almacenada en un archivo. Esta poderosa diferencia
puede parecer confusa al principio, así que te la explicaré paso a paso.
Supón que estás en un directorio de archivos de imágenes JPEG llamados
del [Link] [Link], pero a algunos archivos les falta misteriosamente y
quieres identificarlos. Produce dicho directorio con los siguientes
comandos:
$ mkdir /tmp/jpegs && cd /tmp/jpegs
$ touch {1..1000}.jpg
$ rm [Link] [Link]
Una mala forma de localizar los archivos que faltan es hacer una lista del
directorio, ordenada numéricamente, y buscar los huecos a ojo:
$ ls -1 | sort -n | less
[Link]
[Link]
[Link]
⋮
[Link] [Link] is missing
Una solución más sólida y automatizada es comparar los nombres de
archivo existentes con una lista completa de nombres
desde [Link] hasta [Link], utilizando el comandodiff . Una forma de
lograr esta solución es con archivos temporales. Almacena los nombres de
archivo existentes, ordenados, en un archivo temporal,original-list:
$ ls *.jpg | sort -n > /tmp/original-list
A continuación, imprime una lista completa de nombres de archivo
desde [Link] hasta [Link] en otro archivo temporal, lista completa,
generando los números enteros del 1 al 1000 con seq, y añadiendo ".jpg" a
cada línea con sed:
$ seq 1 1000 | sed 's/$/.jpg/' > /tmp/full-list
Compara los dos archivos temporales con el comando diff para descubrir
que faltan [Link] y [Link], y luego elimina los archivos temporales:
$ diff /tmp/original-list /tmp/full-list
3a4
> [Link]
979a981
> [Link]
$ rm /tmp/original-list /tmp/full-list Clean up
afterwards
Son muchos pasos. ¿No sería estupendo comparar las dos listas de
nombres directamente y no molestarse con los archivos temporales? El
problema es que diff no puede comparar dos listas desde stdin; necesita
archivos como argumentos.5 La sustitución de procesos resuelve el
problema. Hace que ambas listas aparezcan en diffcomo archivos. (La
barra lateral "Cómo funciona la sustitución de procesos" proporciona los
detalles técnicos). La sintaxis:
<(any command here)
ejecuta el comando en una subshell y presenta su salida como si estuviera
contenida en un archivo. Por ejemplo, la siguiente expresión representa la
salida de ls -1 | sort -n como si estuviera contenida en un archivo:
<(ls -1 | sort -n)
Puedes cat el archivo:
$ cat <(ls -1 | sort -n)
[Link]
⋮
[Link]
Puedes copiar el archivo con cp:
$ cp <(ls -1 | sort -n) /tmp/listing
$ cat /tmp/listing
[Link]
⋮
[Link]
y como ahora verás, puedes diff el archivo contra otro. Empieza con los
dos comandos que generaron tus dos archivos temporales:
ls *.jpg | sort -n
seq 1 1000 | sed 's/$/.jpg/'
Aplica la sustitución de procesos para que diff pueda tratarlos como
archivos, y obtendrás el mismo resultado que antes, pero sin utilizar
archivos temporales:
$ diff <(ls *.jpg | sort -n) <(seq 1 1000 | sed
's/$/.jpg/')
3a4
> [Link]
979a981
> [Link]
Limpia la salida buscando líneas que empiecen por > y eliminando los dos
primeros caracteres con cut, y ya tienes tu informe de archivos perdidos:
$ diff <(ls *.jpg | sort -n) <(seq 1 1000 | sed
's/$/.jpg/') \
| grep '>' \
| cut -c3-
[Link]
[Link]
La sustitución de procesos transformó mi forma de utilizar la línea de
comandos. Comandos que sólo leían de archivos de disco, de repente
podían leer de stdin. Con la práctica, comandos que antes parecían
imposibles se volvieron fáciles.
CÓMO FUNCIONA LA SUSTITUCIÓN DE PROCESOS
Cuando el sistema operativo Linux abre un archivo de disco, representa
ese archivo con un número entero llamado descriptor de archivo. La
sustitución de procesos imita a un archivo ejecutando un comando y
asociando su salida a un descriptor de archivo, de modo que la salida
parece estar en un archivo de disco desde la perspectiva de los programas
que acceden a él. Puedes ver el descriptor de archivo con echo:
$ echo <(ls)
/dev/fd/63
En este caso, el descriptor de archivo para <(ls) es 63, y se rastrea en el
directorio del sistema /dev/fd.
Dato curioso: stdin, stdout y stderr están representados por los
descriptores de archivo 0, 1 y 2, respectivamente. Por eso la redirección de
stderr tiene la sintaxis 2>.
La expresión <(…) crea un descriptor de archivo para lectura. La
expresión relacionada >(…) crea un descriptor de archivo para escribir,
pero en 25 años nunca la he necesitado.
La sustitución de procesos es una función no POSIX de que puede estar
desactivada en tu shell. Para activar funciones no POSIX en tu shell actual,
ejecutaset +o posix .
Técnicas de comando como cadena
Todo comando es una cadena, pero algunos comandos son más "fibrosos"
que otros. Te mostraré varias técnicas que construyen una cadena, trozo a
trozo, y luego ejecutan la cadena como un comando:
Pasar un comando a bash como argumento
Canalización de comandos a bash en stdin
Enviar comandos a otro host con ssh
Ejecutar una secuencia de comandos con xargs
ADVERTENCIA
Las siguientes técnicas pueden ser arriesgadas porque envían texto invisible a
una shell para su ejecución. Nunca hagas esto a ciegas. Comprende siempre el
texto (y confía en su origen) antes de ejecutarlo. No querrás ejecutar la
cadena "rm -rf $HOME" por error y borrar todos tus archivos.
Técnica nº 5: Pasar un comando como argumento a
bash
bash es un comando normal como cualquier otro, como se explica en"Los
intérpretes de comandos son archivos ejecutables", por lo que puedes
ejecutarlo por su nombre en la línea de comandos. Por defecto, al
ejecutar bash se lanza una shell interactiva para escribir y ejecutar
comandos, como ya has visto. Alternativamente, puedes pasar una orden
a bash como una cadena, mediante la opción -c, y bash ejecutará esa
cadena como una orden y saldrá:
$ bash -c "ls -l"
-rw-r--r-- 1 smith smith 325 Jul 3 17:44 [Link]
¿Por qué es útil? Porque el nuevo proceso bash es un hijo con su propio
entorno, incluyendo un directorio actual, variables con valores, etc.
Cualquier cambio en el shell hijo no afectará a tu shell actualmente en
ejecución. Aquí tienes un comando bash -c que cambia el directorio
a /tmp el tiempo suficiente para borrar un archivo, y luego sale:
$ pwd
/home/smith
$ touch /tmp/badfile Create a
temporary file
$ bash -c "cd /tmp && rm badfile"
$ pwd
/home/smith Current
directory is unchanged
El uso más instructivo y bonito de bash -c, sin embargo, surge cuando
ejecutas determinados comandos como superusuario. Concretamente, la
combinación de sudo y la redirección de entrada/salida produce una
situación interesante (a veces enloquecedora) en la que bash -c es la clave
del éxito.
Supón que quieres crear un archivo de registro en el directorio del
sistema/var/log, que no es escribible por los usuarios normales. Ejecutas
el siguiente comando sudo para obtener privilegios de superusuario y
crear el archivo de registro, pero falla misteriosamente:
$ sudo echo "New log file" > /var/log/[Link]
bash: /var/log/[Link]: Permission denied
Espera un momento-sudo debería darte permiso para crear cualquier
archivo en cualquier lugar. ¿Cómo es posible que falle este comando? ¿Por
qué sudo ni siquiera te pidió una contraseña? La respuesta es:
porque sudo no se ejecutó. Aplicaste sudo al comando echo pero no a la
redirección de salida, que se ejecutó primero y falló. En detalle:
1. Has pulsado Intro.
2. El intérprete de comandos comenzó a evaluar todo el comando,
incluida la redirección (>).
3. El shell intentó crear el archivo [Link] en un directorio
protegido,/var/log.
4. No tenías permiso para escribir en /var/log, por lo que el
intérprete de comandos se dio por vencido e imprimió el
mensaje "Permiso denegado".
Por eso sudo nunca se ejecutó. Para resolver este problema, tienes que
decirle al intérprete de comandos: "Ejecuta todo el comando, incluida la
redirección de salida, como superusuario". Éste es exactamente el tipo de
situación que bash -cresuelve tan bien. Construye el comando que quieres
ejecutar, como una cadena:
'echo "New log file" > /var/log/[Link]'
y pásalo como argumento a sudo bash -c:
$ sudo bash -c 'echo "New log file" > /var/log/[Link]'
[sudo] password for smith: xxxxxxxx
$ cat /var/log/[Link]
New log file
Esta vez, has ejecutado bash, no sólo echo, como superusuario,
ybash ejecuta toda la cadena como un comando. La redirección tiene éxito.
Recuerda esta técnica siempre que combines sudo con la redirección.
Técnica nº 6: Canalizar un comando a bash
El shell lee todos los comandos que escribes en stdin. Eso significa
quebash el programa puede participar en canalizaciones. Por ejemplo,
imprime la cadena "ls -l" y canalízala a bash, y bash tratará la cadena
como una orden y la ejecutará:
$ echo "ls -l"
ls -l
$ echo "ls -l" | bash
-rw-r--r-- 1 smith smith 325 Jul 3 17:44 [Link]
ADVERTENCIA
Recuerda, nunca envíes texto a ciegas a bash. Sé consciente de lo que estás
ejecutando.
Esta técnica es estupenda cuando necesitas ejecutar muchos comandos
similares seguidos. Si puedes imprimir los comandos como cadenas,
entonces puedes canalizar las cadenas a bash para su ejecución. Supón
que estás en un directorio con muchos archivos y quieres organizarlos en
subdirectorios según su primer carácter. Un archivo llamado manzana se
movería al subdirectorio a, un archivo llamado melón se movería al
subdirectorioc, y así sucesivamente.6 (Para simplificar, supondremos que
todos los nombres de archivo empiezan por una letra minúscula y no
contienen espacios ni caracteres especiales).
En primer lugar, lista los archivos, ordenados. Supondremos que todos los
nombres tienen al menos dos caracteres (que coinciden con el patrón ??*)
para que nuestros comandos no choquen con los subdirectorios de la a a
la z:
$ ls -1 ??*
apple
banana
cantaloupe
⋮
carrot
Crea los 26 subdirectorios que necesites mediante la expansión de
corchetes:
$ mkdir {a..z}
Ahora genera los comandos mv que necesitarás, como cadenas. Empieza
con una expresión regular para sed que capture el primer carácter del
nombre del archivo como expresión nº 1 (\1):
^\(.\)
Captura el resto del nombre del archivo como expresión nº 2 (\2):
\(.*\)$
Conecta las dos expresiones regulares:
^\(.\)\(.*\)$
Ahora forma un comando mv con la palabra mv seguida de un espacio, el
nombre completo del archivo (\1\2), otro espacio y el primer carácter (\1):
mv \1\2 \1
El generador de comandos completo es:
$ ls -1 ??* | sed 's/^\(.\)\(.*\)$/mv \1\2 \1/'
mv apple a
mv banana b
mv cantaloupe c
⋮
mv carrot c
Su salida contiene exactamente los comandos mv que necesitas. Lee la
salida para convencerte de que es correcta, quizás pasándola aless para
verla página a página:
$ ls -1 ??* | sed 's/^\(.\)\(.*\)$/mv \1\2 \1/' | less
Cuando estés seguro de que los comandos generados son correctos, envía
la salida a bash para su ejecución:
$ ls -1 ??* | sed 's/^\(.\)\(.*\)$/mv \1\2 \1/' | bash
Los pasos que acabas de completar son un patrón repetible:
1. Imprime una secuencia de comandos manipulando cadenas.
2. Visualiza los resultados con less para comprobar que son
correctos.
3. Envía los resultados a bash.
Técnica nº 7: Ejecutar una cadena remotamente
con ssh
Advertencia: esta técnica sólo tendrá sentido si estás familiarizado con
SSH, el shell seguro, para iniciar sesión en hosts remotos. Establecer
relaciones SSH entre hosts está fuera del alcance de este libro; para saber
más sobre ello, busca untutorial de SSH.
Además de la forma habitual de iniciar sesión en un host remoto:
$ ssh [Link]
también puedes ejecutar un único comando en el host remoto, pasando
una cadena a ssh en la línea de comandos. Sólo tienes que añadir la
cadena al resto de la línea de comandos ssh:
$ ssh [Link] ls
remotefile1
remotefile2
remotefile3
Esta técnica suele ser más rápida que iniciar sesión, ejecutar un comando
y cerrar sesión. Si el comando incluye caracteres especiales, como
símbolos de redirección, que deben ser evaluados en el host remoto,
ponlos entre comillas o escápalos. De lo contrario, serán evaluados por tu
shell local. Los dos comandos siguientes ejecutan ls remotamente, pero la
redirección de la salida se produce en hosts diferentes:
$ ssh [Link] ls > outfile Creates
outfile on local host
$ ssh [Link] "ls > outfile" Creates
outfile on remote host
También puedes enviar comandos a ssh para ejecutarlos en el servidor
remoto, del mismo modo que los envías a bash para ejecutarlos
localmente:
$ echo "ls > outfile" | ssh [Link]
Al canalizar comandos a ssh, el host remoto puede imprimir mensajes de
diagnóstico o de otro tipo. Por lo general, éstos no afectan al comando
remoto, y puedes suprimirlos:
Si ves mensajes sobre pseudoterminales o pseudottys, como "No
se asignará un pseudoterminal porque stdin no es un terminal",
ejecuta ssh con la opción -T para evitar que el servidor SSH
remoto asigne un terminal:
$ echo "ls > outfile" | ssh -T [Link]
Si ves mensajes de bienvenida que aparecen normalmente
cuando te conectas ("¡Bienvenido a Linux!") u otros mensajes no
deseados, intenta decirle explícitamente a sshque ejecute bash en
el host remoto, y los mensajes deberían desaparecer:
$ echo "ls > outfile" | ssh [Link] bash
Técnica 8: Ejecutar una lista de comandos con
xargs
Muchos usuarios de Linux nunca han oído hablar del comando xargs,
pero es una poderosa herramienta para construir y ejecutar múltiples
comandos similares. Aprender xargs fue otro momento transformador en
mi educación sobre Linux, y espero que también en la tuya.
xargs acepta dos entradas:
En stdin: Una lista de cadenas separadas por espacios en blanco.
Un ejemplo son las rutas de archivo producidas por ls o find,
pero cualquier cadena servirá. Las llamaré cadenas de
entrada.
En la línea de comandos: Un comando incompleto al que le
faltan algunos argumentos, al que llamaré plantilla de
comando.
xargs fusiona las cadenas de entrada y la plantilla de comandos para
producir y ejecutar nuevos comandos completos, a los que
llamaré comandos generados. Demostraré este proceso con un ejemplo
de juguete. Supón que estás en un directorio con tres archivos:
$ ls -1
apple
banana
cantaloupe
Pasa el listado de directorios a xargs para que sirva como cadena de
entrada, y proporciona wc -l para que sirva como plantilla del comando,
de esta forma
$ ls -1 | xargs wc -l
3 apple
4 banana
1 cantaloupe
8 total
Como prometí, xargs aplicó la plantilla de comandos wc -l a las cadenas
de entrada y cuenta las líneas de cada archivo. Para imprimir los mismos
tres archivos con cat, basta con cambiar la plantilla de comandos por
"cat":
$ ls -1 | xargs cat
Mis ejemplos de juguete con xargs tienen dos defectos, uno fatal y otro
práctico. El defecto fatal es que xargs puede hacer algo incorrecto si una
cadena de entrada contiene caracteres especiales, como espacios. Hay una
solución robusta en la barra lateral "Seguridad con find y xargs".
El inconveniente práctico es que aquí no necesitas xargs; puedes realizar
las mismas tareas de forma más sencilla con la concordancia de patrones
de archivos:
$ wc -l *
3 apple
4 banana
1 cantaloupe
8 total
Entonces, ¿por qué utilizar xargs? Su potencia se hace evidente cuando las
cadenas de entrada son más interesantes que un simple listado de
directorios. Supón que quieres contar las líneas de todos los archivos de
un directorio y de todos sus subdirectorios (recursivamente), pero sólo
de los archivos fuente de Python cuyos nombres terminen en .py. Es fácil
producir una lista de rutas de archivos de este tipo con find:
$ find . -type f -name \*.py -print
fruits/[Link]
⋮
vegetables/leafy/[Link]
xargs ahora puede aplicar la plantilla de comandos wc -l a cada ruta de
archivo, produciendo un resultado recursivo que sería difícil de obtener
de otro modo. Por seguridad, sustituiré la opción -print por -
print0, yxargs por xargs -0, por las razones explicadas en la barra
lateral"Seguridad con find y xargs":
$ find . -type f -name \*.py -print0 | xargs -0 wc -l
6 ./fruits/[Link]
⋮
3 ./vegetables/leafy/[Link]
Combinando find y xargs, puedes hacer que cualquier comando se ejecute
recursivamente a través del sistema de archivos, afectando sólo a los
archivos (y/o directorios) que coincidan con tus criterios establecidos. (En
algunos casos, puedes producir el mismo efecto con find solo, utilizando
su opción -exec,pero xargs suele ser una solución más limpia).
xargs tiene numerosas opciones (véase man xargs) que controlan cómo
crea y ejecuta los comandos generados. En mi opinión, las más
importantes (aparte de -0) son -n y -I. La opción -n controla cuántos
argumentos añade xargs a cada comando generado. El comportamiento
por defecto es añadir tantos argumentos como quepan dentro de los
límites del intérprete de comandos:7
$ ls | xargs echo Fit as many input
strings as possible:
apple banana cantaloupe carrot echo apple banana
cantaloupe carrot
$ ls | xargs -n1 echo One argument per
echo command:
apple echo apple
banana echo banana
cantaloupe echo cantaloupe
carrot echo carrot
$ ls | xargs -n2 echo Two arguments per
echo command:
apple banana echo apple banana
cantaloupe carrot echo cantaloupe
carrot
$ ls | xargs -n3 echo Three arguments per
echo command:
apple banana cantaloupe echo apple banana
cantaloupe
carrot echo carrot
SEGURIDAD CON FIND Y XARGS
Cuando combine find y xargs, utiliza xargs -0 (guión cero) en lugar
de xargssolo para protegerte contra caracteres especiales inesperados en
las cadenas de entrada. Emparéjala con la salida producida por find -
print0 (en lugar de find -print):
$ find options... -print0 | xargs -0 options...
Normalmente, xargs espera que sus cadenas de entrada estén separadas
por espacios en blanco, como los caracteres de nueva línea. Esto es un
problema cuando las propias cadenas de entrada contienen otros espacios
en blanco, como nombres de archivo con espacios. Por
defecto, xargs tratará esos espacios como separadores de entrada y
operará con cadenas incompletas, produciendo resultados incorrectos.
Por ejemplo, si la entrada a xargs incluye una línea higo
[Link], xargs la tratará como dos cadenas de entrada, y es probable
que veas un error como éste:
prickly: No such file or directory
[Link]: No such file or directory
Para evitar este problema, utiliza xargs -0 (es un cero) para aceptar un
carácter diferente como separador de entrada, a saber, el carácter
nulo(cero ASCII). Los nulos rara vez aparecen en el texto, por lo que son
separadores ideales e inequívocos para las cadenas de entrada.
¿Cómo puedes separar tus cadenas de entrada con nulos en lugar de
nuevas líneas? Afortunadamente, find tiene una opción para hacer
exactamente eso:-print0, en lugar de -print.
Lamentablemente, el comando ls no tiene una opción para separar su
salida con nulos, por lo que mis anteriores ejemplos de juguete con ls no
son seguros. Puedes convertir las nuevas líneas en nulos con tr:
$ ls | tr '\n' '\0' | xargs -0 ...
O utiliza este práctico alias que enumera el directorio actual con entradas
separadas por nulos, adecuado para pasar a xargs:
alias ls0="find . -maxdepth 1 -print0"
La opción -I controla dónde aparecen las cadenas de entrada en el
comando generado. Por defecto, se añaden a la plantilla del comando,
pero puedes hacer que aparezcan en otro lugar. Sigue -I con cualquier
cadena (de tu elección), y esa cadena se convertirá en un marcador de
posición en la plantilla de comandos, indicando exactamente dónde
deben insertarse las cadenas de entrada:
$ ls | xargs -I XYZ echo XYZ is my favorite food Use
XYZ as a placeholder
apple is my favorite food
banana is my favorite food
cantaloupe is my favorite food
carrot is my favorite food
Elegí "XYZ" arbitrariamente como marcador de posición para las cadenas
de entrada y lo coloqué inmediatamente después de echo, desplazando la
cadena de entrada al principio de cada línea de salida. Ten en cuenta que
la opción -I limita xargs a una cadena de entrada por comando generado.
Te recomiendo que leas detenidamente la página de manual xargspara
saber qué más puedes controlar.
LISTAS LARGAS DE ARGUMENTOS
xargs es un solucionador de problemas cuando las líneas de comandos se hacen
muy largas. Supón que tu directorio actual contiene un millón de archivos
llamados [Link] [Link] e intentas eliminarlos
mediante la concordancia de patrones:
$ rm *.txt
bash: /bin/rm: Argument list too long
El patrón *.txt se evalúa como una cadena de más de 14 millones de caracteres,
que es más larga de lo que admite Linux. Para evitar esta limitación, envía una
lista de archivos a xargs para que los elimine. xargs dividirá la lista de archivos
en varios comandos rm. Forma la lista de archivos enviando un listado completo
de directorios a grep, buscando sólo los nombres de archivo que terminen
en .txt, y luego envíalo axargs:
$ ls | grep '\.txt$' | xargs rm
Esta solución es mejor que la concordancia de patrones de archivos (ls *.txt),
que producirá el mismo error "Lista de argumentos demasiado larga". Mejor
aún, ejecuta find -print0 como se describe en "Seguridad con find y xargs":
$ find . -maxdepth 1 -name \*.txt -type f -print0 \
| xargs -0 rm
Técnicas de control de procesos
Hasta ahora, todos los comandos de los que he hablado ocupan el shell
padre hasta que terminan. Consideremos varias técnicas que forjan una
relación diferente con el shell padre:
Comandos de fondo
Devuelve inmediatamente el aviso y ejecútalo fuera de la vista
Subshells explícitos
Puede lanzarse en medio de un comando combinado
Sustitución del proceso
Sustituye al caparazón padre
Técnica nº 9: Colocar un comando en segundo
plano
Hasta ahora, todas nuestras técnicas ejecutan un comando hasta su
finalización mientras esperas, y luego presentan el siguiente prompt del
shell. Pero no tienes por qué esperar, sobre todo en el caso de los
comandos que requieren mucho tiempo. Puedes lanzar comandos de una
forma especial para que desaparezcan de la vista (más o menos) pero
sigan ejecutándose, liberando inmediatamente el intérprete de comandos
actual para ejecutar otros comandos. Esta técnica se denomina ejecutar
un comando en segundo plano. En cambio, los comandos que ocupan el
shell se denominan comandos en primer plano. Una instancia del shell
ejecuta como máximo un comando en primer plano a la vez más
cualquier número de comandos en segundo plano.
Lanzar un comando en segundo plano
Para ejecutar un comando en segundo plano, simplemente añade un
ampersand (&).El shell responde con un mensaje de aspecto críptico que
indica que el comando está en segundo plano y presenta el siguiente
prompt:
$ wc -c my_extremely_huge_file.txt & Count characters
in a huge file
[1] 74931 Cryptic-looking
response
$
A continuación, puedes seguir ejecutando comandos en primer plano (o
más comandos en segundo plano) en este intérprete de comandos. La
salida de los comandos en segundo plano puede aparecer en cualquier
momento, incluso mientras estás escribiendo. Si el comando en segundo
plano finaliza con éxito, el intérprete de comandos te informará con un
mensaje Done:
59837483748 my_extremely_huge_file.txt
[1]+ Done wc -c my_extremely_huge_file.txt
o si falla, verás un mensaje de salida con un código de salida:
[1]+ Exit 1 wc -c my_extremely_huge_file.txt
CONSEJO
El ampersand es también un operador de lista, como && y ||:
$ command1 & command2 & command3 & All 3 commands
[1] 57351 in background
[2] 57352
[3] 57353
$ command4 & command5 & echo hi All in background
[1] 57431 but "echo"
[2] 57432
hi
Suspender un comando y enviarlo al fondo
Una técnica relacionada consiste en ejecutar un comando en primer
plano, cambiar de opinión durante su ejecución y enviarlo a segundo
plano. Pulsa Ctrl-Z para detener temporalmente el comando (lo que se
denomina suspender el comando) y volver al prompt del shell; a
continuación, escribe bg para reanudar la ejecución del comando en
segundo plano.
Trabajos y control de trabajos
Los comandos en segundo plano forman parte de una función del shell
llamada control de trabajosque manipula los comandos en ejecución de
varias formas, como ponerlos en segundo plano, suspenderlos y
reanudarlos. Un trabajo es la unidad de trabajo de un intérprete de
comandos: una única instancia de un comando que se ejecuta en un
intérprete de comandos. Los comandos simples, las canalizaciones y las
listas condicionales son ejemplos de trabajos: básicamente, cualquier cosa
que puedas ejecutar en la línea de comandos.
Un trabajo es más que un proceso Linux. Un trabajo puede consistir en un
proceso, dos procesos o más. Una tubería de seis programas, por ejemplo,
es un único trabajo que incluye (al menos) seis procesos. Los trabajos son
una construcción del shell. El sistema operativo Linux no lleva la cuenta
de los trabajos, sólo de los procesos subyacentes.
En cualquier momento, un shell puede tener varios trabajos en ejecución.
Cada trabajo de un shell determinado tiene un ID entero positivo, llamado
ID de trabajo o número de trabajo. Cuando ejecutas un comando en
segundo plano, el shell imprime el número de trabajo y el ID del primer
proceso que ejecuta dentro del trabajo. En el siguiente comando, el
número de trabajo es 1 y el ID del proceso es 74931:
$ wc -c my_extremely_huge_file.txt &
[1] 74931
Operaciones habituales de control de trabajos
El shell tiene comandos integrados para controlar los trabajos, que se
enumeran enla Tabla 7-1. Demostraré las operaciones de control de
trabajos más comunes ejecutando un montón de trabajos y
manipulándolos. Para que los trabajos sean sencillos y predecibles,
ejecutaré el comando sleep, que se queda ahí sin hacer nada
("durmiendo") durante un número determinado de segundos y luego sale.
Por ejemplo, sleep 10 duerme durante 10 segundos.
Mando Significado
bg Mover el trabajo suspendido actual a un segundo
plano
bg %n Mueve el trabajo suspendido número n al fondo
(ejemplo: bg %1)
Mando Significado
fg Mueve el trabajo actual en segundo plano al primer
plano
fg %n Mueve el trabajo de fondo número n al primer
plano (ejemplo: fg %2)
matar %n Finaliza el trabajo de fondo
número n (ejemplo: kill %3)
puestos de Ver los trabajos de un proyectil
trabajo
Tabla 7-1. Comandos de control de trabajos
Ejecuta un trabajo en segundo plano hasta completarlo:
$ sleep 20 & Run in the background
[1] 126288
$ jobs List this shell's jobs
[1]+ Running sleep 20 &
$
...eventually...
[1]+ Done sleep 20
NOTA
Cuando se completen los trabajos, es posible que el mensaje Listo no aparezca
hasta la próxima vez que pulses Intro.
Ejecuta un trabajo en segundo plano y tráelo al primer plano:
$ sleep 20 & Run in the background
[1] 126362
$ fg Bring into the
foreground
sleep 20
...eventually...
$
Ejecuta un trabajo en primer plano, suspéndelo y vuelve a ponerlo en
primer plano:
$ sleep 20 Run in the foreground
^Z Suspend the job
[1]+ Stopped sleep 20
$ jobs List this shell's jobs
[1]+ Stopped sleep 20
$ fg Bring into the
foreground
sleep 20
...eventually...
[1]+ Done sleep 20
Ejecuta un trabajo en primer plano y envíalo a segundo plano:
$ sleep 20 Run in the foreground
^Z Suspend the job
[1]+ Stopped sleep 20
$ bg Move to the background
[1]+ sleep 20 &
$ jobs List this shell's jobs
[1]+ Running sleep 20 &
$
...eventually...
[1]+ Done sleep 20
Trabaja con varios trabajos en segundo plano. Refiérete a un trabajo por
su número de trabajoprecedido de un signo de porcentaje (%1, %2, y así
sucesivamente):
$ sleep 100 & Run 3 commands in the
background
[1] 126452
$ sleep 200 &
[2] 126456
$ sleep 300 &
[3] 126460
$ jobs List this shell's
jobs
[1] Running sleep 100 &
[2]- Running sleep 200 &
[3]+ Running sleep 300 &
$ fg %2 Bring job 2 into the
foreground
sleep 200
^Z Suspend job 2
[2]+ Stopped sleep 200
$ jobs See job 2 is
suspended ("stopped")
[1] Running sleep 100 &
[2]+ Stopped sleep 200
[3]- Running sleep 300 &
$ kill %3 Terminate job 3
[3]+ Terminated sleep 300
$ jobs See job 3 is gone
[1]- Running sleep 100 &
[2]+ Stopped sleep 200
$ bg %2 Resume suspended job
2 in the background
[2]+ sleep 200 &
$ jobs See job 2 is running
again
[1]- Running sleep 100 &
[2]+ Running sleep 200 &
$
Salida y entrada en segundo plano
Un comando en segundo plano puede escribir en stdout, a veces en
momentos inconvenientes o confusos. Observa lo que ocurre si ordenas el
archivo diccionario de Linux (de 100.000 líneas de longitud) e imprimes
las dos primeras líneas en segundo plano. Como era de esperar, el shell
imprime inmediatamente el número de trabajo (1), un ID de proceso
(81089) y el siguiente prompt:
$ sort /usr/share/dict/words | head -n2 &
[1] 81089
$
Si esperas a que termine el trabajo, imprime dos líneas en stdout,
dondequiera que esté el cursor en ese momento. En este caso, el cursor
está situado en el segundo indicador, por lo que obtendrás esta salida de
aspecto descuidado:
$ sort /usr/share/dict/words | head -n2 &
[1] 81089
$ A
A's
Pulsa Intro, y el intérprete de comandos imprimirá un mensaje de
"trabajo realizado":
[1]+ Done sort /usr/share/dict/words | head
-n2
La salida de pantalla de un trabajo en segundo plano puede aparecer en
cualquier momento mientras se ejecuta el trabajo. Para evitar este tipo de
desorden, redirige stdout a un archivo y examínalo cuando quieras:
$ sort /usr/share/dict/words | head -n2 > /tmp/results &
[1] 81089
$
[1]+ Done sort /usr/share/dict/words | head
-n2 > /tmp/results
$ cat /tmp/results
A
A's
$
Ocurren otras cosas raras cuando un trabajo en segundo plano intenta
leer de stdin. El intérprete de comandos suspende el trabajo, imprime un
mensaje "Detenido" y espera la entrada en segundo plano. Demuéstralo
poniendo en segundo plano catsin argumentos para que lea la entrada
estándar:
$ cat &
[1] 82455
[1]+ Stopped cat
Los trabajos no pueden leer la entrada en segundo plano, así que pon el
trabajo en primer plano con fg y luego suministra la entrada:
$ fg
cat
Here is some input
⋮
Here is some input
Después de suministrar todos los datos, haz una de las siguientes
acciones:
Continúa ejecutando el comando en primer plano hasta que se
complete.
Suspende y vuelve a poner en segundo plano el comando
pulsando Ctrl-Z seguido de bg.
Finaliza la entrada con Ctrl-D, o mata el comando con Ctrl-C .
Consejos de fondo
La ejecución en segundo plano es ideal para comandos que tardan mucho
en ejecutarse, como los editores de texto durante largas sesiones de
edición, o cualquier programa que abra sus propias ventanas. Por
ejemplo, los programadores pueden ahorrar mucho tiempo suspendiendo
su editor de texto en lugar de salir. He visto a ingenieros experimentados
modificar algún código en su editor de texto, guardar y salir del editor,
probar el código, luego volver a lanzar el editor y buscar el lugar del
código donde lo habían dejado. Pierden entre 10 y 15 segundos en
cambiar de trabajo cada vez que salen del editor. Si en cambio suspenden
el editor (Ctrl-Z), prueban su código y reanudan el editor ( fg), evitan
perder tiempo innecesariamente.
La ejecución en segundo plano también es ideal para ejecutar una
secuencia de comandos en segundo plano utilizando una lista
[Link] falla algún comando de la lista, el resto no se ejecutará y el
trabajo se completará. (Sólo ten cuidado con los comandos que leen la
entrada, ya que harán que el trabajo se suspenda y espere la entrada).
$ command1 && command2 && command3 &
Técnica nº 10: Subconjuntos explícitos
Cada vez que lanzas un comando simple, se ejecuta en un proceso hijo,
como viste en "Procesos padre e hijo". La sustitución de comandos y la
sustitución de procesos crean subshells. Sin embargo, hay ocasiones en las
que resulta útil lanzar explícitamente una subshell adicional. Para ello,
basta con encerrar un comando entre paréntesisy se ejecutará en una
subshell:
$ (cd /usr/local && ls)
bin etc games lib man sbin share
$ pwd
/home/smith "cd /usr/local" occurred in
a subshell
Cuando se aplica a un comando completo, esta técnica no es superútil,
excepto quizá para ahorrarte ejecutar un segundo comando cd para
volver a tu directorio anterior. Sin embargo, si colocas paréntesis
alrededor de una parte de un comando combinado, puedes realizar
algunos trucos útiles. Un ejemplo típico es una tubería que cambia de
directorioen mitad de la ejecución. Supongamos que has descargado un
archivo comprimido tar ,[Link], y quieres extraer los archivos.
Un comando tar para extraer los archivos es:
$ tar xvf [Link]
Makefile
src/
src/defs.h
⋮
src/main.c
La extracción se produce en relación al directorio actual.8 ¿Qué ocurre si
quieres extraerlos en un directorio distinto? Podrías cd al otro directorio
primero y ejecutar tar(y luego cd de vuelta), pero también puedes realizar
esta tarea con un solo comando. El truco consiste en canalizar los datos
targeteados a una subshell que realice las operaciones de directorio y
ejecute tar mientras lee de stdin:9
$ cat [Link] | (mkdir -p /tmp/other && cd
/tmp/other && tar xzvf -)
Esta técnica también funciona para copiar archivos de un
directorio dir1 a otro directorio existente dir2 utilizando dos procesos tar,
uno escribiendo a stdout y otro leyendo de stdin:
$ tar czf - dir1 | (cd /tmp/dir2 && tar xvf -)
La misma técnica puede copiar archivos a un directorio existente en otro
host a través de SSH:
$ tar czf - dir1 | ssh myhost '(cd /tmp/dir2 && tar xvf -)'
¿QUÉ TÉCNICAS CREAN SUBCÁPSULAS?
Muchas de las técnicas de este capítulo lanzan un subshell, quehereda el
entorno del padre (variables y sus valores) además de otro contexto del
shell, como los alias. Otras técnicas sólo lanzan un proceso hijo. La forma
más sencilla de distinguirlos es evaluar la variable BASH_SUBSHELL, que será
distinta de cero para un subshell y cero en caso [Link]ás
más detalles en "Child Shells Versus Subshells".
$ echo $BASH_SUBSHELL Ordinary execution
0 Not a subshell
$ (echo $BASH_SUBSHELL) Explicit subshell
1 Subshell
$ echo $(echo $BASH_SUBSHELL) Command
substitution
1 Subshell
$ cat <(echo $BASH_SUBSHELL) Process
substitution
1 Subshell
$ bash -c 'echo $BASH_SUBSHELL' bash -c
0 Not a subshell
ADVERTENCIA
Es tentador ver los paréntesis de bash como si simplemente agruparan órdenes,
como los paréntesis en aritmética. Pero no es así. Cada par de paréntesis hace
que se lance un subcomando.
Técnica nº 11: Sustitución de procesos
Normalmente, cuando ejecutas un comando, el shell lo ejecuta en un
proceso separado que se destruye cuando el comando sale, como se
describe en"Procesos padre e hijo". Puedes cambiar este comportamiento
con el comando exec , que es unafunción del shell. Sustituye el shell en
ejecución (un proceso) por otro comando de tu elección (otro proceso).
Cuando el nuevo comando salga, no aparecerá ningún indicador del
intérprete de comandos, porque el intérprete de comandos original ha
desaparecido.
Para demostrarlo, ejecuta manualmente un nuevo intérprete de
comandos y cambia su indicador:
$ bash Run a child shell
$ PS1="Doomed> " Change the new shell's prompt
Doomed> echo hello Run any command you like
hello
Ahora exec un comando y observa cómo muere la nueva shell:
Doomed> exec ls ls replaces the child shell,
runs, and exits
[Link]
$ A prompt from the original
(parent) shell
EJECUTAR PUEDE SER FATAL
Si ejecutas exec en un intérprete de comandos, éste se cierra. Si el intérprete de
comandos se ejecutaba en una ventana de terminal, la ventana se cierra. Si el
intérprete de comandos era un intérprete de comandos de inicio de sesión, se
cerrará tu sesión.
¿Por qué ibas a ejecutar exec? Una razón es conservar recursos al no
lanzar un segundo proceso. Las secuencias de comandos Shell a veces
aprovechan esta optimización ejecutando exec en la última orden de la
secuencia de comandos. Si el script se ejecuta muchas veces (digamos,
millones o miles de millones de ejecuciones), el ahorro puede merecer la
pena.
exec tiene una segunda habilidad: puede reasignar stdin, stdout, y/o
stderr para la shell actual. Esto es más práctico en un script de shell, como
este ejemplo de juguete que imprime información en un
archivo,/tmp/outfile:
#!/bin/bash
echo "My name is $USER" >
/tmp/outfile
echo "My current directory is $PWD" >>
/tmp/outfile
echo "Guess how many lines are in the file /etc/hosts?" >>
/tmp/outfile
wc -l /etc/hosts >>
/tmp/outfile
echo "Goodbye for now" >>
/tmp/outfile
En lugar de redirigir la salida de cada comando a /tmp/fichero
de salidaindividualmente, utiliza exec para redirigir stdout
a /tmp/fichero de salida para todo el script. Los comandos posteriores
pueden simplemente imprimirse en stdout:
#!/bin/bash
# Redirect stdout for this script
exec > /tmp/outfile2
# All subsequent commands print to /tmp/outfile2
echo "My name is $USER"
echo "My current directory is $PWD"
echo "Guess how many lines are in the file /etc/hosts?"
wc -l /etc/hosts
echo "Goodbye for now"
Ejecuta este script y examina el archivo /tmp/outfile2 para ver los
resultados:
$ cat /tmp/outfile2
My name is smith
My current directory is /home/smith
Guess how many lines are in the file /etc/hosts?
122 /etc/hosts
Goodbye for now
Probablemente no utilices exec a menudo, pero está ahí cuando lo
necesitas.
Resumen
Ahora tienes 13 técnicas para ejecutar un comando: las 11 de este capítulo
más los comandos simples y las canalizaciones. La Tabla 7-2repasa
algunos casos de uso habitual de las distintas técnicas.
Problema Solución
Enviar stdout de un programa a Tuberías
stdin de otro
Insertar la salida (stdout) en un Sustitución de comandos
comando
Proporcionar salida (stdout) a un Sustitución de procesos
comando que no lee de stdin, pero
sí de archivos de disco
Ejecutar una cadena como bash -co tuberías a bash
comando
Imprimir varios comandos en Tuberías a bash
stdout y ejecutarlos
Ejecutar muchos comandos xargso construyendo los
similares seguidos comandos como cadenas y
enviándolas a bash
Gestionar mandos que dependen Listas condicionales
unos del éxito de otros
Problema Solución
Ejecutar varios comandos a la vez En segundo plano
Ejecutar varios comandos a la vez Poner en segundo plano una
que dependen del éxito de los lista condicional
demás
Ejecutar un comando en un host Ejecuta ssh host command
remoto
Cambiar de directorio en medio de Subshells explícitos
una tubería
Ejecutar un comando más tarde Lista incondicional
con sleep seguida de la orden
Redirigir a/desde archivos Ejecuta sudo bash -c
protegidos "command >file"
Tabla 7-2. Modismos habituales para ejecutar comandos
Los dos capítulos siguientes te enseñarán a combinar técnicas para
alcanzar los objetivos empresariales con eficacia.
1 El comando mkdir -p dir, que crea una ruta de directorio sólo si no
existe ya, sería una solución más elegante en este caso.
2 Este comportamiento es opuesto a lo que hacen muchos lenguajes de
programación, donde cero significa fallo.
3 Alternativamente, podrías utilizar cron para el trabajo de copia de
seguridad y at para los recordatorios, pero Linux se basa en la
flexibilidad: encontrar múltiples formas de conseguir el mismo resultado.
4 Los archivos de extractos descargables de Bank of America se
denominan así en el momento de la impresión.
5 Técnicamente, diff puede leer una lista de stdin si proporcionas un
guión como nombre de archivo, pero no dos listas.
6 Esta estructura de directorios es similar a una tabla hash con
encadenamiento.
7 El número exacto depende de los límites de longitud de tu sistema Linux;
consulta man xargs.
8 Suponiendo que el archivo tar se construyera con rutas relativas -lo que
es típico del software descargado- y no con rutas absolutas.
9 Este problema concreto puede resolverse de forma más sencilla con la
opción tar -C o --directory, que especifica un directorio de destino. Sólo
estoy demostrando la técnica general de utilizar una subshell.
Capítulo 8. Construir una frase
descarada
Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y
comentarios: translation-feedback@[Link]
¿Recuerdas esta larga e intrincada orden del prefacio?
$ paste <(echo {1..10}.jpg | sed 's/ /\n/g') \
<(echo {0..9}.jpg | sed 's/ /\n/g') \
| sed 's/^/mv /' \
| bash
Este tipo de conjuros mágicos se denominan conjuros de una sola
línea.1 Vamos a desmontarlo para entender qué hace y cómo funciona.
Los comandos más internos de echo utilizan la expansión de llaves para
generar listas de nombres de archivos JPEG:
$ echo {1..10}.jpg
[Link] [Link] [Link] ... [Link]
$ echo {0..9}.jpg
[Link] [Link] [Link] ... [Link]
Al pasar los nombres de archivo a sed se sustituyen los caracteres de
espacio por nuevas líneas:
$ echo {1..10}.jpg | sed 's/ /\n/g'
[Link]
⋮
[Link]
[Link]
$ echo {0..9}.jpg | sed 's/ /\n/g'
[Link]
⋮
[Link]
[Link]
El comando paste imprime las dos listas una al lado de la otra. La
sustitución de procesos permite a paste leer las dos listas como si fueran
archivos:
$ paste <(echo {1..10}.jpg | sed 's/ /\n/g') \
<(echo {0..9}.jpg | sed 's/ /\n/g')
[Link] [Link]
⋮
[Link] [Link]
[Link] [Link]
Anteponiendo mv a cada línea se imprime una secuencia de cadenas que
son comandos demv:
$ paste <(echo {1..10}.jpg | sed 's/ /\n/g') \
<(echo {0..9}.jpg | sed 's/ /\n/g') \
| sed 's/^/mv /'
mv [Link] [Link]
⋮
mv [Link] [Link]
mv [Link] [Link]
Ahora se revela el propósito del comando: genera 10 comandos para
renombrar los archivos de imagen del [Link] al [Link]. Los nuevos
nombres [Link] a [Link], respectivamente. Al enviar la salida a bashse
ejecutan los comandos de mv:
$ paste <(echo {1..10}.jpg | sed 's/ /\n/g') \
<(echo {0..9}.jpg | sed 's/ /\n/g') \
| sed 's/^/mv /' \
| bash
Las frases ingeniosas son como los rompecabezas. Te enfrentas a un
problema empresarial, como renombrar un conjunto de archivos, y
aplicas tu caja de herramientas para construir un comando Linux que lo
resuelva. Las frases ingeniosas desafían tu creatividad y desarrollan tus
habilidades.
En este capítulo, crearás descaradas frases como la anterior, paso a paso,
utilizando la siguiente fórmula mágica:
[Link] un comando que resuelva una pieza del puzzle.
[Link] el comando y comprueba el resultado.
[Link] el comando del historial y modifícalo.
[Link] los pasos 2 y 3 hasta que el comando produzca el
resultado deseado.
Este capítulo hará que tu cerebro trabaje duro. Prepárate para sentirte
desconcertado a veces por los ejemplos. Ve paso a paso y ejecuta los
comandos en un ordenador a medida que los leas.
NOTA
Algunas frases de una línea de este capítulo son demasiado largas para una sola
línea, así que las he dividido en varias líneas utilizando barras invertidas. Sin
embargo, no las llamamos frases de dos líneas (o de siete líneas).
Prepárate para ser descarado
Antes de lanzarte a crear frases descaradas, tómate un momento para
adoptar la mentalidad adecuada:
Sé flexible.
Piensa por dónde empezar.
Conoce tus herramientas de comprobación.
Discutiré cada una de estas ideas por separado.
Sé flexible
La clave para escribir frases descaradas es la flexibilidad. A estas alturas
ya has aprendido algunas herramientas increíbles: un conjunto básico de
programas de Linux (y un sinfín de formas de ejecutarlos) junto con el
historial de comandos, la edición de la línea de comandos y mucho más.
Puedes combinar estas herramientas de muchas maneras, y un problema
dado suele tener múltiples soluciones.
Incluso las tareas más sencillas de Linux pueden realizarse de muchas
maneras. Considera cómo podrías [Link] en tu directorio
actual. Apuesto a que el 99,9% de los usuarios de Linux ejecutarían un
comando como éste:
$ ls *.jpg
Pero ésta es sólo una solución entre muchas otras. Por ejemplo, podrías
hacer una lista de todos losarchivos del directorio y luego
utilizar grep para que sólo coincidan los nombres que terminen en .jpg:
$ ls | grep '\.jpg$'
¿Por qué elegir esta solución? Bueno, ya viste un ejemplo en"Listas largas
de argumentos", cuando un directorio contenía tantos archivos que no se
podían listar mediante la concordancia de patrones. La técnica debuscar
la extensión de un archivo en es un método robusto y general para
resolver todo tipo de problemas. Lo importante aquí es ser flexible y
conocer tus herramientas para poder aplicar la mejor en el momento en
que la necesites. Esa es la habilidad de un mago a la hora de crear frases
descaradas.
Todos los comandos siguientes listan los archivos .jpg del directorio
actual. Intenta averiguar cómo funciona cada comando:
$ echo $(ls *.jpg)
$ bash -c 'ls *.jpg'
$ cat <(ls *.jpg)
$ find . -maxdepth 1 -type f -name \*.jpg -print
$ ls > tmp && grep '\.jpg$' tmp && rm -f tmp
$ paste <(echo ls) <(echo \*.jpg) | bash
$ bash -c 'exec $(paste <(echo ls) <(echo \*.jpg))'
$ echo 'monkey *.jpg' | sed 's/monkey/ls/' | bash
$ python -c 'import os; [Link]("ls *.jpg")'
¿Los resultados son idénticos o algunos comandos se comportan de forma
un poco diferente? ¿Se te ocurren otros comandos adecuados?
Piensa por dónde empezar
Todo comando de una sola línea comienza con la salida de un comando
simple. Esa salida puede ser el contenido de un archivo, parte de un
archivo, un listado de directorios, una secuencia de números o letras, una
lista de usuarios, una fecha y hora, u otros datos. Tu primer reto, por
tanto, es producir los datos iniciales para tu comando.
Por ejemplo, si quieres conocer la 17ª letra del alfabeto inglés,, tus datos
iniciales podrían ser 26 letras producidas por expansión de llaves:
$ echo {A..Z}
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
Una vez que puedas producir este resultado, el siguiente paso es decidir
cómo masajearlo para que se ajuste a tu objetivo. ¿Necesitas dividir el
resultado por filas o columnas? ¿Unir el resultado con otra información?
¿Transformar el resultado de una forma más complicada? Busca los
programas de los Capítulos 1 y 5 para hacer ese trabajo,
comogrep y sed y cut, y aplícalos utilizando las técnicas delCapítulo 7.
Para este ejemplo, podrías imprimir el 17º campo con awk, o eliminar los
espacios con sed y localizar el 17º carácter con cut:
$ echo {A..Z} | awk '{print $(17)}'
Q
$ echo {A..Z} | sed 's/ //g' | cut -c17
Q
Como otro ejemplo, si quieres imprimir los meses del año, tus datos
iniciales podrían ser los números del 1 al 12, de nuevo producidos por
expansión de llaves:
$ echo {1..12}
1 2 3 4 5 6 7 8 9 10 11 12
A partir de ahí, aumenta la expansión de llaves para que forme fechas
para el primer día de cada mes (desde 2021-01-01 hasta 2021-12-01); luego
ejecuta date -d en cada línea para producir los nombres de los meses:
$ echo 2021-{01..12}-01 | xargs -n1 date +%B -d
January
February
⋮
March
December
O, supongamos que quieres saber la longitud del nombre de archivo más
largo del directorio actual. Tus datos iniciales podrían ser un listado de
directorios:
$ ls
[Link] [Link] ... [Link]
A partir de ahí, utiliza awk para generar comandos que cuenten los
caracteres de cada nombre de archivo con wc -c:
$ ls | awk '{print "echo -n", $0, "| wc -c"}'
echo -n "[Link]" | wc -c
⋮
echo -n "[Link] | wc -c"
echo -n "[Link] | wc -c"
(La opción -n impide que echo imprima caracteres de nueva línea,que
desvirtuarían en uno cada recuento). Por último, canaliza los comandos
a bash para ejecutarlos, ordena los resultados numéricos de mayor a
menor, y coge el valor máximo (la primera línea) con head -n1:
$ ls | awk '{print "echo -n", $0, "| wc -c"}' | bash | sort
-nr | head -n1
23
Este último ejemplo era complicado, ya que generaba canalizaciones
como cadenas y las pasaba a otra canalización. No obstante, el principio
general es el mismo: averigua tus datos de partida y manipúlalos para
adaptarlos a tus necesidades.
Conoce tus herramientas de comprobación
Construir una frase descarada puede requerir ensayo y error. Las
siguientes herramientas y técnicas te ayudarán a probar diferentes
soluciones rápidamente:
Utiliza el historial de comandos y la edición de la línea de comandos.
No vuelvas a escribir comandos mientras experimentas. Utiliza las
técnicas delCapítulo 3 para recuperar órdenes anteriores,
modificarlas y ejecutarlas.
Añade echo para probar tus expresiones.
Si no estás seguro de cómo se evaluará una expresión, imprímela
previamente conecho para ver los resultados evaluados en stdout.
Utiliza ls o añade echo para probar los comandos destructivos.
Si tu comando invoca rm, mv, cp, u otros comandos quepodrían
sobrescribir o eliminar archivos, coloca echo delante de ellos para
confirmar qué archivos se verán afectados. (Así, en lugar de
ejecutar rm, ejecuta echo rm.) Otra táctica de seguridad es
sustituir rm por lspara listar los archivos que se eliminarían.
Inserta un tee para ver los resultados intermedios.
Si quieres ver la salida (stdout) en medio de una larga canalización,
introduce el comando tee para guardar la salida en un archivo y
poder examinarla. El siguiente comando guarda la salida
de command3 en el archivo outfile, mientras canaliza esa misma
salida a command4:
$ command1 | command2 | command3 | tee outfile |
command4 | command5
$ less outfile
Vale, ¡vamos a construir algunas frases descaradas!
Insertar un nombre de archivo en una
secuencia
Esta descarada frase es similar a la que abría el capítulo (renombrar
archivos .jpg ), pero más detallada. También es una situación real a la que
me enfrenté mientras escribía este libro. Al igual que la anterior, combina
dos técnicas del Capítulo 7: la sustitución de procesos y la canalización
a bash. El resultado es un patrón repetible para
resolverproblemas similares.
Escribí este libro en un ordenador Linux utilizando un lenguaje de
composición llamado AsciiDoc. Los detalles del lenguaje no son
importantes aquí; lo que importa es que cada capítulo era un archivo
independiente, y originalmente eran 10:
$ ls
[Link] [Link] [Link] [Link]
[Link]
[Link] [Link] [Link] [Link]
[Link]
En algún momento, decidí insertar un 11º capítulo entre los capítulos 2 y
3. Eso significaba cambiar el nombre de algunos archivos. Los capítulos 3-
10 tenían que convertirse en 4-11, dejando un hueco para que pudiera
hacer un nuevo capítulo 3([Link]). Podría haber renombrado los
archivos manualmente, empezando por [Link] y trabajando hacia
atrás:2
$ mv [Link] [Link]
$ mv [Link] [Link]
⋮
$ mv [Link] [Link]
$ mv [Link] [Link]
Pero este método es tedioso (¡imagínate si hubiera 1.000 archivos en lugar
de 11!), así que en su lugar, he generado los comandos mv necesarios y los
he canalizado a bash. Fíjate bien en los comandos mv anteriores y piensa
por un momento cómo podrías crearlos.
Céntrate primero en los nombres de archivo
originales [Link] [Link]. Podrías imprimirlos utilizando
una expansión de llaves comoch{10..03}.asciidoc, como en el primer
ejemplo de este capítulo, pero para practicar un poco de flexibilidad,
utiliza el comando seq -w para imprimir los números:
$ seq -w 10 -1 3
10
09
⋮
08
03
A continuación, convierte esta secuencia numérica en nombres de
archivo pasándola a sed:
$ seq -w 10 -1 3 | sed 's/\(.*\)/ch\[Link]/'
[Link]
⋮
[Link]
[Link]
Ahora tienes una lista con los nombres de los archivos originales. Haz lo
mismo con los Capítulos 4-11 para crear los nombres de los archivos de
destino:
$ seq -w 11 -1 4 | sed 's/\(.*\)/ch\[Link]/'
[Link]
⋮
[Link]
[Link]
Para formar los comandos mv, necesitas imprimir los nombres de archivo
original y nuevo uno al lado del otro. El primer ejemplo de este capítulo
resolvía el problema de "uno al lado del otro" con paste, y utilizaba la
sustitución de procesos para tratar las dos listas impresas como archivos.
Haz lo mismo aquí:
$ paste <(seq -w 10 -1 3 | sed 's/\(.*\)/ch\[Link]/') \
<(seq -w 11 -1 4 | sed 's/\(.*\)/ch\[Link]/')
[Link] [Link]
⋮
[Link] [Link]
[Link] [Link]
CONSEJO
El comando anterior puede parecer mucho tecleo, pero con el historial de
comandos y la edición de la línea de comandos al estilo de Emacs, en realidad no
lo es. Para pasar de la línea única "seq y sed" al comandopaste:
1. Recupera el comando anterior del historial con la flecha hacia arriba.
2. Pulsa Ctrl-A y luego Ctrl-K para cortar toda la línea.
3. Escribe la palabra paste seguida de un espacio.
4. Pulsa Ctrl-Y dos veces para crear dos copias de los comandos seq y sed.
5. Utiliza el movimiento y las pulsaciones de las teclas de edición para
modificar la segunda copia.
6. Y así sucesivamente.
Antepone mv a cada línea canalizando la salida a sed, imprimiendo
exactamente los comandos mv que necesites:
$ paste <(seq -w 10 -1 3 | sed 's/\(.*\)/ch\[Link]/') \
<(seq -w 11 -1 4 | sed 's/\(.*\)/ch\[Link]/') \
| sed 's/^/mv /'
mv [Link] [Link]
⋮
mv [Link] [Link]
mv [Link] [Link]
Como paso final, canaliza los comandos a bash para su ejecución:
$ paste <(seq -w 10 -1 3 | sed 's/\(.*\)/ch\[Link]/') \
<(seq -w 11 -1 4 | sed 's/\(.*\)/ch\[Link]/') \
| sed 's/^/mv /' \
| bash
Utilicé exactamente esta solución para mi libro. Después de ejecutar los
comandos mv, los archivos resultantes eran los Capítulos 1, 2 y 4-11,
dejando un hueco para un nuevo Capítulo 3:
$ ls ch*.asciidoc
[Link] [Link] [Link] [Link]
[Link]
[Link] [Link] [Link] [Link]
[Link]
El patrón que acabo de presentar es reutilizable en todo tipo de
situaciones para ejecutar una secuencia de comandos relacionados:
1. Genera los argumentos del comando como listas en stdout.
2. Imprime las listas una al lado de la otra con paste y procesa la
sustitución.
3. Antepone a un nombre de comando sed sustituyendo el carácter
de inicio de línea (^) por un nombre de programa y un espacio.
4. Envía los resultados a bash.
Comprobación de pares de archivos
coincidentes
Este sujetador está inspirado en un uso real de Mediawiki, el software que
hace funcionar Wikipedia y miles de otras wikis. Mediawiki permite a los
usuarios subir imágenes para mostrarlas. La mayoría de los usuarios
siguen un proceso manual a través de formularios web: haz clic en Elegir
archivo para que aparezca un cuadro de diálogo de archivos, navega
hasta un archivo de imagen y selecciónalo, añade un comentario
descriptivo en el formulario y haz clic en Subir. Los administradores de la
wiki utilizan un método más automatizado: un script que lee todo un
directorio y sube sus imágenes. Cada archivo de imagen (por
ejemplo, águila_calva.jpg) se empareja con un archivo de
texto(águila_calva.txt) que contiene un comentario descriptivo sobre la
imagen.
Imagina que te enfrentas a un directorio enorme que sólo contiene
archivos JPEG y TXT. Quieres confirmar que a cada archivo de imagen le
corresponde un archivo de texto y viceversa. Aquí tienes una versión más
pequeña de ese directorio:
$ ls
bald_eagle.jpg blue_jay.jpg [Link] [Link]
[Link]
bald_eagle.txt [Link] [Link] [Link]
[Link]
Vamos a desarrollar dos soluciones diferentes para identificar los
archivos que no coincidan. Para la primera solución, crea dos listas, una
para los archivos JPEG y otra para los archivos de texto, y utiliza cut para
eliminar sus extensiones de archivo .txt y .jpg:
$ ls *.jpg | cut -d. -f1
bald_eagle
blue_jay
cardinal
robin
wren
$ ls *.txt | cut -d. -f1
bald_eagle
cardinal
oriole
robin
wren
A continuación, compara las listas con diff utilizando la sustitución de
procesos:
$ diff <(ls *.jpg | cut -d. -f1) <(ls *.txt | cut -d. -f1)
2d1
< blue_jay
3a3
> oriole
Podrías detenerte aquí, porque el resultado indica que la primera lista
tiene un blue_jay de más (lo que implica blue_jay.jpg) y la segunda lista
tiene una oriole de más (lo que implica [Link]). No obstante, vamos a
precisar más los resultados. Elimina las líneas no deseadas buscando los
caracteres < y > al principio de cada línea:
$ diff <(ls *.jpg | cut -d. -f1) <(ls *.txt | cut -d. -
f1) \
| grep '^[<>]'
< blue_jay
> oriole
A continuación, utiliza awk para añadir la extensión de archivo correcta a
cada nombre de archivo ($2), en función de si el nombre de archivo va
precedido de < o >:
$ diff <(ls *.jpg | cut -d. -f1) <(ls *.txt | cut -d. -
f1) \
| grep '^[<>]' \
| awk '/^</{print $2 ".jpg"} /^>/{print $2 ".txt"}'
blue_jay.jpg
[Link]
Ya tienes tu lista de archivos no coincidentes. Sin embargo, esta solución
tiene un fallo sutil. Supón que el directorio actual contiene el nombre de
[Link], que tiene dos puntos. El comando anterior
produciría una salida incorrecta:
blue_jay.jpg
[Link]
[Link] This is wrong
Este problema se produce porque los dos comandos cut eliminan
caracteres desde el primer punto en adelante, en lugar del último punto
en adelante, por lo [Link]. jpg se trunca a amarillo en lugar
[Link]. Para solucionar este problema,
sustituye cut por sed para eliminar los caracteres desde el último punto
hasta el final de la cadena:
$ diff <(ls *.jpg | sed 's/\.[^.]*$//') \
<(ls *.txt | sed 's/\.[^.]*$//') \
| grep '^[<>]' \
| awk '/</{print $2 ".jpg"} />/{print $2 ".txt"}'
blue_jay.txt
[Link]
[Link]
La primera solución ya está completa. La segunda solución adopta un
enfoque diferente. En lugar de aplicar diff a dos listas, genera una única
lista y elimina los pares de nombres de archivo que coincidan. Empieza
eliminando las extensiones de archivo con sed (utilizando el mismo script
sed que antes) y cuenta las apariciones de cada cadena con uniq -c:
$ ls *.{jpg,txt} \
| sed 's/\.[^.]*$//' \
| uniq -c
2 bald_eagle
1 blue_jay
2 cardinal
1 oriole
2 robin
2 wren
1 [Link]
Cada línea de salida contiene el número 2, que representa un par de
nombres de archivo coincidentes, o 1, que representa un nombre de
archivo no coincidente. Utilizaawk para aislar las líneas que comienzan
con un espacio en blanco y un 1, e imprime sólo el segundo campo:
$ ls *.{jpg,txt} \
| sed 's/\.[^.]*$//' \
| uniq -c \
| awk '/^ *1 /{print $2}'
blue_jay
oriole
[Link]
Para el último paso, ¿cómo puedes añadir las extensiones de archivo que
faltan? No te molestes en hacer complicadas manipulaciones de cadenas.
Sólo tienes que utilizar ls para listar los archivos reales del directorio
actual. Pon un punto y un asterisco al final de cada línea de salida con awk:
$ ls *.{jpg,txt} \
| sed 's/\.[^.]*$//' \
| uniq -c \
| awk '/^ *1 /{print $2 ".*"}'
blue_jay.*
oriole.*
[Link].*
y envía las líneas a ls mediante sustitución de comandos. El intérprete de
comandos realiza la concordancia de patrones y ls imprime los nombres
de archivo completos con las extensiones. ¡Listo!
$ ls -1 $(ls *.{jpg,txt} \
| sed 's/\.[^.]*$//' \
| uniq -c \
| awk '/^ *1 /{print $2 ".*"}')
blue_jay.jpg
[Link]
[Link]
Generar un CDPATH desde tu
directorio de inicio
En la sección "Organiza tu directorio principal para una navegación
rápida", escribiste a mano una complicada línea CDPATH. Empezaba
con $HOME, seguida de todos los subdirectorios de$HOME, y terminaba con la
ruta relativa .. (directorio padre):
CDPATH=$HOME:$HOME/Work:$HOME/Family:$HOME/Finances:
$HOME/Linux:$HOME/Music:..
Vamos a crear un descarado one-liner para generar esa
línea CDPATH automáticamente, adecuado para insertarlo en un archivo de
configuración bash. Empieza con la lista de subdirectorios de $HOME,
utilizando un subshell para evitar que el comando cd cambie
eldirectorio actual de tu shell:
$ (cd && ls -d */)
Family/ Finances/ Linux/ Music/ Work/
Añade $HOME/ delante de cada directorio con sed:
$ (cd && ls -d */) | sed 's/^/$HOME\//g'
$HOME/Family/
$HOME/Finances/
$HOME/Linux/
$HOME/Music/
$HOME/Work/
El script sed anterior es un poco complicado porque la cadena de
sustitución, $HOME/, contiene una barra oblicua, y las sustituciones
de sedtambién utilizan la barra oblicua como separador. Por eso mi barra
está escapada: $HOME\/. Para simplificar las cosas, recuerda de "Sustitución
y barras" que sed acepta cualquier carácter conveniente como separador.
Utilicemos la arroba (@) en lugar de la barra oblicua para no tener que
escaparla:
$ (cd && ls -d */) | sed 's@^@$HOME/@g'
$HOME/Family/
$HOME/Finances/
$HOME/Linux/
$HOME/Music/
$HOME/Work/
A continuación, corta la barra diagonal final con otra expresión sed:
$ (cd && ls -d */) | sed -e 's@^@$HOME/@' -e 's@/$@@'
$HOME/Family
$HOME/Finances
$HOME/Linux
$HOME/Music
$HOME/Work
Imprime la salida en una sola línea utilizando echo y la sustitución de
comandos. Observa que ya no necesitas poner paréntesis alrededor
decd y ls para crear una subcarpeta explícitamente, porque la sustitución
de comandos crea una subcarpeta por sí misma:
$ echo $(cd && ls -d */ | sed -e 's@^@$HOME/@' -e 's@/$@@')
$HOME/Family $HOME/Finances $HOME/Linux $HOME/Music
$HOME/Work
Añade el primer directorio $HOME y el directorio relativo final ..:
$ echo '$HOME' \
$(cd && ls -d */ | sed -e 's@^@$HOME/@' -e 's@/$@@')
\
..
$HOME $HOME/Family $HOME/Finances $HOME/Linux $HOME/Music
$HOME/Work ..
Cambia los espacios por dos puntos canalizando toda la salida hasta ahora
a tr:
$ echo '$HOME' \
$(cd && ls -d */ | sed -e 's@^@$HOME/@' -e 's@/$@@')
\
.. \
| tr ' ' ':'
$HOME:$HOME/Family:$HOME/Finances:$HOME/Linux:$HOME/Music:
$HOME/Work:..
Por último, añade la variable de entorno CDPATH, y habrás generado una
definición de variable para pegar en un archivo de configuración bash.
Guarda este comando en un script para generar la línea en cualquier
momento, como cuando añades un nuevo subdirectorio a $HOME:
$ echo 'CDPATH=$HOME' \
$(cd && ls -d */ | sed -e 's@^@$HOME/@' -e 's@/$@@')
\
.. \
| tr ' ' ':'
CDPATH=$HOME:$HOME/Family:$HOME/Finances:$HOME/Linux:
$HOME/Music:$HOME/Work:..
Generar archivos de prueba
Una tarea habitual en la industria del software son las pruebas: alimentar
un programa con una amplia variedad de datos para validar que el
programa se comporta según lo previsto. La siguiente frase descarada
genera mil archivos que contienen texto aleatorio que podría utilizarse en
las pruebas de software. El número mil es arbitrario; puedes generar
tantos archivos como quieras.
La solución seleccionará palabras al azar de un archivo de texto grande y
creará mil archivos más pequeños con contenidos y longitudes aleatorios.
Un archivo fuente perfecto es el diccionario del
sistema /usr/share/dict/words, que contiene 102.305 palabras, cada una
en su propia línea.
$ wc -l /usr/share/dict/words
102305 /usr/share/dict/words
Para producir esta descarada frase, tendrás que resolver cuatro
rompecabezas:
1. Barajar aleatoriamente el archivo del diccionario
2. Seleccionar un número aleatorio de líneas del archivo del
diccionario
3. Crear un archivo de salida para guardar los resultados
4. Ejecutar tu solución mil veces
Para barajar el diccionario en orden aleatorio, utiliza el
comando shuf. Cada ejecución del comando shuf
/usr/share/dict/wordsproduce más de cien mil líneas de salida, así que
echa un vistazo a las primeras líneas aleatorias utilizando head:
$ shuf /usr/share/dict/words | head -n3
evermore
shirttail
tertiary
$ shuf /usr/share/dict/words | head -n3
interactively
opt
perjurer
Tu primer enigma está resuelto. A continuación, ¿cómo puedes
seleccionar una cantidad aleatoria de líneas del diccionario
barajado? shuf tiene una opción,-n, para imprimir un número
determinado de líneas, pero quieres que el valor cambie para cada
archivo de salida que crees. Afortunadamente, bash tiene una
variable, RANDOM, que contiene un número entero positivo aleatorio entre 0
y 32.767. Su valor cambia cada vez que accedes a la variable:
$ echo $RANDOM $RANDOM $RANDOM
7855 11134 262
Por lo tanto, ejecuta shuf con la opción -n $RANDOM para imprimir un
número aleatorio de líneas aleatorias. De nuevo, la salida completa podría
ser muy larga, así que canaliza los resultados a wc -l para confirmar que
el número de líneas cambia con cada ejecución:
$ shuf -n $RANDOM /usr/share/dict/words | wc -l
9922
$ shuf -n $RANDOM /usr/share/dict/words | wc -l
32465
Has resuelto el segundo enigma. A continuación, necesitas mil archivos de
salida, o más concretamente, mil nombres de archivo diferentes. Para
generar nombres de archivo, ejecuta el programa pwgen,que genera
cadenas aleatorias de letras y dígitos:
$ pwgen
eng9nooG ier6YeVu AhZ7naeG Ap3quail poo2Ooj9 OYiuri9m
iQuash0E voo3Eph1
IeQu7mi6 eipaC2ti exah8iNg oeGhahm8 airooJ8N eiZ7neez
Dah8Vooj dixiV1fu
Xiejoti6 ieshei2K iX4isohk Ohm5gaol Ri9ah4eX Aiv1ahg3
Shaew3ko zohB4geu
⋮
Añade la opción -N1 para generar sólo una cadena, y especifica la longitud
de la cadena (10) como argumento:
$ pwgen -N1 10
ieb2ESheiw
Opcionalmente, haz que la cadena se parezca más al nombre de un
archivo de texto, utilizando la sustitución de comandos:
$ echo $(pwgen -N1 10).txt
[Link]
¡Tercer puzzle completado! Ahora tienes todas las herramientas para
generar un único archivo de texto aleatorio. Utiliza la opción -
o de shuf para guardar su resultado en un archivo:
$ mkdir -p /tmp/randomfiles && cd /tmp/randomfiles
$ shuf -n $RANDOM -o $(pwgen -N1 10).txt
/usr/share/dict/words
y comprueba los resultados:
$ ls List the new file
[Link]
$ wc -l [Link] How many lines does it
contain?
13544 [Link]
$ head -n3 [Link] Peek at the first few lines
saviors
guerillas
forecaster
¡Tiene buena pinta! El último enigma es cómo ejecutar mil veces el
comando shufanterior. Podrías utilizar un bucle:
for i in {1..1000}; do
shuf -n $RANDOM -o $(pwgen -N1 10).txt /usr/share/dict/words
done
pero eso no es tan divertido como crear una línea descarada. En lugar de
eso, vamos a pregenerar los comandos, como cadenas, y canalizarlos
a bash. Como prueba, imprime el comando que desees una vez
utilizando echo. Añade comillas simples para asegurarte de que $RANDOMno
se evalúa y pwgen no se ejecuta:
$ echo 'shuf -n $RANDOM -o $(pwgen -N1 10).txt
/usr/share/dict/words'
shuf -n $RANDOM -o $(pwgen -N1 10).txt
/usr/share/dict/words
Este comando se puede enviar fácilmente a bash para su ejecución:
$ echo 'shuf -n $RANDOM -o $(pwgen -N1 10).txt
/usr/share/dict/words' | bash
$ ls
[Link]
Ahora, imprime el comando mil veces utilizando el
comando yes canalizado a head, luego canaliza los resultados a bash, y
habrás resuelto el cuarto enigma:
$ yes 'shuf -n $RANDOM -o $(pwgen -N1 10).txt
/usr/share/dict/words' \
| head -n 1000 \
| bash
$ ls
[Link] [Link] [Link]
[Link]
[Link] [Link] [Link]
[Link]
[Link] [Link] [Link]
⋮
[Link]
Si prefieres mil archivos de imagen aleatorios en lugar de archivos de
texto, utiliza la misma técnica (yes, head, y bash) y sustituye shuf por un
comando que genere una imagen aleatoria. Aquí tienes un descarado one-
liner que he adaptado de unasolución de Mark Setchell en Stack Overflow.
Ejecuta el comando convert,del paquete gráfico ImageMagick, para
producir imágenes aleatorias de tamaño 100 x 100 píxeles formadas por
cuadrados multicolores:
$ yes 'convert -size 8x8 xc: +noise Random -scale 100x100 $
(pwgen -N1 10).png' \
| head -n 1000 \
| bash
$ ls
[Link] [Link] [Link]
[Link]
[Link] [Link] [Link]
[Link]
[Link] [Link] [Link]
⋮
[Link]
$ display [Link] View the first image
Generar archivos vacíos
A veces, todo lo que necesitas para hacer pruebas son montones de
archivos con nombres diferentes, aunque estén vacíos. Generar mil
archivos vacíos [Link] hasta [Link] es tan
sencillo como
$ mkdir /tmp/empties Create a directory for the
files
$ cd /tmp/empties
$ touch file{01..1000}.txt Generate the files
Si prefieres nombres de archivo más interesantes, tómalos aleatoriamente
del diccionario del sistema. Utiliza grep para limitar los nombres a letras
minúsculas por simplicidad (evitando espacios, apóstrofes y otros
caracteres que serían especiales para el shell):
$ grep '^[a-z]*$' /usr/share/dict/words
a
aardvark
⋮
aardvarks
Baraja los nombres con shuf e imprime los mil primeros con head:
$ grep '^[a-z]*$' /usr/share/dict/words | shuf | head -
n1000
triplicating
quadruplicates
⋮
podiatrists
Por último, canaliza los resultados a xargs para crear los archivos
con touch:
$ grep '^[a-z]*$' /usr/share/dict/words | shuf | head -
n1000 | xargs touch
$ ls
abases distinctly magnolia sadden
abets distrusts maintaining sales
⋮
aboard divided malformation salmon
Resumen
Espero que los ejemplos de este capítulo te hayan ayudado a desarrollar
tus habilidades para escribir frases descaradas. Varios de ellos
proporcionaban patrones reutilizables que pueden resultarte útiles en
otras situaciones.
Una advertencia: las frases de una sola línea no son la única solución. Son
sólo una forma de trabajar eficientemente en la línea de comandos. A
veces obtendrás mejores resultados escribiendo un script de shell. Otras
veces encontrarás mejores soluciones con un lenguaje de programación
como Perl o Python. Sin embargo, escribir una sola línea es una habilidad
vital para realizar tareas críticas con rapidez y estilo.
1 El primer uso de este término (que yo sepa) es la página de manual de
lorder(1) en BSD Unix 4.x. Gracias a Bob Byrnes por encontrarlo.
2Empezar por [Link] y seguir hacia delante sería peligroso:
¿puedes ver por qué? Si no es así, crea estos archivos con el
comando touch ch{01..10}.asciidoc e inténtalo tú mismo.
Capítulo 9. Aprovechar los archivos de
texto
Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y
comentarios: translation-feedback@[Link]
El texto plano es el formato de datos más común en muchos sistemas
Linux. El contenido que se envía de comando a comando en la mayoría de
las tuberías es texto. Los archivos de código fuente de los programadores,
los archivos de configuración del sistema en /etc y los archivos HTML y
Markdown son todos archivos de texto. Los mensajes de correo
electrónico son texto; incluso los archivos adjuntos se almacenan como
texto internamente para su transporte. Incluso puedes almacenar como
texto archivos cotidianos como listas de la compra y notas personales.
Contrasta esto con la Internet actual, que es un batiburrillo de audio y
vídeo en streaming, publicaciones en redes sociales, documentos en el
navegador en Google Docs y Office 365, PDF y otros medios enriquecidos.
(Por no hablar de los datos que manejan las aplicaciones móviles, que han
ocultado el concepto de "archivo" a toda una generación). Con este telón
de fondo, los archivos de texto plano parecen casi pintorescos.
No obstante, cualquier archivo de texto puede convertirse en una rica
fuente de datos que puedesminar con comandos Linux cuidadosamente
elaborados, sobre todo si el texto está estructurado. Cada línea del
archivo /etc/passwd, por ejemplo, representa a un usuario de Linux y
tiene siete campos, entre ellos nombre de usuario, ID numérico de
usuario, directorio personal y otros. Los campos están separados por dos
puntos, lo que hace que el archivo sea fácilmente analizable por cut -
d: o awk -F:. He aquí un comando que imprime todos los nombres de
usuario (el primer campo) alfabéticamente:
$ cut -d: -f1 /etc/passwd | sort
avahi
backup
⋮
daemon
Y aquí tienes uno que separa a los usuarios humanos de las cuentas del
sistema por sus ID de usuario numéricos y envía a los usuarios un correo
electrónico de bienvenida. Construyamos paso a paso este descarado de
una sola línea. Primero, utiliza awk para imprimir los nombres de usuario
(campo 1) cuando el ID numérico de usuario (campo 3) sea 1000 o mayor:
$ awk -F: '$3>=1000 {print $1}' /etc/passwd
jones
smith
A continuación, produce saludos por canalización a xargs:
$ awk -F: '$3>=1000 {print $1}' /etc/passwd \
| xargs -I@ echo "Hi there, @!"
Hi there, jones!
Hi there, smith!
A continuación, genera comandos (cadenas) para dirigir cada saludo al
comando mail , que envía un correo electrónico a un usuario determinado
con un asunto determinado (-s):
$ awk -F: '$3>=1000 {print $1}' /etc/passwd \
| xargs -I@ echo 'echo "Hi there, @!" | mail -s greetings
@'
echo "Hi there, jones!" | mail -s greetings jones
echo "Hi there, smith!" | mail -s greetings smith
Por último, canaliza los comandos generados a bash para enviar los
correos electrónicos (no aparecerá ninguna salida en la pantalla):
$ awk -F: '$3>=1000 {print $1}' /etc/passwd \
| xargs -I@ echo 'echo "Hi there, @!" | mail -s greetings
@' \
| bash
Las soluciones anteriores, como muchas otras de este libro, parten de un
archivo de texto existente y manipulan su contenido con comandos. Es
hora de invertir ese enfoque y diseñar intencionadamente nuevos
archivos de texto que se asocien bien con los comandos de Linux. 1 Esta
es una estrategia ganadora para hacer el trabajo con eficacia en un
sistema Linux. Sólo hacen falta cuatro pasos:
1. Fíjate en un problema empresarial que quieras resolver y que
implique datos.
2. Almacena los datos en un archivo de texto en un formato
conveniente.
3. Inventa comandos Linux que procesen el archivo para resolver
el problema.
4. (Opcional.) Captura esos comandos en scripts, alias o funciones
para que sea más sencillo ejecutarlos.
En este capítulo, construirás diversos archivos de texto estructurado y
crearás comandos para procesarlos, con el fin de resolver varios
problemas empresariales.
Un primer ejemplo: Encontrar archivos
Supón que tu directorio personal contiene decenas de miles de archivos y
subdirectorios y, de vez en cuando, no recuerdas dónde pusiste uno de
ellos. El comando find localiza un archivo por su nombre,
[Link]:
$ find $HOME -name [Link] -print
/home/smith/Work/Writing/Books/Lists/[Link]
pero find es lento porque busca en todo tu directorio personal, y necesitas
localizar archivos regularmente. Este es el paso 1, darse cuenta de un
problema empresarial que implica datos: encontrar archivos en tu
directorio personal rápidamente por su nombre.
El paso 2 consiste en almacenar los datos en un archivo de texto con un
formato adecuado. Ejecutafind una vez para construir una lista de todos
tus archivos y directorios, una ruta de archivo por línea, y guárdala en un
archivo oculto:
$ find $HOME -print > $HOME/.ALLFILES
$ head -n3 $HOME/.ALLFILES
/home/smith
/home/smith/Work
⋮
/home/smith/Work/[Link]
Ahora ya tienes los datos: un índice línea por línea de tus archivos. El paso
3 es inventar comandos Linux para acelerar la búsqueda de archivos, y
para ello, utiliza grep. Es mucho más rápido grep a través de un archivo
grande que ejecutar find en un árbol de directorios grande:
$ grep [Link] $HOME/.ALLFILES
/home/smith/Work/Writing/Books/Lists/[Link]
El paso 4 consiste en facilitar la ejecución del comando. Escribe un script
de una línea llamado ff, para "buscar archivos", que ejecute grep con
cualquier opción proporcionada por el usuario y una cadena de
búsqueda, como en el Ejemplo 9-1.
Ejemplo 9-1. El script ff
#!/bin/bash
# $@ means all arguments provided to the script
grep "$@" $HOME/.ALLFILES
Haz que el script sea ejecutable y colócalo en cualquier directorio de tu
ruta de búsqueda, como tu subdirectorio personal bin:
$ chmod +x ff
$ echo $PATH Check
your search path
/home/smith/bin:/usr/local/bin:/usr/bin:/bin
$ mv ff ~/bin
Ejecuta ff en cualquier momento para localizar archivos rápidamente
cuando no recuerdes dónde los pusiste.
$ ff animal
/home/smith/Work/Writing/Books/Lists/[Link]
$ ff -i animal | less Case-
insensitive grep
/home/smith/Work/Writing/Books/Lists/[Link]
/home/smith/Vacations/Zoos/Animals/[Link]
/home/smith/Vacations/Zoos/Animals/[Link]
⋮
$ ff -i animal | wc -l How
many matches?
16
Vuelve a ejecutar el comando find cada cierto tiempo para actualizar el
índice. (O mejor aún, crea una tarea programada con cron;
consulta"Aprende cron, crontab y at"). Voilà: has creado una utilidad de
búsqueda de archivos rápida y flexible a partir de dos pequeños
comandos. Los sistemas Linux proporcionan aotras aplicaciones que
indexan y buscan archivos rápidamente, como el comando locate y las
utilidades de búsqueda de GNOME, KDE Plasma y otros entornos de
escritorio, pero eso no viene al caso. Mira lo fácil que era construirlo tú
mismo. Y la clave del éxito fue crear un archivo de texto con un formato
sencillo.
Comprobar la caducidad de un
dominio
Para el siguiente ejemplo, supongamos que posees algunos nombres de
dominio de Internet y quieres llevar un registro de cuándo caducan para
poder renovarlos. Ése es el paso 1, identificar el problema empresarial. El
paso 2 es crear un archivo con esos nombres de dominio,
como [Link], un nombre de dominio por línea:
[Link]
[Link]
[Link]
El paso 3 consiste en inventar comandos que aprovechen este archivo de
texto para determinar las fechas de caducidad. Empieza con el
comando whois , que consulta al registrador de dominios para obtener
información sobre un dominio:
$ whois [Link] | less
Domain Name: [Link]
Registry Domain ID: 2336799_DOMAIN_COM-VRSN
Registrar WHOIS Server: [Link]
Updated Date: 2021-08-14T07:01:44Z
Creation Date: 1995-08-14T04:00:00Z
Registry Expiry Date: 2022-08-13T04:00:00Z
⋮
La fecha de caducidad va precedida de la cadena "Fecha de caducidad del
registro", que puedes aislar con grep y awk:
$ whois [Link] | grep 'Registry Expiry Date:'
Registry Expiry Date: 2022-08-13T04:00:00Z
$ whois [Link] | grep 'Registry Expiry Date:' | awk
'{print $4}'
2022-08-13T04:00:00Z
Haz que la fecha sea más legible mediante el comando date --date , que
puede convertir una cadena de fecha de un formato a otro:
$ date --date 2022-08-13T04:00:00Z
Sat Aug 13 00:00:00 EDT 2022
$ date --date 2022-08-13T04:00:00Z +'%Y-%m-%d' Year-
month-day format
2022-08-13
Utiliza la sustitución de comandos para introducir la cadena de fecha
de whois en el comandodate:
$ echo $(whois [Link] | grep 'Registry Expiry Date:' |
awk '{print $4}')
2022-08-13T04:00:00Z
$ date \
--date $(whois [Link] \
| grep 'Registry Expiry Date:' \
| awk '{print $4}') \
+'%Y-%m-%d'
2022-08-13
Ahora tienes un comando que consulta a un registrador e imprime una
fecha de caducidad. Crea un script check-expiry, mostrado enel Ejemplo 9-
2, que ejecute el comando anterior e imprima la fecha de caducidad, una
tabulación y el nombre del dominio:
$ ./check-expiry [Link]
2022-08-13 [Link]
Ejemplo 9-2. El script check-expiry
#!/bin/bash
expdate=$(date \
--date $(whois "$1" \
| grep 'Registry Expiry Date:' \
| awk '{print $4}') \
+'%Y-%m-%d')
echo "$expdate $1" # Two values separated by a tab
Ahora, comprueba todos los dominios del archivo [Link] mediante
un bucle. Crea un nuevo script, check-expiry-all, que se muestra en
el Ejemplo 9-3.
Ejemplo 9-3. El script check-expiry-all
#!/bin/bash
cat [Link] | while read domain; do
./check-expiry "$domain"
sleep 5 # Be kind to the registrar's server
done
Ejecuta el script en segundo plano, ya que puede tardar un rato si tienes
muchos dominios, y redirige toda la salida (stdout y stderr) a un archivo:
$ ./check-expiry-all &> [Link] &
Cuando el script finaliza, el archivo [Link] contiene la información
deseada:
$ cat [Link]
2022-08-13 [Link]
2022-05-26 [Link]
⋮
2022-09-17 [Link]
¡Hurra! Pero no te detengas ahí. El propio archivo [Link] está muy
bien estructurado para su posterior procesamiento, con dos columnas con
pestañas. Por ejemplo, ordena las fechas y encuentra el próximo dominio
a renovar:
$ sort -n [Link] | head -n1
2022-05-26 [Link]
O bien, utiliza awk para buscar dominios que hayan caducado o caduquen
hoy, es decir, cuya fecha de caducidad (campo 1) sea menor o igual que la
fecha de hoy (impresa con date +%Y-%m-%d):
$ awk "\$1<=\"$(date +%Y-%m-%d)\"" [Link]
Algunas notas sobre el comando awk anterior:
He escapado de el signo del dólar (antes de $1) y las comillas
dobles alrededor de la cadena de fecha para que el intérprete de
comandos no las evalúe antes de que pueda hacerlo awk.
He hecho un poco de trampa utilizando el operador de
cadena <= para comparar fechas. No es una comparación
matemática, sólo una comparación de cadenas, pero funciona
porque el formato de fecha, YYYY-MM-DDordena alfabética y
cronológicamente en el mismo orden.
Con más esfuerzo, podrías hacer cálculos de fechas en awk para informar
de las fechas de caducidad, digamos, con dos semanas de antelación, y
luego crear una tarea programada para ejecutar el script cada noche y
enviarte un informe por correo electrónico. No dudes en experimentar.
La cuestión aquí, sin embargo, es que una vez más, con un puñado de
comandos, has creado una utilidad útil controlada por un archivo de
texto.
Crear una base de datos de códigos de
área
El siguiente ejemplo utiliza un archivo con tres campos que puedes
procesar de muchas formas. El archivo, llamado [Link], contiene
los prefijos telefónicos de Estados Unidos. Recupera uno del material
complementario de este libro, en el
directoriochapter09/build_area_code_database, o crea tu propio
archivo, por ejemplo, a partir de Wikipedia:2
201 NJ Hackensack, Jersey City
202 DC Washington
203 CT New Haven, Stamford
989 MI Saginaw
CONSEJO
Ordena primero los campos con longitudes predecibles, para que las columnas
aparezcan bien alineadas a la vista. Mira qué desordenado aparece el archivo si
pones los nombres de las ciudades en la primera columna:
Hackensack, Jersey City 201 NJ
Washington 202 DC
Una vez creado este archivo, puedes hacer muchas cosas con él. Busca
códigos de área por estado con grep, añadiendo la opción -w para buscar
sólo palabras completas (en caso de que otro texto contenga casualmente
"NJ"):
$ grep -w NJ [Link]
201 NJ Hackensack, Jersey City
551 NJ Hackensack, Jersey City
609 NJ Atlantic City, Trenton, southeast and central
⋮
west
o busca ciudades por código de área:
$ grep -w 202 [Link]
202 DC Washington
o por cualquier cadena del archivo:
$ grep Washing [Link]
202 DC Washington
227 MD Silver Spring, Washington suburbs, Frederick
⋮
240 MD Silver Spring, Washington suburbs, Frederick
Cuenta los códigos de área con wc:
$ wc -l [Link]
375 [Link]
Encuentra el estado con más prefijos (el ganador es California con 38):
$ cut -f2 [Link] | sort | uniq -c | sort -nr | head
-n1
38 CA
Convierte el archivo a formato CSV para importarlo a una aplicación de
hoja de cálculo. Imprime el tercer campo entre comillas dobles para evitar
que sus comas se interpreten como caracteres separadores CSV:
$ awk -F'\t' '{printf "%s,%s,\"%s\"\n", $1, $2, $3}'
[Link] \
> [Link]
$ head -n3 [Link]
201,NJ,"Hackensack, Jersey City"
202,DC,"Washington"
203,CT,"New Haven, Stamford"
Agrupa todos los prefijos de un estado determinado en una sola línea:
$ awk '$2~/^NJ$/{ac=ac FS $1} END {print "NJ:" ac}'
[Link]
NJ: 201 551 609 732 848 856 862 908 973
o cotejar para cada estado, utilizando matrices y bucles for como
en "Mejorar el detector de archivos duplicados":
$ awk '{arr[$2]=arr[$2] " " $1} \
END {for (i in arr) print i ":" arr[i]}'
[Link] \
| sort
AB: 403 780
AK: 907
⋮
AL: 205 251 256 334 659
WY: 307
Convierte cualquiera de los comandos anteriores en alias, funciones o
scripts, lo que te convenga. Un ejemplo sencillo es el
script areacodedel Ejemplo 9-4.
Ejemplo 9-4. El script areacode
#!/bin/bash
if [ -n "$1" ]; then
grep -iw "$1" [Link]
fi
El script areacode busca cualquier palabra completa en el
archivo [Link], como un código de área, la abreviatura de un
estado o el nombre de una ciudad:
$ areacode 617
617 MA Boston
Crear un gestor de contraseñas
Para un último ejemplo en profundidad, almacenemos nombres de
usuario, contraseñas y notas en un archivo de texto cifrado, en un
formato estructurado para facilitar su recuperación en la línea de
comandos. El comando resultante es un gestor de contraseñas básico, una
aplicación que alivia la carga de memorizar montones de contraseñas
complicadas.
ADVERTENCIA
La gestión de contraseñas es un tema complejo en seguridad informática. Este
ejemplo crea un gestor de contraseñas extremadamente básico como ejercicio
educativo. No lo utilices para aplicaciones de misión crítica.
El archivo de contraseña, llamado bóveda, tiene tres campos separados
por caracteres de tabulación simples:
Nombre de usuario
Contraseña
Notas (cualquier texto)
Crea el archivo de la bóveda y añade los datos. El archivo aún no está
encriptado, así que de momento introduce sólo contraseñas falsas:
$ touch vault Create
an empty file
$ chmod 600 vault Set file
permissions
$ emacs vault Edit the
file
$ cat vault
sally fake1 [Link] account
ssmith fake2 [Link] account for work
s999 fake3 Bank of America account, [Link]
smith2 fake4 My blog at [Link]
birdy fake5 [Link] account for home
Guarda la caja fuerte en un lugar conocido:
$ mkdir ~/etc
$ mv vault ~/etc
La idea es utilizar un programa de comparación de patrones
como grep o awk para imprimir las líneas que coincidan con una cadena
determinada. Esta sencilla pero potente técnica puede coincidir con
cualquier parte de cualquier línea, en lugar de sólo con nombres de
usuario o sitios web. Por ejemplo:
$ cd ~/etc
$ grep sally vault Match a
username
sally fake1 [Link] account
$ grep work vault Match the
notes
ssmith fake2 [Link] account for work
$ grep drop vault Match
multiple lines
ssmith fake2 [Link] account for work
birdy fake5 [Link] account for home
Captura esta sencilla funcionalidad en un script; luego, mejorémosla paso
a paso, incluyendo finalmente la encriptación del archivo bóveda. Llama
al script pman para "gestor de contraseñas" y crea la versión trivial
delEjemplo 9-5.
Ejemplo 9-5. pman versión 1: más sencillo imposible
#!/bin/bash
# Just print matching lines
grep "$1" $HOME/etc/vault
Guarda el script en tu ruta de búsqueda:
$ chmod 700 pman
$ mv pman ~/bin
Prueba el guión:
$ pman goog
sally fake1 [Link] account
$ pman account
sally fake1 [Link] account
ssmith fake2 [Link] account for work
s999 fake3 Bank of America account, [Link]
birdy fake5 [Link] account for home
$ pman facebook
(produces no output)
La siguiente versión del Ejemplo 9-6 añade un poco de comprobación de
errores y algunos nombres de variables memorizables.
Ejemplo 9-6. pman versión 2: añadir alguna comprobación de errores
#!/bin/bash
# Capture the script name.
# $0 is the path to the script, and basename prints the final
filename.
PROGRAM=$(basename $0)
# Location of the password vault
DATABASE=$HOME/etc/vault
# Ensure that at least one argument was provided to the script.
# The expression >&2 directs echo to print on stderr instead of
stdout.
if [ $# -ne 1 ]; then
>&2 echo "$PROGRAM: look up passwords by string"
>&2 echo "Usage: $PROGRAM string"
exit 1
fi
# Store the first argument in a friendly, named variable
searchstring="$1"
# Search the vault and print an error message if nothing matches
grep "$searchstring" "$DATABASE"
if [ $? -ne 0 ]; then
>&2 echo "$PROGRAM: no matches for '$searchstring'"
exit 1
fi
Ejecuta el script:
$ pman
pman: look up passwords by string
Usage: pman string
$ pman smith
ssmith fake2 [Link] account for work
smith2 fake4 My blog at [Link]
$ pman xyzzy
pman: no matches for 'xyzzy'
Un inconveniente de esta técnica es que no es escalable. Si la
bóvedacontuviera cientos de líneas y grep cotejara e imprimiera 63 de
ellas, tendrías que buscar a ojo para encontrar la contraseña que
necesitas. Mejora el script añadiendo una clave única (una cadena) a cada
línea de la tercera columna, y actualiza pman para que busque primero esa
clave única. El archivo dela bóveda, con la tercera columna en negrita,
tiene ahora el siguiente aspecto:
sally fake1 google [Link] account
ssmith fake2 dropbox [Link] account for work
s999 fake3 bank Bank of America account,
[Link]
smith2 fake4 blog My blog at [Link]
birdy fake5 dropbox2 [Link] account for home
El Ejemplo 9-7 muestra el script actualizado que utiliza awk en lugar
degrep. También utiliza la sustitución de comandos para capturar la salida
y comprobar si está vacía (la prueba -z significa "cadena de longitud
cero"). Observa que si buscas una clave que no existe en el
almacén, pmanvuelve a su comportamiento original e imprime todas las
líneas que coincidan con la cadena de búsqueda.
Ejemplo 9-7. pman versión 3: priorizar la búsqueda de la clave en la
tercera columna
#!/bin/bash
PROGRAM=$(basename $0)
DATABASE=$HOME/etc/vault
if [ $# -ne 1 ]; then
>&2 echo "$PROGRAM: look up passwords"
>&2 echo "Usage: $PROGRAM string"
exit 1
fi
searchstring="$1"
# Look for exact matches in the third column
match=$(awk '$3~/^'$searchstring'$/' "$DATABASE")
# If the search string doesn't match a key, find all matches
if [ -z "$match" ]; then
match=$(awk "/$searchstring/" "$DATABASE")
fi
# If still no match, print an error message and exit
if [ -z "$match" ]; then
>&2 echo "$PROGRAM: no matches for '$searchstring'"
exit 1
fi
# Print the match
echo "$match"
Ejecuta el script:
$ pman dropbox
ssmith fake2 dropbox [Link] account for work
$ pman drop
ssmith fake2 dropbox [Link] account for work
birdy fake5 dropbox2 [Link] account for home
La bóveda de archivos en texto plano es un riesgo para la seguridad, por
lo que debes encriptarla con el programa de encriptación estándar de
Linux GnuPG, que se invoca como gpg. Si ya tienes configurado GnuPG
para utilizarlo, estupendo. Si no, configúralo con el siguiente comando,
facilitando tu dirección de correo electrónico:3
$ gpg --quick-generate-key your_email_address default
default never
Se te pedirá una frase de contraseña para la clave (dos veces).
Proporciona una frase de contraseña segura. Cuando gpg finalice, estarás
listo para encriptar el archivo de contraseña utilizando encriptación de
clave pública, produciendo el [Link]:
$ cd ~/etc
$ gpg -e -r your_email_address vault
$ ls vault*
vault [Link]
Como prueba, desencripta el archivo [Link] en stdout:4
$ gpg -d -q [Link]
Passphrase: xxxxxxxx
sally fake1 google [Link] account
⋮
ssmith fake2 dropbox [Link] account for work
A continuación, actualiza tu script para que utilice el
archivo [Link] encriptado en lugar del archivo vault en texto plano.
Esto significa descifrar [Link] en stdout y pasar su contenido
a awk para que coincida, como en el Ejemplo 9-8.
Ejemplo 9-8. pman versión 4: uso de una bóveda encriptada
#!/bin/bash
PROGRAM=$(basename $0)
# Use the encrypted file
DATABASE=$HOME/etc/[Link]
if [ $# -ne 1 ]; then
>&2 echo "$PROGRAM: look up passwords"
>&2 echo "Usage: $PROGRAM string"
exit 1
fi
searchstring="$1"
# Store the decrypted text in a variable
decrypted=$(gpg -d -q "$DATABASE")
# Look for exact matches in the third column
match=$(echo "$decrypted" | awk '$3~/^'$searchstring'$/')
# If the search string doesn't match a key, find all matches
if [ -z "$match" ]; then
match=$(echo "$decrypted" | awk "/$searchstring/")
fi
# If still no match, print an error message and exit
if [ -z "$match" ]; then
>&2 echo "$PROGRAM: no matches for '$searchstring'"
exit 1
fi
# Print the match
echo "$match"
El script muestra ahora las contraseñas del archivo encriptado:
$ pman dropbox
Passphrase: xxxxxxxx
ssmith fake2 dropbox [Link] account for work
$ pman drop
Passphrase: xxxxxxxx
ssmith fake2 dropbox [Link] account for work
birdy fake5 dropbox2 [Link] account for home
Ya tienes todas las piezas de tu gestor de contraseñas. Algunos pasos
finales son:
Cuando estés convencido de que puedes desencriptar el
archivo [Link] de forma fiable, elimina el
archivo vault original.
Si lo deseas, sustituye las contraseñas falsas por otras reales.
Consulta"Editar directamente archivos enc riptados" para
obtener consejos sobre cómo editar un archivo de texto
encriptado.
Admite comentarios en el almacén de contraseñas -líneas que
empiecen por el símbolo almohadilla (#)- para que puedas hacer
anotaciones sobre las entradas. Para ello, actualiza el script para
que canalice el contenido descifrado a grep -v y filtre las líneas
que empiecen por almohadilla:
decrypted=$(gpg -d -q "$DATABASE" | grep -v '^#')
Imprimir contraseñas en stdout no es bueno para la
seguridad."Mejorando el Gestor de Contraseñas" actualizará este script
para copiar y pegar contraseñas en lugar de imprimirlas.
EDITAR DIRECTAMENTE ARCHIVOS ENCRIPTADOS
Para modificar un archivo encriptado, el método más directo, tedioso e
inseguro es desencriptar el archivo, editarlo y volver a encriptarlo.
$ cd ~/etc
$ gpg [Link] Decrypt
Passphrase: xxxxxxxx
$ emacs vault Use your
favorite text editor
$ gpg -e -r your_email_address vault Encrypt for
yourself
$ rm vault
Para facilitar la edición del archivo [Link], tanto emacs como vim tienen
modos para editar archivos cifrados con GnuPG. Empieza añadiendo esta
línea a un archivo de configuración de bash y abasteciéndolo en cualquier
shell asociado:
export GPG_TTY=$(tty)
Para emacs, configura el paquete EasyPG,que está incorporado. Añade las
siguientes líneas al archivo de configuración $HOME/.emacsy
reinicia emacs. Sustituye aquíla cadena GnuPG IDpor la dirección de
correo electrónico asociada a tu clave, comosmith@[Link]:
(load-library "pinentry")
(setq epa-pinentry-mode 'loopback)
(setq epa-file-encrypt-to "GnuPG ID here")
(pinentry-start)
A continuación, edita cualquier archivo encriptado, y emacs te pedirá tu
frase de contraseña y lo desencripta en un búfer para editarlo. Al
guardar, emacsencripta el contenido del búfer.
Para vim, prueba el pluginvim-gnupg y añade estas líneas al archivo de
configuración $HOME/.vimrc:
let g:GPGPreferArmor=1
let g:GPGDefaultRecipients=["GnuPG ID here"]
Considera la posibilidad de crear un alias para editar cómodamente el
almacén de contraseñas, utilizando la técnica de la sección "Editar
archivos editados frecuentemente con un alias":
alias pwedit="$EDITOR $HOME/etc/[Link]"
Resumen
Las rutas de los archivos, los nombres de dominio, los códigos de área y
las credenciales de inicio de sesión son sólo algunos ejemplos de datos que
funcionan bien en un archivo de texto estructurado. ¿Qué te parece?
¿Tus archivos de música? (Utiliza un comando de Linux
como id3tool para extraer la información ID3 de tus archivos
MP3 y colocarla en un archivo).
¿Los contactos de tu dispositivo móvil? (Utiliza una aplicación
para exportar los contactos a formato CSV, súbelos al
almacenamiento en la nube y luego descárgalos a tu máquina
Linux para procesarlos).
¿Tus notas en la escuela? (Utiliza awk para hacer un seguimiento
de tu media de notas).
¿Una lista de películas que has visto o libros que has leído, con
datos adicionales (puntuaciones, autores, actores, etc.)?
De este modo, puedes construir un ecosistema de comandos que te
ahorren tiempo y que tengan un significado personal o sean productivos
para el trabajo, limitado sólo por tu imaginación.
1Este enfoque es similar al diseño de un esquema de base de datos para
que funcione bien con consultas conocidas.
2 La lista oficial de códigos de área en formato CSV, mantenida por el
Administrador del Plan Norteamericano de Numeración, carece de
nombres de ciudades.
3 Este comando genera un par de claves pública/privada con todas las
opciones por defecto y una fecha de caducidad de "nunca". Para saber
más, consulta man gpg para leer sobre las opciones de gpg, o busca un
tutorial de GnuPG en Internet.
4 Si gpg procede sin pedirte tu frase de contraseña, es que ha almacenado
en caché (guardado) tu frase de contraseña temporalmente.
Parte III. Extras
Los capítulos finales profundizan en temas especializados: algunos en
detalle y otros sólo brevemente para abrirte el apetito de saber más.
Capítulo 10. Eficacia en el teclado
Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y
comentarios: translation-feedback@[Link]
En un día normal, en una estación de trabajo Linux típica, puedes tener
abiertas muchas ventanas de aplicaciones: navegadores web, editores de
texto, entornos de desarrollo de software, reproductores de música,
editores de vídeo, máquinas virtuales, etc. Algunas aplicaciones están
centradas en la interfaz gráfica de usuario, como un programa de pintura,
y adaptadas a un dispositivo señalador como un ratón o un trackball.
Otras están más centradas en el teclado, como un intérprete de comandos
dentro de un programa terminal. Un usuario típico de Linux puede
cambiar entre teclado y ratón docenas (o incluso cientos) de veces por
hora. Cada cambio lleva su tiempo. Te ralentiza. Si puedes reducir el
número de cambios, podrás trabajar con más eficacia.
Este capítulo trata de pasar más tiempo ante el teclado y menos con un
dispositivo señalador. Diez dedos pulsando cien teclas suelen ser más
ágiles que un par de dedos en un ratón. No hablo sólo de utilizar atajos de
teclado; estoy seguro de que puedes buscarlos sin necesidad de este libro
(aunque presento algunos). Hablo de un enfoque diferente para acelerar
algunas tareas cotidianas que parecen inherentemente "de ratón":
trabajar con ventanas, recuperar información de la web y copiar y pegar
con el portapapeles.
Trabajar con Windows
En esta sección, comparto consejos para lanzar ventanas de forma
eficiente, en particular ventanas shell (terminales) y ventanas de
navegador.
Conchas y navegadores instantáneos
La mayoría de los entornos de escritorio de Linux, como GNOME, KDE
Plasma, Unity, y Cinnamon, proporcionan alguna forma de definir teclas
de acceso rápido o atajos de teclado personalizados: pulsaciones de teclas
especiales que lanzan comandos o realizan otras operaciones. Te
recomiendo encarecidamente que definas atajos de teclado para estas
operaciones comunes:
Abrir una nueva ventana shell (un programa terminal)
Abrir una nueva ventana del navegador web
Con estos atajos definidos, puedes abrir un terminal o un navegador en
cualquier momento y en un instante, sin importar qué otra aplicación
estés utilizando.1 Para configurar esto, necesitas saber lo siguiente:
El comando que inicia tu programa de terminal preferido
Algunos populares son gnome-terminal, konsole , y xterm.
El comando que lanza tu navegador preferido
Algunos populares son firefox, google-chrome , y opera.
Cómo definir un atajo de teclado personalizado
Las instrucciones difieren para cada entorno de escritorio y pueden
cambiar de una versión a otra, así que es mejor que las busques en
la web. Busca el nombre de tu entorno de escritorio seguido de
"definir atajo de teclado".
En mi escritorio, asigno el atajo de teclado Ctrl-Windows-T para
ejecutarkonsole y Ctrl-Windows-C para ejecutar google-chrome.
DIRECTORIOS DE TRABAJO
Cuando inicias un intérprete de comandos mediante un atajo de teclado en tu
entorno de escritorio , es hijo de tu intérprete de comandos de inicio de sesión.
Su directorio actual es tu directorio personal (a menos que lo hayas configurado
de algún modo para que sea diferente).
A diferencia de abrir un nuevo intérprete de comandos desde tu programa
terminal, ejecutando explícitamente (digamos) gnome-terminal o xterm en la
línea de comandos o utilizando el menú de tu programa terminal para abrir una
nueva ventana. En este caso, el nuevo intérprete de comandos es hijo del
intérprete de comandos de ese terminal. Su directorio actual es el mismo que
el de su padre, que puede no ser tu directorio personal.
Ventanas One-Shot
Supón que estás en medio de la utilización de varias aplicaciones cuando,
de repente, necesitas un intérprete de comandos para ejecutar un
comando. Muchos usuarios cogerían el ratón y buscarían entre las
ventanas abiertas un terminal en ejecución. No hagas esto: estás
perdiendo el tiempo. Simplemente abre un nuevo terminal con tu tecla de
acceso rápido, ejecuta tu comando y sal del terminal justo después.
Una vez que tengas asignadas teclas de acceso rápido para lanzar
programas de terminal y ventanas de navegador, ve abriendo y cerrando
estas ventanas en grandes cantidades con salvaje desenfreno. ¡Te lo
recomiendo! Crea y destruye terminales y ventanas de navegador con
regularidad, en lugar de dejarlas abiertas durante mucho tiempo. A estas
ventanas de corta duración las llamo ventanas de un solo uso. Las abres
rápidamente, las utilizas unos instantes y las cierras.
Puede que dejes algunos shells abiertos durante mucho tiempo si estás
desarrollando software o realizando otro trabajo prolongado, pero las
ventanas de terminal de un solo uso son perfectas para otros comandos
aleatorios a lo largo del día. A menudo es más rápido abrir un nuevo
terminal que buscar en tu pantalla un terminal existente. No te
preguntes: "¿Dónde está la ventana de terminal que necesito?" y te pongas
a hurgar en el escritorio buscándola. Crea una nueva y ciérrala cuando
haya cumplido su función.
Lo mismo ocurre con las ventanas de los navegadores web. ¿Alguna vez
has levantado la cabeza después de un largo día de pirateo de Linux para
descubrir que tu navegador sólo tiene una ventana y 83 pestañas
abiertas? Eso es síntoma de que hay muy pocas ventanas de un solo
intento. Abre una, visualiza la página web que necesites y ciérrala.
¿Necesitas volver a visitar la página más tarde? Localízala en el historial
de tu navegador.
Atajos de teclado del navegador
Ya que hablamos de las ventanas del navegador, asegúrate de que conoces
los atajos de teclado más importantes de la Tabla 10-1. Si ya tienes las
manos en el teclado y quieres navegar a un nuevo sitio web, a menudo es
más rápido pulsar Ctrl-L para saltar a la barra de direcciones o Ctrl-T para
abrir una pestaña que apuntar y hacer clic.
Acción Atajo de teclado
Abrir ventana nueva Ctrl-N
Abrir nueva ventana Ctrl-Shift-P (Firefox), Ctrl-Shift-N
privada/de incógnito (Chrome y Opera)
Abrir nueva pestaña Ctrl-T
Cerrar pestaña Ctrl-W
Recorre las pestañas del Ctrl-Tab (ciclo hacia delante) y Ctrl-
navegador Shift-Tab (ciclo hacia atrás)
Acción Atajo de teclado
Saltar a la barra de Ctrl-L (o Alt-D o F6)
direcciones
Buscar texto en la página Ctrl-F
actual
Mostrar tu historial de Ctrl-H
navegación
Tabla 10-1. Los atajos de teclado más importantes para Firefox, Google Chrome y Opera
Cambiar de Windows y de escritorio
Cuando tu ajetreado escritorio está lleno de ventanas, ¿cómo encuentras
rápidamente la ventana que quieres? Podrías apuntar y hacer clic para
abrirte paso a través del marasmo, pero a menudo es más rápido utilizar
el atajo de teclado Alt-Tab. Mantén pulsado Alt-Tab y recorrerás todas las
ventanas del escritorio, de una en una. Cuando llegues a la ventana que
quieres, suelta las teclas y esa ventana estará enfocada y lista para usar.
Para desplazarte en sentido inverso, pulsa Alt-Mayúsculas-Tab.
Para recorrer todas las ventanas del escritorio que pertenezcan a la
misma aplicación, como todas las ventanas de Firefox, pulsa Alt-` (Alt-
entrecomillado, o Alt más la tecla situada encima de Tabulador). Para
retroceder en el ciclo, añade la tecla Mayúsculas (Alt-Mayúsculas-
retrocomillas).
Una vez que puedas cambiar de ventana, es hora de hablar de cambiar de
escritorio. Si trabajas en serio en Linux y sólo utilizas un escritorio, te
estás perdiendo una forma estupenda de organizar tu trabajo. Los
escritorios múltiples, también llamados espacios de trabajo o escritorios
virtuales, son exactamente lo que parecen. En lugar de un único
escritorio, puedes tener cuatro, seis o más, cada uno con sus propias
ventanas, y puedes cambiar entre ellos.
En mi estación de trabajo con Ubuntu Linux y KDE Plasma, ejecuto seis
escritorios virtuales y les asigno diferentes propósitos. El escritorio nº 1 es
mi espacio de trabajo principal con correo electrónico y navegación, el nº
2 es para tareas relacionadas con la familia, el nº 3 es donde ejecuto
máquinas virtuales VMware, el nº 4 es para escribir libros como éste, y los
nº 5-6 son para cualquier tarea ad hoc. Estas asignaciones coherentes
hacen que sea rápido y fácil localizar mis ventanas abiertas de distintas
aplicaciones.
Cada entorno de escritorio Linux como GNOME, KDE Plasma, Cinnamon, y
Unity tiene su propia forma de implementar escritorios virtuales, y todos
ellos proporcionan un "conmutador" o "paginador" gráfico para cambiar
entre ellos. Recomiendo definir atajos de teclado en tu entorno de
escritorio para saltar rápidamente a cada escritorio. En mi ordenador,
definí Windows + F1 a Windows + F6 para saltar a los escritorios nº 1 a nº
6, respectivamente.
Hay muchos otros estilos de trabajar con escritorios y ventanas. Algunas
personas utilizan un escritorio por aplicación: un escritorio para shells,
un escritorio para navegar por la web, un escritorio para procesar textos,
etc. Algunas personas con pantallas de portátil pequeñas abren sólo una
ventana en cada escritorio, a pantalla completa, en lugar de varias
ventanas por escritorio. Encuentra un estilo que funcione para ti, siempre
que sea rápido y eficiente.
Acceso web desde la línea de comandos
Los navegadores de apuntar y hacer clic son casi sinónimos de la web,
pero también puedes acceder a sitios web desde la línea de comandos de
Linux con gran efecto.
Iniciar Ventanas del Navegador desde la Línea de
Comandos
Puede que estés acostumbrado a iniciar un navegador web haciendo clic o
tocando un icono, pero también puedes hacerlo desde la línea de
comandos de Linux. Si el navegador aún no se está ejecutando, añade un
ampersand para ejecutarlo en segundo plano, de modo que recuperes el
prompt del intérprete de comandos:
$ firefox &
$ google-chrome &
$ opera &
Si ya se está ejecutando un navegador determinado, omite el ampersand.
El comando indica a una instancia existente del navegador que abra una
nueva ventana o pestaña. El comando sale inmediatamente y te devuelve
el prompt del shell.
CONSEJO
Un comando de navegador en segundo plano puede imprimir mensajes de
diagnóstico y desordenar tu ventana de shell. Para evitarlo, redirige toda la
salida a/dev/null cuando inicies el navegador por primera vez. Por ejemplo
$ firefox &> /dev/null &
Para abrir un navegador y visitar una URL desde la línea de comandos,
proporciona la URL como argumento:
$ firefox [Link]
$ google-chrome [Link]
$ opera [Link]
Por defecto, los comandos anteriores abren una nueva pestaña y la
enfocan. Para forzarlos a abrir una nueva ventana en su lugar, añade una
opción:
$ firefox --new-window [Link]
$ google-chrome --new-window [Link]
$ opera --new-window [Link]
Para abrir una ventana privada o de incógnito del navegador, añade la
opción apropiada de la línea de comandos:
$ firefox --private-window [Link]
$ google-chrome --incognito [Link]
$ opera --private [Link]
Los comandos anteriores pueden parecer mucho tecleo y esfuerzo, pero
puedes ser eficiente definiendo alias para los sitios que visitas a menudo:
# Place in a shell configuration file and source it:
alias oreilly="firefox --new-window [Link]
Del mismo modo, si tienes un archivo que contiene una URL de interés,
extrae la URL con grep, cut u otros comandos de Linux y pásala al
navegador en la línea de comandos con la sustitución de comandos. Aquí
tienes un ejemplo con un archivo separado por tabuladores con dos
columnas:
$ cat [Link]
[Link] My search engine
[Link] My newspaper
[Link] My music
$ grep music [Link] | cut -f1
[Link]
$ google-chrome [Link] music [Link] | cut -f1)
Visit spotify
O, supongamos que haces un seguimiento de los paquetes que esperas con
un archivo denúmeros de seguimiento:
$ cat [Link]
1Z0EW7360669374701 UPS Shoes
568733462924 FedEx Kitchen blender
9305510823011761842873 USPS Care package from Mom
El script shell del Ejemplo 10-1 abre las páginas de seguimiento de los
transportistas apropiados (UPS, FedEx o el Servicio Postal de EEUU)
añadiendo los números de seguimiento a las URL adecuadas.
Ejemplo 10-1. Scripttrack-it que accede a la página de seguimiento
de los remitentes
#!/bin/bash
PROGRAM=$(basename $0)
DATAFILE=[Link]
# Choose a browser command: firefox, opera, google-chrome
BROWSER="opera"
errors=0
cat "$DATAFILE" | while read line; do
track=$(echo "$line" | awk '{print $1}')
service=$(echo "$line" | awk '{print $2}')
case "$service" in
UPS)
$BROWSER "[Link] &
;;
FedEx)
$BROWSER "[Link] &
;;
USPS)
$BROWSER "[Link]
tLabels=$track" &
;;
*)
>&2 echo "$PROGRAM: Unknown service '$service'"
errors=1
;;
esac
done
exit $errors
Recuperar HTML con curl y wget
Los navegadores web no son los únicos programas de Linux que visitan
páginas web. Los programas curl y wget pueden descargar páginas web y
otros contenidos web con un solo comando, sin tocar un navegador. Por
defecto,curl imprime su salida en stdout, y wget guarda su salida en un
archivo (después de imprimir un montón de mensajes de diagnóstico):
$ curl [Link]
Welcome to Efficient [Link]!
$ wget [Link]
--2021-10-27 20:05:47-- [Link]
Resolving [Link] ([Link])...
⋮
Connecting to [Link] ([Link])...
2021-10-27 20:05:47 (12.8 MB/s) - ‘[Link]’ saved
[32/32]
$ cat [Link]
Welcome to Efficient [Link]!
CONSEJO
Algunos sitios no admiten la recuperación mediante wget y curl. Ambos
comandos pueden hacerse pasar por otro navegador en esos casos. Sólo tienes
que decirle a cada programa que cambie su agente de usuario, es decir, la
cadena que identifica a un cliente web con un servidor web. Un agente de
usuario conveniente es "Mozilla":
$ wget -U Mozilla url
$ curl -A Mozilla url
Tanto wget como curl tienen montones de opciones y funciones que
puedes descubrir en sus páginas de manual. De momento, vamos a ver
cómo incorporar estos comandos en descarados comandos de una sola
línea. Supongamos que el sitio web [Link] tiene un
directorio, images, que contiene los [Link] a [Link], y te gustaría
descargarlos. Sus URL son:
[Link]
[Link]
[Link]
Un método ineficiente sería visitar cada URL en un navegador web, de
una en una, y descargar cada imagen. (¡Que levante la mano quien lo
haya hecho alguna vez!) Un método mejor es utilizar wget. Genera las URL
conseq y awk:
$ seq 1 20 | awk '{print
"[Link] $1 ".jpg"}'
[Link]
[Link]
⋮
[Link]
A continuación, añade la cadena "wget" en el programa awk y canaliza los
comandos resultantes a bash para su ejecución:
$ seq 1 20 \
| awk '{print "wget [Link]
$1 ".jpg"}' \
| bash
Alternativamente, utiliza xargs para crear y ejecutar los comandos wget:
$ seq 1 20 | xargs -I@ wget
[Link]
La solución xargs es superior si tus comandos wget contienen algún
carácter especial. La solución "pipe to bash" haría que el shell evaluara
esos caracteres (cosa que no quieres que ocurra), mientras que xargsno lo
haría.
Mi ejemplo era un poco artificioso porque los nombres de archivo de las
imágenes son muy uniformes. En un ejemplo más realista, podrías
descargar todas las imágenes de una página web recuperando la página
con curl, pasándola por una secuencia inteligente de comandos para
aislar las URL de las imágenes, una por línea, y aplicando después una de
las técnicas que acabo de mostrarte:
curl URL | ...clever pipeline here... | xargs -n1 wget
Procesar HTML con HTML-XML-utils
Si sabes algo de HTML y CSS, puedes analizar el código fuente HTML de
las páginas web desde la línea de comandos. A veces es más eficaz que
copiar y pegar a mano trozos de una página web desde una ventana del
navegador. Un práctico conjunto de herramientas para este fin es HTML-
XML-utils, que está disponible en muchas distribuciones Linux y en
elConsorcio World Wide Web. Una receta general es:
1. Utiliza curl (o wget) para capturar la fuente HTML.
2. Utiliza hxnormalize para asegurarte de que el HTML está bien
formado.
3. Identifica los selectores CSS para los valores que quieres
capturar.
4. Utiliza hxselect para aislar los valores, y canaliza la salida a
otros comandos para su procesamiento.
Vamos a ampliar el ejemplo de "Crear una base de datos de códigos de
área" para obtener los datos de los códigos de áreade la Web y producir el
archivo [Link] utilizado en ese ejemplo. Para tu comodidad, he
creado una tabla HTML de códigos de área para que la descargues y la
proceses, que se muestra en la Figura 10-1.
Figura 10-1. Tabla de códigos de área en [Link]
Primero, captura la fuente HTML con curl, utilizando la opción -s para
suprimir los mensajes en pantalla. Pasa la salida a hxnormalize -x para
limpiarla un poco. Conéctalo a less para ver la salida de pantalla en
pantalla:
$ curl -s [Link] \
| hxnormalize -x \
| less
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"[Link]
⋮
<html>
<body>
⋮
<h1>Area code test</h1>
La tabla HTML de esa página, mostrada en el Ejemplo 10-2, tiene el ID
CSS #ac, y sus tres columnas (código de área, estado y ubicación) utilizan
las clases CSS ac, state y cities, respectivamente.
Ejemplo 10-2. Fuente HTML parcial de la tabla de la Figura 10-1
<table id="ac">
<thead>
<tr>
<th>Area code</th>
<th>State</th>
<th>Location</th>
</tr>
</thead>
<tbody>
<tr>
<td class="ac">201</td>
<td class="state">NJ</td>
<td class="cities">Hackensack, Jersey City</td>
⋮
</tr>
</tbody>
</table>
Ejecuta hxselect para extraer los datos del código de área de cada celda de
la tabla, suministrando la opción -c para omitir las etiquetas td de la
salida. Imprime los resultados como una línea larga, con los campos
separados por un carácter de tu elección (utilizando la opción -s ).2 Yo
elegí el carácter @ por su fácil visibilidad en la página:
$ curl -s [Link] \
| hxnormalize -x \
| hxselect -c -s@ '#ac .ac, #ac .state, #ac .cities'
201@NJ@Hackensack, Jersey
City@202@DC@Washington@203@CT@New Haven, Stamford@...
Por último, canaliza la salida a sed para convertir esta larga línea en tres
columnas separadas por tabuladores. Escribe una expresión regular que
coincida con las siguientes cadenas:
1. Un código de área, que consta de dígitos, [0-9]*
2. Un símbolo @
3. Una abreviatura estatal, que son dos letras mayúsculas, [A-Z][A-
Z]
4. Un símbolo @
5. Las ciudades, que es cualquier texto que no incluya un
símbolo @, [^@]*
6. Un símbolo @
Combina las partes para obtener la siguiente expresión regular:
[0-9]*@[A-Z][A-Z]@[^@]*@
Captura el prefijo, el estado y las ciudades como tres subexpresiones
rodeándolas con \( y \). Ahora tienes una expresión regular completa
para sed:
\([0-9]*\)@\([A-Z][A-Z]\)@\([^@]*\)@
Para la cadena de sustitución de sed, proporciona las tres
subexpresionesseparadas por tabuladores y terminadas por nuevas
líneas, lo que produce el formato del archivo [Link]:
\1\t\2\t\3\n
Combina la expresión regular y la cadena de sustitución anteriores para
crear este script sed:
s/\([0-9]*\)@\([A-Z][A-Z]\)@\([^@]*\)@/\1\t\2\t\3\n/g
El comando finalizado produce los datos necesarios para el
archivo [Link]:
$ curl -s [Link] \
| hxnormalize -x \
| hxselect -c -s'@' '#ac .ac, #ac .state, #ac .cities' \
| sed 's/\([0-9]*\)@\([A-Z][A-Z]\)@\([^@]*\)@/\1\t\2\t\3\
n/g'
201 NJ Hackensack, Jersey City
202 DC Washington
⋮
203 CT New Haven, Stamford
MANEJO DE EXPRESIONES REGULARES LARGAS
Si tus guiones sed se hacen tan largos que parecen ruido aleatorio:
s/\([0-9]*\)@\([A-Z][A-Z]\)@\([^@]*\)@/\1\t\2\t\3\n/g
prueba a dividirlas. Almacena partes de la expresión regular en varias
variables del shell, y combina las variables más tarde, como en el
siguiente script del shell:
# The three parts of the regular expression.
# Use single quotes to prevent evaluation by the shell.
areacode='\([0-9]*\)'
state='\([A-Z][A-Z]\)'
cities='\([^@]*\)'
# Combine the three parts, separated by @ symbols.
# Use double quotes to permit variable evaluation by the shell.
regexp="$areacode@$state@$cities@"
# The replacement string.
# Use single quotes to prevent evaluation by the shell.
replacement='\1\t\2\t\3\n'
# The sed script now becomes much simpler to read:
# s/$regexp/$replacement/g
# Run the full command:
curl -s [Link] \
| hxnormalize -x \
| hxselect -c -s'@' '#ac .ac, #ac .state, #ac .cities' \
| sed "s/$regexp/$replacement/g"
Recuperar contenido web renderizado con un
navegador basado en texto
A veces, cuando recuperas datos de la web en la línea de comandos, puede
que no quieras el código HTML de una página web, sino una versión
renderizada de la página en texto. El texto renderizado puede ser más
fácil de analizar. Para realizar esta tarea, utiliza un navegador basado en
texto como lynx olinks. Los navegadores basados en texto muestran las
páginas web en un formato simplificado, sin imágenes ni otras funciones
extravagantes. La Figura 10-2muestra la página de códigos de área de la
sección anterior tal y como la muestra lynx.
Figura 10-2. lynx muestra la página [Link]
Tanto lynx como links descargan una página renderizada con la opción -
dump . Utiliza el programa que prefieras.
$ lynx -dump [Link] >
tempfile
$ cat tempfile
Area code test
Area code State Location
201 NJ Hackensack, Jersey City
202 DC Washington
⋮
203 CT New Haven, Stamford
CONSEJO
lynx y links también son estupendos para comprobar un enlace de aspecto
sospechoso cuando no estás seguro de si es legítimo o malicioso. Estos
navegadores basados en texto no admiten JavaScript ni muestran imágenes, por
lo que son menos vulnerables a los ataques. (Por supuesto, no pueden prometer
una seguridad total, así que utiliza tu mejor criterio).
Control del Portapapeles desde la Línea
de Comandos
Toda aplicación de software moderna con un menú Edición incluye las
operaciones cortar, copiar y pegar para transferir contenido dentro y
fuera del portapapeles del sistema. Puede que también conozcas los atajos
de teclado para estas operaciones. Pero, ¿sabías que puedes procesar el
portapapeles directamente desde la línea de comandos?
Primero un poco de contexto: las operaciones de copiar y pegar en Linux
forman parte de un mecanismo más general llamado selecciones X. Una
selección es un destino para el contenido copiado, como el portapapeles
del sistema. "X" es sólo el nombre del software de ventanas de Linux.
La mayoría de los entornos de escritorio Linux que se basan en X, como
GNOME, Unity, Cinnamon, y KDE Plasma, admiten dos selecciones.3 La
primera es elportapapeles, y funciona igual que los portapapeles de otros
sistemas operativos. Cuando ejecutas operaciones de cortar o copiar en
una aplicación, el contenido va al portapapeles, y lo recuperas con una
operación de pegar. Una selección X menos familiar es la
llamada selección primaria. Cuando seleccionas texto en determinadas
aplicaciones, se escribe en la selección primaria aunque no ejecutes una
operación de copia. Un ejemplo es resaltar texto en una ventana de
terminal con el ratón. Ese texto se escribe automáticamente en la
selección primaria.
NOTA
Si te conectas a un host Linux de forma remota mediante SSH o programas
similares, el copiar/pegar generalmente lo gestiona el ordenador local, no las
selecciones X del host Linux remoto.
La Tabla 10-2 enumera las operaciones de ratón y teclado para acceder a
las selecciones de X en el Terminal de GNOME (gnome-terminal) y en
Konsole de KDE (konsole). Si utilizas un programa de terminal diferente,
comprueba en su menú Edición los equivalentes de teclado para Copiar y
Pegar.
Operación Portapapeles Selección primaria
Copiar (ratón) Abre el menú del Haz clic y arrastra; o haz
botón derecho y doble clic para seleccionar
selecciona Copiar la palabra actual; o haz
triple clic para seleccionar
la línea actual
Pegar (ratón) Abre el menú del Pulsa el botón central del
botón derecho y ratón (normalmente la
selecciona Pegar rueda de desplazamiento)
Copiar (teclado) Ctrl-Shift-C n/a
Pegar Ctrl-Mayús-V o Cambiar-Insertar
(teclado), gnome- Ctrl-Mayús-
terminal Insertar
Pegar Ctrl-Mayús-V o Ctrl-Shift-Insertar
(teclado), konsole Mayús-Insertar
Tabla 10-2. Acceso a las selecciones X en los programas de terminal habituales
Conectar Selecciones a stdin y stdout
Linux proporciona un comando, xclip, que conecta las selecciones X a
stdin y stdout. Por tanto, puedes insertar operaciones de copiar y pegar en
pipelines y otros comandos combinados. Por ejemplo, puede que hayas
copiado texto en una aplicación como ésta:
1. Ejecuta un comando de Linux y redirige su salida a un archivo.
2. Ver el archivo.
3. Utiliza el ratón para copiar el contenido del archivo en el
portapapeles.
4. Pega el contenido en otra aplicación.
Con xclip, puedes acortar considerablemente el proceso:
1. Canaliza la salida de un comando Linux a xclip.
2. Pega el contenido en otra aplicación.
A la inversa, puede que hayas pegado texto en un archivo para procesarlo
con comandos Linux como éste:
1. Utiliza el ratón para copiar un montón de texto en un programa
de aplicación.
2. Pégalo en un archivo de texto.
3. Procesa el archivo de texto con comandos Linux.
Con xclip -o, puedes omitir el archivo de texto intermedio:
1. Utiliza el ratón para copiar un montón de texto en un programa
de aplicación.
2. Canaliza la salida de xclip -o a otros comandos de Linux para su
procesamiento.
ADVERTENCIA
Si estás leyendo este libro digitalmente en un dispositivo Linux y quieres probar
algunos de los comandos xclip de esta sección, no copies y pegues los
comandos en una ventana shell. Escribe los comandos a mano. ¿Por qué?
Porque tu operación de copia puede sobrescribir la misma selección X a la que
acceden los comandos con xclip, provocando que los comandos produzcan
resultados inesperados.
Por defecto, xclip lee stdin y escribe la selección primaria. Puede leer de
un archivo:
$ xclip < [Link]
o de una tubería:
$ echo "Efficient Linux at the Command Line" | xclip
Ahora imprime el texto en stdout, o canaliza el contenido de la selección a
otros comandos, como wc:
$ xclip -o Paste
to stdout
Efficient Linux at the Command Line
$ xclip -o > [Link] Paste
to a file
$ xclip -o | wc -w Count
words
6
Cualquier comando combinado que escriba en stdout puede enviar sus
resultados axclip, como éste del "Comando nº 6: uniq":
$ cut -f1 grades | sort | uniq -c | sort -nr | head -n1 |
cut -c9 | xclip
Borra la selección primaria estableciendo su valor en la cadena vacía
con echo -n:
$ echo -n | xclip
La opción -n es importante; de lo contrario, echo imprime un carácter de
nueva línea en stdout que acaba en la selección primaria.
Para copiar texto en el portapapeles en lugar de la selección principal,
ejecuta xclip con la opción -selection clipboard:
$ echo [Link] | xclip -selection clipboard
Copy
$ xclip -selection clipboard -o
Paste
[Link]
xclip Las opciones pueden abreviarse siempre que no sean ambiguas:
$ xclip -sel c -o Same as xclip -
selection clipboard -o
[Link]
Abre una ventana del navegador Firefox para visitar la URL anterior,
utilizandola sustitución de comandos:
$ firefox $(xclip -selection clipboard -o)
Linux ofrece otro comando, xsel, que también lee y escribe selecciones X.
Tiene algunas funciones extra, como borrar una selección (xsel -c) y
añadir a una selección (xsel -a). No dudes en leer la página de manual y
experimentar con xsel.
Mejorar el gestor de contraseñas
Utilicemos tus nuevos conocimientos de xclip para integrar las
selecciones X en el gestor de contraseñas pman de "Crear un gestor de
contraseñas". Cuando el script modificado pman coincide con una sola línea
del archivo [Link], escribe el nombre de usuario en el portapapeles y
la contraseña en la selección principal. Después, puedes rellenar una
página de inicio de sesión en la web, por ejemplo, pegando el nombre de
usuario con Ctrl-V y pegando la contraseña con el botón central del ratón.
ADVERTENCIA
Asegúrate de que no estás ejecutando un gestor del portapapeles o cualquier otra
aplicación que haga un seguimiento de las selecciones X y su contenido. De lo
contrario, los nombres de usuario y/o las contraseñas se hacen visibles en el
gestor del portapapeles, lo que supone un riesgo para la seguridad.
La nueva versión de pman está en el Ejemplo 10-3. pmanEl comportamiento
de 's ha cambiado de las siguientes maneras:
Una nueva función, load_password , carga el nombre de usuario y
la contraseña asociados en las selecciones X.
Si pman localiza una única coincidencia para la cadena de
búsqueda, ya sea por clave (campo 3) o por cualquier otra parte
de una línea, ejecuta load_password.
Si pman localiza varias coincidencias, imprime todas las claves y
notas (campos 3 y 4) de las líneas coincidentes para que el
usuario pueda volver a buscar por clave.
Ejemplo 10-3. Un script pman mejorado que carga el nombre de
usuario y la contraseña como selecciones
#!/bin/bash
PROGRAM=$(basename $0)
DATABASE=$HOME/etc/[Link]
load_password () {
# Place username (field 1) into clipboard
echo "$1" | cut -f1 | tr -d '\n' | xclip -selection
clipboard
# Place password (field 2) into X primary selection
echo "$1" | cut -f2 | tr -d '\n' | xclip -selection primary
# Give feedback to the user
echo "$PROGRAM: Found" $(echo "$1" | cut -f3- --output-
delimiter ': ')
echo "$PROGRAM: username and password loaded into X selections"
}
if [ $# -ne 1 ]; then
>&2 echo "$PROGRAM: look up passwords"
>&2 echo "Usage: $PROGRAM string"
exit 1
fi
searchstring="$1"
# Store the decrypted text in a variable
decrypted=$(gpg -d -q "$DATABASE")
if [ $? -ne 0 ]; then
>&2 echo "$PROGRAM: could not decrypt $DATABASE"
exit 1
fi
# Look for exact matches in the third column
match=$(echo "$decrypted" | awk '$3~/^'$searchstring'$/')
if [ -n "$match" ]; then
load_password "$match"
exit $?
fi
# Look for any match
match=$(echo "$decrypted" | awk "/$searchstring/")
if [ -z "$match" ]; then
>&2 echo "$PROGRAM: no matches"
exit 1
fi
# Count the matches
count=$(echo "$match" | wc -l)
case "$count" in
0)
>&2 echo "$PROGRAM: no matches"
exit 1
;;
1)
load_password "$match"
exit $?
;;
*)
>&2 echo "$PROGRAM: multiple matches for the following keys:"
echo "$match" | cut -f3
>&2 echo "$PROGRAM: rerun this script with one of the keys"
exit
;;
esac
Ejecuta el script:
$ pman dropbox
Passphrase: xxxxxxxx
pman: Found dropbox: [Link] account for work
pman: username and password loaded into X selections
$ pman account
Passphrase: xxxxxxxx
pman: multiple matches for the following keys:
google
dropbox
bank
dropbox2
pman: rerun this script with one of the keys
Las contraseñas permanecen en la selección primaria hasta que se
sobrescriben. Para borrar automáticamente la contraseña transcurridos
(digamos) 30 segundos, añade la siguiente línea a la
función load_password. La línea lanza una subshell en segundo plano que
espera 30 segundos y luego borra la selección primaria (poniéndola en la
cadena vacía). Ajusta el número 30 como creas conveniente.
(sleep 30 && echo -n | xclip -selection primary) &
Si definiste un atajo de teclado personalizado para lanzar ventanas de
terminal en "Conchas y navegadores instantáneos", ahora tienes una
forma rápida de acceder a tus contraseñas. Abre un terminal mediante la
tecla de acceso rápido, ejecuta pman, y cierra el terminal.
Resumen
Espero que este capítulo te haya animado a probar algunas técnicas
nuevas que mantengan tus manos en el teclado. Al principio pueden
parecer esforzadas, pero con la práctica se vuelven rápidas y automáticas.
Pronto serás la envidia de tus amigos de Linux cuando manipules con
fluidez las ventanas del escritorio, el contenido de la web y las selecciones
X de formas que las masas atadas al ratón no pueden.
1A menos que estés trabajando en una aplicación que capture todas las
pulsaciones, como una máquina virtual en una ventana.
2 Este ejemplo utiliza tres selectores CSS, pero algunas versiones antiguas
de hxselect sólo pueden manejar dos. Si tu versión de hxselect tiene este
inconveniente, descarga, la última versión del World Wide Web
Consortium, y compílala con el comando configure && make.
3 En realidad hay tres selecciones X, pero una de ellas, llamada selección
secundaria, rara vez está expuesta por los entornos de escritorio
modernos.
Capítulo 11. Ahorradores de tiempo
finales
Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y
comentarios: translation-feedback@[Link]
Me he divertido mucho escribiendo este libro, y espero que tú también te
hayas divertido leyéndolo. Para el último acto, vamos a cubrir un montón
de temas menores que no encajaban del todo en los capítulos anteriores.
Estos temas me han hecho mejor usuario de Linux, y quizá también te
ayuden a ti.
Ganancias rápidas
Los siguientes trucos para ahorrar tiempo son fáciles de aprender en
pocos minutos.
Saltar a tu editor desde menos
Cuando estés viendo un archivo de texto con less y quieras editarlo, no
salgas de less. Simplemente pulsa v para iniciar tu editor de texto
preferido. Cargará el archivo y colocará el cursor justo en el lugar que
estabas viendo en less. Sal del editor y estarás de nuevo en less en la
ubicación original.
Para que este truco funcione mejor, establece la variable de
entorno EDITOR y/o VISUAL a un comando de edición. Estas variables de
entorno representan tu editor de texto predeterminado de Linux, que
puede ser lanzado por varios comandos,
incluyendo less, lynx, git , crontab , y numerosos programas de correo
electrónico. Por ejemplo, para establecer emacscomo tu editor
predeterminado, coloca cualquiera de las siguientes líneas (o ambas) en
un archivo de configuración del intérprete de comandos y ejecútalo:
VISUAL=emacs
EDITOR=emacs
Si no estableces estas variables, tu editor por defecto será el que
establezca tu sistema Linux, que suele ser vim. Si acabas dentro de vim y no
sabes cómo utilizarlo, que no cunda el pánico. Sal devim pulsando la tecla
Escape y escribiendo :q! (dos puntos, la letraq y un signo de exclamación),
luego pulsa Intro. Para salir de emacs, pulsa Ctrl-X seguido de Ctrl-C.
Editar archivos que contienen una cadena
determinada
¿Quieres editar todos los archivos del directorio actual que contengan una
determinada cadena (o expresión regular)? Genera una lista de nombres
de archivo con grep -l y pásalos a tu editor con la sustitución de
comandos. Suponiendo que tu editor sea vim, el comando es el siguiente:
$ vim $(grep -l string *)
Edita todos los archivos que contengan cadena en todo un árbol de
directorios (directorio actual y todos los subdirectorios) añadiendo la
opción -r (recursiva) a grep y comenzando en el directorio actual (el
punto):
$ vim $(grep -lr string .)
Para realizar búsquedas más rápidas en árboles de directorios grandes,
utiliza find con xargsen lugar de grep -r:
$ vim $(find . -type f -print0 | xargs -0 grep -l string)
"Técnica nº 3: Sustitución de comandos" ya se ha referido a esta técnica,
pero quería hacer hincapié en ella porque es muy útil. Recuerda tener
cuidado con los nombres de archivo que contengan espacios y otros
caracteres especiales del intérprete de comandos, ya que pueden alterar
los resultados, como se explica en "Caracteres especiales y sustitución de
comandos".
Aceptar los errores tipográficos
Si escribes mal un comando constantemente, define alias para tus errores
más comunes, de modo que el comando correcto se ejecute de todos
modos:
alias firfox=firefox
alias les=less
alias meacs=emacs
Ten cuidado de no ensombrecer (anular) accidentalmente un comando
Linux existente definiendo un alias con el mismo nombre. Busca primero
el alias que propones con el comando which o type (consulta"Localización
de programas a ejecutar"), y ejecuta el comando man para asegurarte de
que no hay ningún otro comando con el mismo nombre:
$ type firfox
bash: type: firfox: not found
$ man firfox
No manual entry for firfox
Crear archivos vacíos rápidamente
Hay varias formas de crear archivos vacíos en Linux. El comando touch ,
que actualiza la fecha y hora de un archivo, también crea un archivo si
aún no existe:
$ touch newfile1
touch es genial para crear un gran número de archivos vacíos para hacer
pruebas:
$ mkdir tmp Create a directory
$ cd tmp
$ touch file{0000..9999}.txt Create 10,000 files
$ cd ..
$ rm -rf tmp Remove the directory
and files
El comando echo crea un archivo vacío si rediriges su salida a un archivo,
pero sólo si proporcionas la opción -n:
$ echo -n > newfile2
Si olvidas la opción -n, el archivo resultante contiene un carácter, una
nueva línea, por lo que no está vacío.
Procesar un fichero línea a línea
Cuando necesites procesar un archivo línea a línea, cat el archivo en un
bucle while read :
$ cat myfile | while read line; do
...do something here...
done
Por ejemplo, para calcular la longitud de cada línea de un archivo,
como/etc/hosts, canaliza cada línea a wc -c:
$ cat /etc/hosts | while read line; do
echo "$line" | wc -c
done
65
31
⋮
1
Un ejemplo más práctico de esta técnica está en el Ejemplo 9-3.
Identificar los comandos que admiten la recursión
En "El comando find", introduje find -exec, que aplica cualquier comando
de Linux a todo un árbol de directorios de forma recursiva:
$ find . -exec your command here \;
Algunos otros comandos admiten la recursividad por sí mismos, y si los
conoces, puedes ahorrar tiempo de escritura utilizando su recursividad
nativa en lugar de construir un comando find:
ls -R
Para listar directorios y su contenido de forma recursiva
cp -ro cp -a
Para copiar directorios y su contenido de forma recursiva
rm -r
Para borrar directorios y su contenido de forma recursiva
grep -r
Para buscar por expresión regular en todo un árbol de directorios
chmod -R
Para cambiar recursivamente las protecciones de los archivos
chown -R
Para cambiar la propiedad de un archivo de forma recursiva
chgrp -R
Para cambiar la propiedad del grupo de archivos de forma
recursiva
Leer una página de manual
Elige un comando común, como cut o grep, y lee detenidamente su página
de manual. Probablemente descubrirás una o dos opciones que nunca has
utilizado y que te resultarán valiosas. Repite esta actividad de vez en
cuando para pulir y ampliar tu caja de herramientas Linux.
Aprendizaje más largo
Las siguientes técnicas requieren un verdadero esfuerzo de aprendizaje,
pero te compensarán en tiempo ahorrado. Te ofrezco sólo una muestra de
cada tema, no para enseñarte los detalles, sino para incitarte a descubrir
más por tu cuenta.
Lee la página principal de bash
Ejecuta man bash para ver la documentación oficial completa en bash, y
léela entera: sí, las 46.318 palabras que contiene:
$ man bash | wc -w
46318
Tómate unos días. Trabaja en ello lentamente. Seguro que aprenderás
muchas cosas que te facilitarán el uso diario de Linux.
Aprende cron, crontab y en
En "Un primer ejemplo: Encontrar archivos", hay una breve nota sobre la
programación de comandos para que se ejecuten automáticamente en el
futuro a intervalos regulares. Te recomiendo que aprendas el
programa crontab para configurar comandos programados para ti. Por
ejemplo, podrías hacer una copia de seguridad de los archivos en una
unidad externa de forma programada, o enviarte recordatorios por
correo electrónico para un evento mensual.
Antes de ejecutar crontab, define tu editor por defecto como se muestra
en"Saltar a tu editor desde less". A continuación, ejecuta crontab -e para
editar tu archivo personal de comandos programados. crontab lanza tu
editor por defecto y abre un archivo vacío para especificar los comandos.
Ese archivo se llama tu crontab.
Brevemente, un comando programado en un archivo crontab, a menudo
llamado trabajo cron, consta de seis campos, todos en una sola línea
(posiblemente larga). Los cinco primeros campos determinan la
programación del trabajo por minuto, hora, día del mes, mes y día de la
semana, respectivamente. El sexto campo es el comando Linux a ejecutar.
Puedes lanzar un comando cada hora, cada día, cada semana, cada mes,
cada año, en determinados días u horas, o en otras disposiciones más
complejas. Algunos ejemplos son:
* * * * * command Run command every minute
30 7 * * * command Run command at 07:30 every
day
30 7 5 * * command Run command at 07:30 the
5th day of every month
30 7 5 1 * command Run command at 07:30 every
January 5
30 7 * * 1 command Run command at 07:30 every
Monday
Una vez que hayas creado los seis campos, guardado el archivo y salido
del editor, el comando se lanzará automáticamente (mediante un
programa llamadocron) según la programación que hayas definido. La
sintaxis de las programaciones es breve y críptica, pero está bien
documentada en la página de manual (man 5 crontab) y en numerosos
tutoriales en línea (busca cron tutorial).
También te recomiendo que aprendas el comando at, que programa
comandos para que se ejecuten una vez, en lugar de repetidamente, en
una fecha y hora determinadas. Ejecutaman at para más detalles. Aquí
tienes un comando que te envía un recordatorio por correo electrónico
mañana a las 10 de la noche para que te laves los dientes:
$ at 22:00 tomorrow
warning: commands will be executed using /bin/sh
at> echo brush your teeth | mail $USER
at> ^D Type Ctrl-D
to end input
job 699 at Sun Nov 14 22:00:00 2021
Para listar tus trabajos pendientes en at, ejecuta atq:
$ atq
699 Sun Nov 14 22:00:00 2021 a smith
Para ver los comandos de un trabajo de at, ejecuta at -c con el número de
trabajo e imprime las últimas líneas:
⋮
$ at -c 699 | tail
echo brush your teeth | mail $USER
Para eliminar un trabajo pendiente antes de que se ejecute,
ejecuta atrm con el número de trabajo:
$ atrm 699
Aprende rsync
Para copiar un directorio completo, incluidos sus subdirectorios, de una
ubicación de disco a otra, muchos usuarios de Linux recurren al
comando cp -r o cp -a:
$ cp -a dir1 dir2
cp hace bien el trabajo la primera vez, pero si más tarde modificas
algunos archivos del directorio dir1 y vuelves a realizar la copia, cp es un
derroche. Vuelve a copiar todos los archivos y directorios de dir1, aunque
ya existan copias idénticas en dir2.
El comando rsync es una copia más inteligente del programa. Sólo copia
las diferencias entre el primer y el segundo directorio.
$ rsync -a dir1/ dir2
NOTA
La barra oblicua en el comando anterior significa copiar los archivos dentro
de dir1. Sin la barra, rsync copiaría el propio dir1, creando dir2/dir1.
Si más tarde añades un archivo al directorio dir1, rsync copia sólo ese
archivo. Si cambias una línea dentro de un archivo en dir1, ¡ rsynccopia
esa única línea! Es un gran ahorro de tiempo cuando se copian árboles de
directorios grandes varias veces. rsync puede incluso copiar a un servidor
remoto a través de una conexión SSH.
rsync tiene decenas de opciones. Éstas son algunas especialmente útiles:
-v (que significa "verboso")
Para imprimir los nombres de los archivos a medida que se copian
-n
Fingir que se copia; combinar con -v para ver qué
archivos se copiarían
-x
Para indicar a rsync que no cruce los límites del sistema de archivos
Te recomiendo encarecidamente que te familiarices con rsync para que la
copia sea más eficaz. Lee la página de manual y mira ejemplos en el
artículo "Rsync Examples in Linux" de Korbin Brown.
Aprende otro lenguaje de programación
Los scripts de shell son prácticos y potentes, pero tienen algunos defectos
graves. Por ejemplo, son terribles a la hora de manejar nombres de
archivo que contienen caracteres de espacio en blanco. Considera este
breve script de bash que intenta eliminar un archivo:
#!/bin/bash
BOOKTITLE="Slow Inefficient Linux"
rm $BOOKTITLE # Wrong! Don't do this!
Parece que la segunda línea elimina un archivo llamado Lento
Ineficiente Linux, pero no es así. Intenta eliminar tres archivos
llamados Lento, Ineficiente y Linux. El shell expande la
variable $BOOKTITLE antes de llamar a rm, y su expansión son tres palabras
separadas por espacios en blanco, como si hubieras escrito lo siguiente:
rm Slow Inefficient Linux
A continuación, el intérprete de comandos invoca rm con tres argumentos,
y se produce un desastre potencial, ya que elimina los archivos
equivocados. Un comando de eliminación correcto
rodearía $BOOKTITLE con comillas dobles:
rm "$BOOKTITLE"
a la que se expande el caparazón:
rm "Slow Inefficient Linux"
Este tipo de rareza sutil y potencialmente destructiva es sólo un ejemplo
de lo inadecuado que es el shell scripting para proyectos serios. Por eso,
recomiendo aprender un segundo lenguaje de scripting, como Perl, PHP,
Python, o Ruby. Todos ellos manejan correctamente los espacios en
blanco. Todos soportan estructuras de datos reales. Todos tienen potentes
funciones de gestión de cadenas. Todos hacen cálculos fácilmente. La lista
de ventajas continúa.
Utiliza el shell para lanzar comandos complejos y crear scripts sencillos,
pero para tareas más sustanciales, recurre a otro lenguaje. Prueba uno de
los muchos tutoriales de idiomas que hay en Internet.
Utilizar make para tareas no relacionadas con la
programación
El programa make actualiza automáticamente los archivos basándose en
reglas. Está diseñado para acelerar el desarrollo de software, pero con un
poco de esfuerzo,make puede simplificar otros aspectos de tu vida en
Linux.
Supón que tienes tres archivos
llamados capí[Link], capí[Link] ycapí[Link] en los que trabajas
por separado. También tienes un cuarto archivo, libro. txt, que es una
combinación de los tres archivos de capítulo. Cada vez que cambie un
capítulo, tienes que recombinarlos y [Link], quizás con un
comando como éste:
$ cat [Link] [Link] [Link] > [Link]
Esta situación es perfecta para utilizar make. Lo tienes:
Un montón de archivos
Una regla que relacione los archivos, es decir,
que [Link] necesite una actualización cada vez que cambie
cualquier archivo de capítulo
Un comando que realiza la actualización
make funciona leyendo un archivo de configuración, normalmente
llamadoMakefile, que está lleno de reglas y comandos. Por ejemplo, la
siguiente regla de Makefile establece que [Link] depende de los tres
archivos de capítulo:
[Link]: [Link] [Link] [Link]
Si el objetivo de la regla (en este caso [Link]) es más antiguo que
cualquiera de sus dependencias (los archivos de los capítulos),
entonces make considera que el objetivo está desactualizado. Si
proporcionas un comando en la línea después de la regla,make ejecuta el
comando para actualizar el objetivo:
[Link]: [Link] [Link] [Link]
cat [Link] [Link] [Link] >
[Link]
Para aplicar la regla, basta con ejecutar el comando make:
$ ls
Makefile [Link] [Link] [Link]
$ make
cat [Link] [Link] [Link] > [Link]
Executed by make
$ ls
Makefile [Link] [Link] [Link]
[Link]
$ make
make: '[Link]' is up to date.
$ vim [Link]
Update a chapter
$ make
cat [Link] [Link] [Link] > [Link]
make fue desarrollado para programadores, pero con un poco de estudio,
puedes utilizarlo para tareas ajenas a la programación. Siempre que
necesites actualizar archivos que dependan de otros archivos,
probablemente puedas simplificar tu trabajo escribiendo unMakefile.
make me ayudaron a escribir y depurar este libro. Escribí el libro en un
lenguaje tipográfico llamado AsciiDoc y convertía regularmente los
capítulos a HTML para verlos en un navegador. He aquí una regla
de make para convertir cualquier archivo AsciiDoc en un archivo HTML:
%.html: %.asciidoc
asciidoctor -o $@ $<
Significa: para crear un archivo con la extensión .html (%.html), busca un
archivo correspondiente con la extensión .asciidoc(%.asciidoc). Si el
archivo HTML es más antiguo que el archivo AsciiDoc, regenera el archivo
HTML ejecutando el comando asciidoctor en el archivo dependiente ($<),
enviando la salida al archivo HTML de destino ( -o $@). Con esta regla,
ligeramente críptica pero breve, escribo un simple comando make para
crear la versión HTML del capítulo que estás leyendo
ahora. make lanza asciidoctor para realizar la actualización:
$ ls ch11*
[Link]
$ make [Link]
asciidoctor -o [Link] [Link]
$ ls ch11*
[Link] [Link]
$ firefox [Link] View the
HTML file
Se tarda menos de una hora en dominar razonablemente makepara
realizar pequeñas tareas. Merece la pena el esfuerzo. Hay una guía útil
[Link].
Aplica el control de versiones a los archivos
cotidianos
¿Alguna vez has querido editar un archivo pero temías que tus cambios
pudieran estropearlo? Tal vez hiciste una copia de seguridad para
guardarla y editaste el original, sabiendo que podrías restaurar la copia
de seguridad si cometías un error:
$ cp myfile [Link]
Esta solución no es escalable. ¿Qué pasa si tienes docenas o cientos de
archivos y docenas o cientos de personas trabajando en ellos? Los
sistemas de control de versiones como Git y Subversion se inventaron
para resolver este problema en general, haciendo un seguimiento cómodo
de las múltiples versiones de un archivo.
Git está muy extendido para mantener el código fuente del software, pero
te recomiendo que lo aprendas y lo utilices para cualquier archivo de
texto importante en el que importen tus cambios. Quizá sean archivos
personales, o archivos del sistema operativo en /etc. "Viajando con tu
entorno" sugiere mantener tus archivos de configuración de bash con
control de versiones.
Utilicé Git mientras escribía este libro para poder probar distintas formas
de presentar el material. Sin demasiado esfuerzo, creé y mantuve tres
versiones diferentes del libro: una para el manuscrito completo hasta el
momento, otra que contenía sólo los capítulos que había enviado a mi
editor para su revisión, y otra para el trabajo experimental en el que
probaba nuevas ideas. Si no me gustaba lo que escribía, un simple
comando restauraba una versión anterior.
Enseñar Git está fuera del alcance de este libro, pero aquí tienes algunos
comandos de ejemplo para mostrarte el flujo de trabajo básico y abrirte el
apetito. Convierte el directorio actual (y todos sus subdirectorios) en un
repositorio Git:
$ git init
Edita algunos archivos. Después, añade los archivos modificados a un
"área de preparación" invisible, una operación que declara tu intención
de crear una nueva versión:
$ git add .
Crea la nueva versión, proporcionando un comentario para describir tus
cambios en los archivos:
$ git commit -m"Changed X to Y"
Ver tu historial de versiones:
$ git log
Hay mucho más, como recuperar versiones antiguas de archivos y
guardar(empujar) versiones a otro servidor. Consigue un tutorial degit ,
¡y empieza!
Adiós
Muchas gracias por acompañarme a lo largo de este libro. Espero que
haya cumplido la promesa que hice en el prefacio de llevar tus
habilidades con la línea de comandos de Linux al siguiente nivel.
Cuéntame tu experiencia en dbarrett@[Link]. Feliz informática.
Apéndice A. Repaso de Linux
Si tus conocimientos de Linux están oxidados, aquí tienes un repaso muy
rápido de algunos detalles que necesitarás para este libro. (Si eres un
completo principiante, este repaso puede ser demasiado breve. Consulta
las lecturas recomendadas al final).
Comandos, argumentos y opciones
Para ejecutar un comando Linux en la línea de comandos, escribe el
comando y pulsa Intro. Para matar un comando en curso, pulsa Ctrl-C.
Un comando Linux sencillo consta de una sola palabra, que suele ser el
nombre de un programa, seguida de cadenas adicionales
llamadasargumentos. Por ejemplo, el siguiente comando consta de un
nombre de programa, ls, y dos argumentos:
$ ls -l /bin
Los argumentos que empiezan por un guión,como -l, se
denominan opciones porque cambian el comportamiento del comando.
Otros argumentos pueden ser nombres de archivo, nombres de directorio,
nombres de usuario, nombres de host o cualquier otra cadena que
necesite el programa. Las opciones suelen preceder (aunque no siempre)
al resto de argumentos.
Las opciones de comando se presentan de varias formas, dependiendo del
programa que ejecutes:
Una sola letra, como -l, a veces seguida de un valor, como en -n
10. Normalmente se puede omitir el espacio entre la letra y el
valor: -n10.
Una palabra precedida de dos guiones, como --long, a veces
seguida de un valor, como en --block-size 100. El espacio entre
la opción y su valor puede sustituirse a menudo por un signo
igual: --block-size=100.
Una palabra precedida de un guión, como -type, seguida
opcionalmente de un valor, como en -type f. Este formato de
opción es poco frecuente; un comando que lo utiliza es find.
Una sola letra sin guión. Este formato de opción es poco
frecuente; un comando que lo utiliza es tar.
A menudo se pueden combinar varias opciones (dependiendo del
comando)detrás de un solo guión. Por ejemplo, el comando ls -
al equivale a ls -a -l.
Las opciones varían no sólo en apariencia, sino también en significado. En
el comando ls -l, el -l significa "salida larga", pero en el comando wc -
l significa "líneas de texto". Dos programas también pueden utilizar
opciones diferentes para significar lo mismo, como -q para "ejecutar
silenciosamente" frente a -spara "ejecutar silenciosamente".
Inconsistencias como éstas hacen que Linux sea más difícil de aprender,
pero al final te acostumbras a ellas.
El sistema de archivos, directorios y
rutas
Los archivos de Linux están contenidos en directorios (carpetas) que se
organizan en una estructura de árbol, como la de la Figura A-1. El árbol
comienza en un directorio llamado raíz, denotado por una única barra
oblicua (/),que puede contener archivos y otros directorios,
llamadossubdirectorios. Por ejemplo, el directorio Música tiene dos
subdirectorios, mp3 y SheetMusic. Llamamos Música al directorio
padre de mp3 y SheetMusic. Los directorios con el mismo padre se
llaman hermanos.
Una ruta a través del árbol se escribe como una secuencia de nombres de
directorio separados por barras
inclinadas, como /home/smith/Music/mp3. Una ruta también puede
terminar con un nombre de archivo, como
en/home/smith/Music/mp3/[Link]. Estas rutas se denominan rutas
absolutas porque comienzan en el directorio raíz. Las rutas que
comienzan en otro lugar (y no empiezan con una barra diagonal) se
denominan rutas relativas porque son relativas al directorio actual. Si tu
directorio actual es /home/smith/Música, entonces algunas rutas relativas
sonmp3 (un subdirectorio) y mp3/[Link] (un archivo). Incluso un
nombre de archivo por sí mismo, como [Link], es una ruta relativa
con respecto a/home/smith/Music/mp3.
Dos rutas relativas especiales son un solo punto (.), que se refiere al
directorio actual, y dos puntos seguidos (..), que se refiere al directorio
padre del directorio actual.1 Ambos pueden formar parte de rutas
mayores. Por ejemplo, si tu directorio actual es /home/smith/Music/mp3,
entonces la ruta.. se refiere a Música, la ruta ../../../.. se refiere al
directorio raíz, y la ruta ../SheetMusic se refiere a un hermano de mp3.
Figura A-1. Rutas en un árbol de directorios de Linux
Tú y cualquier otro usuario de un sistema Linux tenéis un directorio
designado, llamado vuestro directorio personal, donde podéis crear,
editar y borrar archivos y directorios libremente. Su ruta suele
ser /home/ seguido de tu nombre de usuario, como /home/smith.
Movimiento del Directorio
En todo momento, tu línea de comandos (shell) opera en un directorio
determinado, denominado directorio actual, directorio de
trabajo, odirectorio de trabajo actual. Visualiza la ruta de tu directorio
actual con el comando pwd (imprimir directorio de trabajo):
$ pwd
/home/smith The home directory of
user smith
Muévete entre directorios con el comando cd (cambiar directorio),
proporcionando la ruta (absoluta o relativa) al destino que desees:
$ cd /usr/local Absolute path
$ cd bin Relative path leading to
/usr/local/bin
$ cd ../etc Relative path leading to
/usr/local/etc
Crear y editar archivos
Edita los archivos con un editor de texto estándar de Linux ejecutando
cualquiera de los siguientescomandos:
emacs
Una vez que emacs se esté ejecutando, teclea Ctrl-h seguido de t
para ver un tutorial.
nano
Visita [Link] para consultar la documentación.
vim o vi
Ejecuta el comando vimtutor para obtener un tutorial.
Para crear un archivo, basta con proporcionar su nombre como
argumento, y el editor lo crea:
$ nano [Link]
Alternativamente, crea un archivo vacío con el comando touch ,
proporcionando el nombre de archivo deseado como argumento:
$ touch [Link]
$ ls
[Link]
Manejo de archivos y directorios
Lista los archivos de un directorio (por defecto, tu directorio actual) con el
comandols :
$ ls
[Link]
Consulta los atributos de un archivo o directorio con un listado "largo" (ls
-l):
$ ls -l
-rw-r--r-- 1 smith smith 325 Jul 3 17:44 [Link]
De izquierda a derecha, los atributos son los permisos de archivo (-rw-r—
r--) descritos en "Permisos de archivo", el propietario (smith) y el grupo
(smith), el tamaño en bytes (325), la fecha y hora de la última
modificación (Jul 3 de este año en 17:44), y el nombre del archivo
([Link]).
Por defecto, ls no imprime los nombres de archivos que empiezan por
punto. Para listar estos archivos, que suelen denominarse archivos de
puntos o archivos ocultos, añade la opción -a:
$ ls -a
.bashrc .bash_profile [Link]
Copia un archivo con el comando cp , indicando el nombre del archivo
original y el del nuevo:
$ cp [Link] [Link]
$ ls
[Link] [Link]
Cambia el nombre de un archivo con el comando mv (mover),
proporcionando el nombre original del archivo y el nuevo nombre:
$ mv [Link] [Link]
$ ls
[Link] [Link]
Elimina un archivo con el comando rm (eliminar):
$ rm [Link]
ADVERTENCIA
El borrado en Linux no es amigable. El comando rm no pregunta "¿Estás seguro?"
y no hay papelera para restaurar archivos.
Crea un directorio con mkdir, renómbralo con mv, y elimínalo (si está
vacío) con rmdir:
$ mkdir testdir
$ ls
[Link] testdir
$ mv testdir newname
$ ls
[Link] newname
$ rmdir newname
$ ls
[Link]
Copia uno o varios archivos (o directorios) en un directorio:
$ touch file1 file2 file3
$ mkdir dir
$ ls
dir file1 file2 file3
$ cp file1 file2 file3 dir
$ ls
dir file1 file2 file3
$ ls dir
file1 file2 file3
$ rm file1 file2 file3
Continuando, mueve uno o más archivos (o directorios) a un directorio:
$ touch thing1 thing2 thing3
$ ls
dir thing1 thing2 thing3
$ mv thing1 thing2 thing3 dir
$ ls
dir
$ ls dir
file1 file2 file3 thing1 thing2 thing3
Elimina un directorio y todo su contenido con rm -rf. Ten cuidado antes
de ejecutar este comando porque no es reversible. Consulta "No vuelvas a
eliminar el archivo equivocado (gracias a la ampliación del
historial)" para obtener consejos de seguridad.
$ rm -rf dir
Visualización de archivos
Imprime un archivo de texto en la pantalla con el comando cat :
$ cat [Link]
Visualiza un archivo de texto pantalla a pantalla con el comando less :
$ less [Link]
Mientras se ejecuta less, visualiza la página siguiente pulsando la barra
espaciadora. Para salir deless, pulsa q. Para obtener ayuda, pulsa h.
Permisos de archivos
El comando chmod establece que un archivo sea legible, escribible y/o
ejecutable por ti mismo, por un grupo determinado de usuarios o por todo
el mundo. La Figura A-2 es un breve repaso alos permisos de los archivos.
Figura A-2. Bits de permiso de archivos
He aquí algunas operaciones habituales con chmod. Haz que un archivo
sea legible y escribible por ti, y sólo legible por los demás:
$ chmod 644 [Link]
$ ls -l
-rw-r--r-- 1 smith smith 325 Jul 3 17:44 [Link]
Protégelo de todos los demás usuarios:
$ chmod 600 [Link]
$ ls -l
-rw------- 1 smith smith 325 Jul 3 17:44 [Link]
Crea un directorio legible y en el que todo el mundo pueda entrar, pero en
el que sólo puedas escribir tú:
$ mkdir dir
$ chmod 755 dir
$ ls -l
drwxr-xr-x 2 smith smith 4096 Oct 1 12:44 dir
Protege un directorio de todos los demás usuarios:
$ chmod 700 dir
$ ls -l
drwx------ 2 smith smith 4096 Oct 1 12:44 dir
Los permisos normales no se aplican al superusuario, que puede leer y
escribir en todos los archivos y directorios del sistema.
Procesos
Cuando ejecutas un comando Linux, lanza uno o másprocesos Linux,
cada uno con un ID numérico de proceso llamado PID. Consulta los
procesos actuales de tu shell con elcomando ps :
$ ps
PID TTY TIME CMD
5152 pts/11 00:00:00 bash
117280 pts/11 00:00:00 emacs
117273 pts/11 00:00:00 ps
o todos los procesos en ejecución de todos los usuarios con:
$ ps -uax
Mata un proceso propio con el comando kill , proporcionando el PID
como argumento. El superusuario (administrador de Linux) puede matar
el proceso de cualquier usuario.
$ kill 117280
[1]+ Exit 15 emacs [Link]
Visualización de la documentación
El comando man imprime documentación sobre cualquier comando
estándar de tu sistema Linux. Simplemente escribe man seguido del
nombre del comando. Por ejemplo, para ver la documentación del
comando cat, ejecuta lo siguiente:
$ man cat
El documento mostrado se conoce como la página de manual del
comando. Cuando alguien dice "ver la página del manual de grep", se
refiere a ejecutar el comandoman grep.
man muestra la documentación página a página utilizando el
programaless,2 por lo que las pulsaciones estándar
de lessfuncionarán. La Tabla A-1 enumera algunas pulsaciones comunes.
Pulsación de
Acción
teclas
h Ayuda: muestra una lista de combinaciones de
teclas para less
barra Ver la página siguiente
espaciadora
b Ver la página anterior
Pulsación de
Acción
teclas
Entra en Baja una línea
< Saltar al principio del documento
> Saltar al final del documento
/ Buscar texto hacia delante (escribe el texto y
pulsa Intro)
? Buscar texto hacia atrás (escribe el texto y pulsa
Intro)
n Localiza la siguiente aparición del texto buscado
q Deja man
Tabla A-1. Algunas combinaciones de teclas para ver páginas de manual con less
Scripts de Shell
Para ejecutar un grupo de comandos Linux como una unidad, sigue estos
pasos:
1. Coloca los comandos en un archivo.
2. Inserta una primera línea mágica.
3. Haz que el archivo sea ejecutable con chmod.
4. Ejecuta el archivo.
El archivo se llama script o guión de shell. La primera línea mágica
deben ser los símbolos #! (pronunciado "shebang") seguidos de la ruta a
un programa que lea y ejecute el script:3
#!/bin/bash
Aquí tienes un script de shell que dice hola e imprime la fecha de hoy. Las
líneas que empiezan por # son comentarios:
#!/bin/bash
# This is a sample script
echo "Hello there!"
date
Utiliza un editor de texto para guardar estas líneas en un archivo
llamado howdy. A continuación, haz que el archivo sea ejecutable
ejecutando cualquiera de estos comandos:
$ chmod 755 howdy Set all permissions, including
execute permission
$ chmod +x howdy Or, just add execute permission
y ejecútalo:
$ ./howdy
Hello there!
Fri Sep 10 17:00:52 EDT 2021
El punto y la barra (./) indican que el script está en tu directorio actual.
Sin ellos, el shell de Linux no encontrará el script:4
$ howdy
howdy: command not found
Los shells de Linux proporcionan algunas características del lenguaje de
programación que son útiles en los scripts. bash, por ejemplo,
proporcionaif sentencias,for bucles, while bucles, y otras estructuras de
control. A lo largo del libro aparecen algunos ejemplos. Consulta man
bash para ver la sintaxis.
Convertirse en superusuario
Algunos archivos, directorios y programas están protegidos de los
usuarios normales, incluido tú:
$ touch /usr/local/avocado Try to create a file
in a system directory
touch: cannot touch '/usr/local/avocado': Permission
denied
"Permiso denegado" suele significar que has intentado acceder a recursos
protegidos. Sólo puede acceder a ellos el superusuario de Linux (nombre
de usuarioroot). La mayoría de los sistemas Linux vienen con un
programa llamado sudo (pronunciado "soo doo") que te permite
convertirte en superusuario mientras dure un único comando. Si
instalaste Linux tú mismo, probablemente tu cuenta ya esté configurada
para ejecutar sudo. Si eres un usuario en el sistema Linux de otra
persona, puede que no tengas privilegios de superusuario; habla con el
administrador del sistema si no estás seguro.
Suponiendo que estás configurado correctamente, simplemente
ejecuta sudo, proporcionándole el comando deseado para ejecutarlo
como superusuario. Se te pedirá tu contraseña de inicio de sesión para
probar tu identidad. Introdúcela correctamente y el comando se ejecutará
con privilegios de root:
$ sudo touch /usr/local/avocado Create
the file as root
[sudo] password for smith: password here
$ ls -l /usr/local/avocado List the
file
-rw-r--r-- 1 root root 0 Sep 10 17:16 avocado
$ sudo rm /usr/local/avocado Clean up
as root
sudo puede recordar (almacenar en caché) tu frase de contraseña durante
un tiempo, dependiendo de cómo esté configurado sudo, por lo que puede
que no te pregunte cada vez.
Otras lecturas
Para más información básica sobre el uso de Linux, lee mi libro
anterior, Linux Pocket Guide (O'Reilly), o busca tutoriales en Internet.
1 El punto y el doble punto no son expresiones evaluadas por el shell. Son
enlaces duros presentes en todos los directorios.
2 O en otro programa si redefines el valor de la variable de shell PAGER.
3 Si omites la línea shebang, tu shell por defecto ejecutará el script. Es una
buena práctica incluir la línea.
4 Esto se debe a que el directorio actual suele omitirse de la ruta de
búsqueda del intérprete de comandos, por razones de seguridad. De lo
contrario, un atacante podría colocar un script malicioso y ejecutable
llamado ls en tu directorio actual, y cuando ejecutaras ls, el script se
ejecutaría en lugar del verdadero comando ls.
Apéndice B. Si utilizas un shell
diferente
Este libro asume que tu shell de inicio de sesión es bash, pero si no lo es,la
Tabla B-1 puede ayudarte a adaptar los ejemplos del libro para otros
shells. El símbolo ✓ indica compatibilidad: la función en cuestión es lo
suficientemente similar a la de bashcomo para que los ejemplos del libro
se ejecuten correctamente. Sin embargo, el comportamiento de la función
puede diferir del de bashen otros aspectos. Lee atentamente las notas a
pie de página.
NOTA
Independientemente de cuál sea tu shell de inicio de sesión, los scripts que
empiezan por#!/bin/bash son procesados por bash.
Para experimentar con otra shell instalada en tu sistema, simplemente
ejecútala por su nombre (por ejemplo, ksh) y pulsa Ctrl-D al terminar.
Para cambiar tu shell de inicio de sesión, lee man chsh.
función gui
pescado ksh tcsh zsh
bash ón
alias incor ✓ ✓, ✓ No hay ✓
porado pero ali signo
as name igual:
no alias
imprime g
grep
el alias
Fondo con & ✓ ✓ ✓ ✓ ✓
bash -c das fish -c ksh - tcsh zsh -c
h - c -c
c
bash coman das fish ksh tcsh zsh
do h
bash ubicac / /bin/ /bin/ /bin/ /bin/zsh
ión bin/ pescado ksh tcsh
das
en /bin/bas
h
h
función gui
pescado ksh tcsh zsh
bash ón
BASH_SUBS
HELL variab
le
Ampliación Utili Sólo {a, ✓ Utiliza ✓
del corsé za s b,c}, seq
con {} eq no {a..c
}
cd ✓ ✓ ✓ ✓ ✓
- (alternar
directorios)
cd incorpor ✓ ✓ ✓ ✓ ✓
ado
CDPATH vari ✓ set ✓ set ✓
able CDPATH cdpat
value h
= (dir
1
dir2
…)
Sustitución ✓ Utiliza ( ✓ Utiliza ✓
de ) comill
comandos as
con $() invers
as
Sustitución ✓ Utiliza ( ✓ ✓ ✓
de )
comandos
con comillas
Edición de ✓ ✓a ✓ ✓
la línea de
función gui
pescado ksh tcsh zsh
bash ón
comandos
con las
teclas de
flecha
Edición de ✓ ✓a ✓ ✓
línea de
comandos
con teclas
Emacs
Edición de ✓ Ejecut ✓
línea de a bind
comandos key -
con teclas v
Vim con set
-o vi
complete i sintaxis sintax sintaxi compdefb
ncorporado diferente is s
b difere difere
nteb nteb
Listas ✓ ✓ ✓ ✓ ✓
condicional
es
con && y ||
Archivos de .pe .config/ .profil .tcshr .zshenv, .z
configuració rfil pescado e, .ks c, .csh profile, .zs
/ hrc rc, .log
n hrc, .zlogin,
config.p in
en $HOME (le escado
.zlogout
e la página
de manual
para más
detalles)
función gui
pescado ksh tcsh zsh
bash ón
Estructuras ✓ sintaxis ✓ sintaxi ✓
de diferente s
control: for difere
bucles, if d nte
eclaraciones
, etc.
dirs incorp ✓ ✓ ✓
orado
echo incorp ✓ ✓ ✓ ✓ ✓
orado
Escapa del ✓ ✓ ✓ ✓
alias con \
Escapa ✓ ✓ ✓ ✓ ✓
con \
exec incorp ✓ ✓ ✓ ✓ ✓
orado
Código de ✓ $status ✓ ✓ ✓
salida
con $?
export inco ✓ set - ✓ seten ✓
rporado x name v name
value value
Funciones ✓c sintaxis ✓ ✓
diferente
HISTCONTR Consulta las
OL variable variables
cuyos
función gui
pescado ksh tcsh zsh
bash ón
nombres
empiezan
por HIST_ e
n la página
de manual
HISTFILE v set fish ✓ set hi ✓
ariable _histor stfil
y path e = pa
th
HISTFILES set sa +GUARDIA
IZE variabl vehis
e t = va
lue
history inc ✓, pero histo ✓ ✓
orporado los ry es
comando un
s no alias
están de hi
numerad st -l
os
history - history Borra ✓ history -p
c clear ~/.sh
_histo
ry y
reinici
a ksh
Ampliación ✓ ✓
de la
historia
con ! y ^
Búsqueda Escribe ✓a d ✓e ✓f
función gui
pescado ksh tcsh zsh
bash ón
incremental el
en el principio
historial con del
Ctrl-R comando
, luego
pulsa la
flecha
arriba
para
buscar,
la flecha
derecha
para
seleccion
ar
history nu history histo ✓ history -
mber -number ry - number
N num
ber
Historia con ✓ ✓a ✓ ✓
las teclas de
flecha
Historia con ✓ ✓a ✓ ✓
teclas
Emacs
Historia con ✓ Ejecut ✓
teclas Vim a bind
con set -o key -
vi v
HISTSIZE v ✓ ✓
ariable
función gui
pescado ksh tcsh zsh
bash ón
Control del ✓ ✓ ✓ ✓g ✓
trabajo
con fg, bg,
Ctrl-Z, jobs
Concordanc ✓ ✓ ✓ ✓ ✓
ia de
patrones
con *, ?, []
Tuberías ✓ ✓ ✓ ✓ ✓
popd incorp ✓ ✓ ✓
orado
Procesar la ✓ ✓
sustitución
con <()
PS1 variabl ✓ set ✓ set ✓
e PS1 val promp
ue t
= valu
e
pushd incor ✓ ✓, ✓
porado pero
sin
argum
entos
negati
vos
Citas, doble ✓ ✓ ✓ ✓ ✓
Citas, ✓ ✓ ✓ ✓ ✓
función gui
pescado ksh tcsh zsh
bash ón
soltero
Redirección ✓ ✓ ✓ ✓
de stderr
(2>)
Redirección ✓ ✓ ✓ ✓ ✓
de stdin (<),
stdout
(>, >>)
Redirección Aña ✓ Añade >& ✓
de stdout + de 2 2>&1h
stderr (&>) >&1
h
Busca un sólo ✓ ✓i sourc ✓i
archivo pun e sólo
con source toi
o . (punto)
Subconcha ✓ ✓ ✓ ✓
con ()
Completar ✓ ✓a ✓ ✓
los nombres
de archivo
con el
tabulador
type incorp ✓ ✓ type No, ✓
orado es un pero w
alias hich e
de wh stá
ence incorp
-v orado
función gui
pescado ksh tcsh zsh
bash ón
unalias inc ✓ functio ✓ ✓ ✓
orporado ns --
erase
Definición ✓ set nam ✓ set na ✓
de variables e value me = v
con name=v alue
alue
Evaluación ✓ ✓ ✓ ✓ ✓
de variables
con $name
a Esta función está desactivada por defecto. Ejecuta set -o
emacs para activarla. Las versiones anteriores de ksh pueden
comportarse de forma diferente.
b La finalización personalizada de comandos, mediante el
comando complete o similar, difiere significativamente de un
intérprete de comandos a otro; lee la página de manual del intérprete
de comandos.
c Funciones: Este intérprete de comandos no admite definiciones de
estilo más reciente que empiecen por la palabra clave function.
d La búsqueda incremental del historial funciona de forma diferente
en ksh. Pulsa Ctrl-R, escribe una cadena y pulsa Intro para recuperar
el comando más reciente que contenga esa cadena. Pulsa Ctrl-R y
Enter de nuevo para buscar hacia atrás el siguiente comando que
coincida, y así sucesivamente. Pulsa Intro para ejecutar.
e Para activar la búsqueda incremental en el historial con Ctrl-R
en tcsh, ejecuta el comando bindkey ^R i-search-back (y añádelo
a un archivo de configuración del shell). El comportamiento de la
búsqueda es un poco diferente del de bash; consulta man tcsh.
f En el modo vi, escribe / seguido de la cadena de búsqueda y pulsa
Intro. Pulsa n para saltar al siguiente resultado de la búsqueda.
g Control de trabajos: tcsh no rastrea el número de trabajo por
defecto de forma tan inteligente como otros shells, por lo que puede
que tengas que proporcionar el número de trabajo, como %1, como
argumento a fg y bg más a menudo.
función gui
pescado ksh tcsh zsh
bash ón
h Redirección de stdout y stderr: La sintaxis en este intérprete de
comandos es command >file 2>&1. El término final 2>&1 significa
"redirigir stderr, que es el descriptor de archivo 2, a stdout, que es el
descriptor de archivo 1".
i La búsqueda de fuentes en este intérprete de comandos requiere una
ruta explícita al archivo de origen, como ./myfile para un archivo en
el directorio actual, o el intérprete de comandos no localizará el
archivo. Alternativamente, coloca el archivo en un directorio de la
ruta de búsqueda del intérprete de coman
Tabla B-1. bash funciones soportadas por otros shells, en orden alfabético
Sobre el autor
Daniel J. Barrett lleva más de 30 años enseñando y escribiendo sobre
Linux y tecnologías afines. Es autor de numerosos libros de O'Reilly,
como Linux Pocket Guide, Linux Security Cookbook, SSH, The Secure
Shell: The Definitive Guide, Macintosh Terminal Pocket
Guide y MediaWiki. Dan también ha sido ingeniero de software, cantante
de heavy metal, administrador de sistemas, profesor universitario,
diseñador web y humorista. Trabaja en Google.
Visita [Link] saber más.
Colofón
El animal de la portada de Linux Eficaz en la Línea de Comandos es un
halcón sacre(Falco cherrug).
Rápidas, poderosas y agresivas, estas grandes rapaces han sido apreciadas
por los cetreros durante miles de años. Hoy son el ave nacional de varios
países, como Hungría, Mongolia y los Emiratos Árabes Unidos.
Los sacres adultos suelen alcanzar un tamaño de 45-57 cm, con alas
anchas que abarcan 97-126 cm. Las hembras de la especie son
significativamente más grandes que los machos, con un peso de 970-1.300
g (34-46 oz) frente a los 730-990 g (26-35 oz) de estos últimos. El plumaje de
ambos sexos es muy variable, desde el marrón oscuro hasta el bronceado
pálido, o incluso el blanco, con vetas o barras marrones.
En estado salvaje, los halcones cazan principalmente aves y roedores,
alcanzando velocidades de vuelo de hasta 120-150 km/h antes de
abalanzarse sobre su presa. Los hábitats típicos incluyen praderas,
acantilados y bosques de galería, donde los halcones ocupan nidos
abandonados por otras aves. Con algunas excepciones en las zonas más
meridionales de su área de distribución, los halcones sacres son aves
migratorias que viajan anualmente desde Europa oriental y Asia central
hasta las zonas septentrionales de África y Asia meridional para pasar el
invierno.
Aparte de los humanos, los sacres no tienen depredadores conocidos en
estado salvaje. Sin embargo, debido al rápido declive de su población, el
halcón sacre está ahora clasificado en peligro de extinción, al igual que
muchos de los animales de las portadas de O'Reilly. Todos ellos son
importantes para el mundo.
La ilustración de la portada es obra de Karen Montgomery, basada en un
antiguo grabado lineal de La Real Historia Natural de Lydekker. Los tipos
de letra de la portada son Gilroy Semibold y Guardian Sans. La fuente del
texto es Adobe Minion Pro; la fuente del encabezamiento es Adobe Myriad
Condensed; y la fuente del código es Ubuntu Mono de Dalton Maag.