Gracias a una Raspberry Pi por medio del procesamiento de imágenes en efecto podemos hacer más inteligente nuestro vehículo y añadir nuevas funcionalidades, pero para ello necesitaremos los siguientes componentes:
Raspberry Pi 3 Model B(unos 38€ en Amazon) o también Raspberry Pi Zero que cuesta algo mas barata(unos 25€ con caja en Amazon)
Raspberry Pi Camera Module (se puede compar por unos 15€ en Amazon)
Flex Cable for Raspberry Pi Camera or Display – 2 meters (puede comprrlo aqui en Amazon por unos 7,29€ )
Adafruit CSI or DSI Cable Extender Thingy for Raspberry Pi (opcionalmente)
Conexión del módulo de cámara
El modulo de cámara de Pi tiene un mayor rendimiento que una cámara USB por lo que lo ideal es usar una cámara del tipo compatibles con Raspberry Pi (se puede comprar por unos 15€ en Amazon)
No es problema la distancia pues con un cable plano de 200 cm suele ser suficiente para llevar la cámara hasta la posición de conducción (puede comprarlo aqui en Amazon por unos 7,29€ )
Se puede pues llevar el cable plano al l frente del coche y luego conectado a una pantalla de táctil de 7″ de modo que la Pi y la pantalla táctil pueden ser alimentados por el adaptador USB en el coche.
Estos son los pasos para instalar la cámara especifica para su uso , con la Raspberry Pi
Localice el puerto de la cámara y conecte la cámara:
Poner en marcha la Raspberry Pi
Abra la Herramienta de configuración de frambuesa Pi desde el menú principal:
Asegúrese de que está activado el software de la cámara:
Si no está activado, habilítelo y reinicie su Pi para comenzar. Asimismo si va utilizar una pantalla táctil también necesitara activar I2C y SPI
Es decir resumidamente; con la Raspberry Pi apagada, debe conectar el módulo de la cámara al puerto de la cámara de la Raspberry Pi,ahora encienda el Pi y asegúrese de que se activa el software.
Añadiendo reconocimiento de imágenes
En un post anterior vimos como podemos superponer un imagen escalada a nuestro hw que nos determinara con bastante aproximación si vamos a encontrar obstáculos a la hora de introducir la marcha atrás ..¿pero se puede mejorar ?
Pues en efecto se puede ,pues podemos usar OpenCV con python lo cual permitirá analizar lo que está en la imagen y definir los parámetros para lo que se encuentre.
La idea sería tomar las imágenes y establecer un límite en la parte inferior (cerca del parachoques) para la «zona de alarma»: justo entonces al detectar cualquier objeto grande en la iamgen capturada , si la mayor parte del área de los objetos de la parte inferior entra en la «zona de alarma», entonces debería enviar un mensaje de alerta.
Para generar la alarma se puede usar un simple zumbador piezoeléctrico conectando la pata positiva al Pin 22 y la pata negativa con un conector de tierra, pero desde luego se puede ser mas sofisticado .
En los tutoriales de instalación de OpenCV se recomienda compilar desde la fuente; sin embargo, en el último año ha sido posible instalar OpenCV a través de pip, el propio administrador de paquetes de Python.
Si bien la instalación desde la fuente le dará el mayor control sobre su configuración de OpenCV, también es la más difícil y la que más tiempo consume asi que si está buscando la manera más rápida posible de instalar OpenCV en su sistema, pues lo aconsejabñe es usar pip para instalar OpenCV
Una de las desventajas de la instalación de OpenCV es que usted no tiene ningún control sobre la compilación en sí, ya que los archivos binarios están creados previamente para usted, lo que, aunque es bueno, también significa que no puede incluir optimizaciones adicionales.
Para la Raspberry Pi, estamos de suerte pues Dave Jones (creador del módulo Python picamera ) y Ben Nuttall de la piwheels.org , administran para la comunidad Raspberry Pi, un repositorio de paquetes Python que proporciona wheels ARM (es decir, paquetes de binarios precompilados) para la Raspberry Pi.
Al usar PiWheels, podemoss instalar OpenCV en cuestión de segundos, y(lo mismo se aplica a otras bibliotecas de Python que pueden tardar mucho tiempo en compilarse, incluidos NumPy, SciPy, scikit-learn, etc).
Si está utilizando Raspbian Stretch, le complacerá saber que el comando pip comprobará PiWheels en busca de un binario precompilado antes de que compruebe PyPI, permitiendo que su Pi guarde un montón de ciclos de CPU (y usted un montón de tiempor)! Si termina usando pip para instalar OpenCV en su Raspberry Pi, puedes estar seguro de que estás usando la versión optimizada.
Comencemos aprendiendo cómo instalar OpenCV en nuestra Raspberry Pi.
Instale los requisitos previos en su Raspberry Pi
La Raspberry Pi requiere que instale algunos paquetes de sistema antes de comenzar:
El administrador de paquetes de Python, «pip», se puede obtener a través de wget:
$wget https://bootstrap.pypa.io/get–pip.py
$sudo python3 get–pip.py
Instalar OpenCv
No se recomendaría esta opción si desea poder utilizar diferentes versiones de OpenCV en entornos aislados.. Pero muchas personas implementan su Raspberry Pis con un solo propósito / proyecto y no necesitan entornos virtuales. Dicho esto, es bastante complicado limpiarlo si cambia de opinión más tarde y quiere usar entornos virtuales, por lo que se recomienda que omita esta opción y sigas la Opción B.
Para instalar OpenCV en su sistema Raspberry Pi, asegúrese de usar sudo como este:
$sudo pip install opencv–contrib–python
pip install opencv
En cuestión de segundos, OpenCV está listo para entrar en los paquetes de sitio de Raspberry Pi junto con cualquier otro paquete que tenga instalado.
Antes de comenzar con el código, tenemos que instalar OpenCV en el Pi primero. Por suerte se puede hacer de forma muy sencilla . Para ello abrir la consola del sistema y ejecutar pip3 install opencv-python
Esto tardara unos minutos y la salida debería dar algo similar a esto:
En los tutoriales de instalación de OpenCV se recomienda compilar desde la fuente; sin embargo, en el último año ha sido posible instalar OpenCV a través de pip, el propio administrador de paquetes de Python. Si bien la instalación desde la fuente le dará el mayor control sobre su configuración de OpenCV, también es la más difícil y la que más tiempo consume. Si está buscando la manera más rápida posible de instalar OpenCV en su sistema, querrá usar pip para instalar OpenCV, pero hay algunas cosas que pueden hacer que se tropiece en el camino, así que asegúrese de leer el resto de este post.
Script en Python
Una vez OpenCV está instalado, podemos crear un nuevo archivo de Python y empezar en el código.
En las lineas siguientes podemos ver el código completamente documentado, que se puede visitar el repositorio de github de gigafide.
import time
import cv2
import numpy as np
from picamera.array import PiRGBArray
from picamera import PiCamera
import RPi.GPIO as GPIO
buzzer = 22
GPIO.setmode(GPIO.BCM)
GPIO.setup(buzzer, GPIO.OUT)
camera = PiCamera()
camera.resolution = (320, 240) #Una resolución más pequeña significa un procesamiento más rápido
camera.framerate = 24
rawCapture = PiRGBArray(camera, size=(320, 240))
kernel = np.ones((2,2),np.uint8)
time.sleep(0.1)
for still in camera.capture_continuous(rawCapture, format="bgr", use_video_port=True):
GPIO.output(buzzer, False)
image = still.array
#create a detection area
widthAlert = np.size(image, 1) #obtener el ancho de la imagen
heightAlert = np.size(image, 0) #obtener la altura de la imagen
yAlert = (heightAlert/2) + 100 #determina la coordenada y del area
cv2.line(image, (0,yAlert), (widthAlert,yAlert),(0,0,255),2)
#dibujar una linea para mostrar el area
lower = [1, 0, 20]
upper = [60, 40, 200]
lower = np.array(lower, dtype="uint8")
upper = np.array(upper, dtype="uint8")
#utilizar la gama de colores para crear una mascara para la imagen y aplicarla a la imagen
mask = cv2.inRange(image, lower, upper)
output = cv2.bitwise_and(image, image, mask=mask)
dilation = cv2.dilate(mask, kernel, iterations = 3)
closing = cv2.morphologyEx(dilation, cv2.MORPH_GRADIENT, kernel)
closing = cv2.morphologyEx(dilation, cv2.MORPH_CLOSE, kernel)
edge = cv2.Canny(closing, 175, 175)
contours, hierarchy = cv2.findContours(closing, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
threshold_area = 400
centres = []rasp
if len(contours) !=0:
for x in contours:
#encuentra el area de cada contorno area = cv2.contourArea(x)
#encuentra el centro de cada contorno
moments = cv2.moments(x)
#elimine los contornos que son menores que nuestro umbralif area > threshold_area:
(x,y,w,h) = cv2.boundingRect(x)
centerX = (x+x+w)/2
centerY = (y+y+h)/2
cv2.circle(image,(centerX, centerY), 7, (255, 255, 255), -1)
if ((y+h) > yAlert):
cv2.putText(image, "ALERTA!", (centerX -20, centerY -20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,255,255),2)
GPIO.output(buzzer, True)
cv2.imshow("Display", image)
rawCapture.truncate(0)
key = cv2.waitKey(1) & 0xFF
if key == ord("q"):
GPIO.output(buzzer, False)
break
Ahora este script puede ser nombrado como car_detector.py y lanzarlo desde la consola .
Ojo no basta con lanzarlo F5 desde el propio Python pues dará probablemente error a l ahora de importar al libreria cv2, pero no se preocupe situarse en la ruta del scrript en prython y ejecutar
sudo python car_detector.py
A partir desde ahí debería funcionar la detección de imágenes con lo que capte la imagen de la Raspberry Pi
En el siguiente vídeo podemos ver el script en acción
Como vemos en elvideo en pequeña escala, lo hizo bastante bien detectando un montón de objetos innecesarios, ( aunque a veces detecta las sombras como objetos.)En un escenario del mundo real, los resultados fueron sorprendentemente precisos ,pero si es cerca de las condiciones perfectas, Tenga en cuenta que el código es básico actualmente y necesitaria muchas más pruebas y depuración)
De los dos métodos,este método , pero el primer método es más confiable en múltiples situaciones. Así que si usted fuera a hacer esto para su coche, iría con el método inicial ( el que usa overlay).
Conexión hacia la Raspberry Pi via vnc
Ya que se ha conectado exitósamente a la consola de comandos por ssh , seguro que le interesa también poderse conectar tambien al interfaz gráfico por lo que ahora nos toca instalar y configurar el servidor de VNC sobre Raspberry Pi en su tablet/smartphone.
Para instalar y configurar el VNC viewer en su tablet/smartphon en android siga los siguientes pasos:
Al ejecutar la app por primera vez le pedira una cuenta de vnc, de modo qeu si no la tiene tendra que creala desde la propia aplicacion introduciendo una cuenta de correo, un nombre , apellido,pais y un catcha,
En nuestro correo electrónico recibiremos un email que debemos validar para confirmar que cuenta nos pertenece
Una vez que haya confirmado los datos puede intentar volver a entrar en la app VNV Viewer ingresando las credenciales que introdujo
Ahora siga el procedimiento similar al del ssh
Haga clic en el el Boton del signo más en la parte inferior izquierda de la pantalla y seleccione la opción Nuevo Host.
Entrar los siguientes datos:
Alias: cualquier nombre es aceptable como por ejemplo Raspberry Pi
Username:pi
Nombre de host: la dirección Ip obatnida con el comando ifconfig
Contraseña: la pwd que haya puesto
Los demás campos se pueden dejar espacios en blanco, luego toque la marca de verificación en la derecha superior. Después de eso, haga clic en una Conectar cuando se le preguntó » Si desea conectar el dispositivo
Ahora valide todos los datos y quedara almacenada en el Address Book estos datos de conexión para cuando los necesite
Para conectarse a su Raspberry Pi , dado que ya ha creado la conexión, pulse sobre el icono nuevo que aparece en la pantalla de inicio
Enseguida deberían aparecer las credenciales de acceso y probablemente el pwd en blanco que deberemos completar .
Finalmente progresar la conexión pulsando en CONTINUE
Una vez dado el botón de conectar debería aparecer la pantalla principal de Raspbian que ahora podremos controlar desde nuestro smartphone
Podemos ver la imagen de la cámara ahora desde el propio teléfono
Bash , c-shell o simplemente shell scripting es un lenguaje de script creado a fines de la década de 1980 por un programador llamado Brian Fox, que trabajaba para la Free Software Foundation .Fue pensado como una alternativa de software libre para el shell Bourne (de hecho, su nombre es un acrónimo de Bourne Again SHell ), e incorpora todas las características de ese shell, así como nuevas características como la aritmética de enteros y el control de trabajo
Bash es un «shell de Unix», es decir una interfaz de línea de comandos para interactuar con el sistema operativo por lo que está ampliamente disponible, siendo el shell predeterminado en muchas distribuciones de GNU / Linux y en Mac OSX, con puertos existentes para muchos otros sistemas.
Además del modo interactivo, donde el usuario escribe un comando a la vez, con ejecución y respuesta inmediatas, Bash (como muchos otros shells) también tiene la capacidad de ejecutar un script completo de comandos, conocido como «Bash shell script» (o «Bash script» o «shell script» o simplemente «script») que es justo lo que vamos a tratar en este post
Un scriptl cinstituye un fichero normalmente con extensión .sh que puede contener solo una lista muy simple de comandos, o incluso un solo comando, aunque los normal es que contenga funciones, bucles, construcciones condicionales y todas las demás características de la programación imperativa.
Por otra parte los scripts de shell se pueden llamar desde línea de comandos interactiva descrita anteriormente o bien, se pueden llamar desde otras partes del sistema. Por ejemplo se puede configurar un script para que se ejecute cuando se inicie el sistema;otro podría configurarse para funcionar todos los días de la semana a las 2:30 AM;otro podría ejecutarse cada vez que un usuario inicia sesión en el sistema.
Los scripts de shell se usan comúnmente para muchas tareas de administración del sistema, como realizar copias de seguridad en disco, evaluar registros del sistema, etc.También se utilizan comúnmente como scripts de instalación para programas complejos.Son especialmente adecuados para todo esto porque alejan la complejidad sin necesidad de casi hacer nada si un script solo necesita ejecutar dos programas externos, puede ser un script de dos líneas, y si necesita toda la capacidad de toma de decisiones y poder de un lenguaje de programación imperativo Turing-completo, entonces puede tener eso también.
Cuando esté experimentando, es probable que le resulte útil consultar la documentación de varios comandos.Para los comandos que están integrados en Bash, puede usar el comando de ayuda incorporado(comando help ) ;por ejemplo, helpecho «imprimirá» (es decir, mostrará) información sobre el comando echo incorporado.Para programas externos, es probable que sus páginas de manual estén instaladas en su sistema, en cuyo caso puede verlas a través del comando man («manual»);por ejemplo, para información sobre el comando cp(«copiar»), puede escribir man cp .Además, la mayoría de los programas, cuando se ejecutan con el argumento --help , imprimirán información de ayuda;por ejemplo, cp --help proporciona casi tanta información como man cp .(Sin embargo, con muchos programas, el enfoque de ayuda no brinda tanta información como el enfoque del comando man ).
Photo by Lorenzo Cafaro on Pexels.com
Hello world en bash script
Empecemos con un programa simple «hola mundo»:
echo'Hola mundo!'
Podemos escribir esto directamente en el indicador de Bash, o bien guardar esto como un archivo (por ejemplo, hello_world.sh ) y ejecutarlo escribiendo bash hello_world.sh en el indicador de Bash o también mendiante el comando Kash (kash hello_world.sh ).
En cualquier caso, se imprimirá Hello, world!:
$ echo'Hello, world!' ¡Hola Mundo!
Aquí hemos utilizado el símbolo $ para indicar el indicador de Bash: después de $ , el resto de la línea muestra el comando que escribimos, y la siguiente línea muestra la salida del comando.
Aquí hay un script un poco más complejo:
if[[ -e readme.txt ]]; then echo'El archivo "readme.txt" existe.' elseecho'El archivo "readme.txt" no existe.'fi
Esta secuencia de comandos comprueba si existe un archivo llamado readme.txt en el directorio actual y utiliza una instrucción if para controlar, en función de esa prueba, qué comandos se ejecutan.También se puede escribir directamente en el indicador de comandos (cualquier script), pero en este caso no es probable que sea útil.
Es decir todo lo anterior es completamente «dentro de Bash», ya que no requiern que Bash ejecute ningún programa externo.(Los comandos echo , if ... then ... else ... fi , y [[-e ...]] son comandos incorporados, implementados por Bash.) Pero, al ser un lenguaje de shell-scripting, una gran parte del propósito de Bash es para ejecutar programas externos.
La siguiente secuencia de comandos demuestra esta capacidad:
if [[ -e config.txt ]]; thenecho'El archivo "config.txt" ya existe.Comparando con el predeterminado...' diff -u config-default.txt config.txt> config-diff.txtecho'A diff se ha escrito en "config-diff.txt".' elseecho'El archivo "config.txt" no existe.Copia por defecto...' cp config-default.txt config.txtecho'...hecho.'fi
Aquí diff y cp son dos programas de utilidad comunes que, aunque no forman parte de Bash, se encuentran en la mayoría de los sistemas que tienen Bash.
La secuencia de comandos anterior asume la presencia de un archivo de configuración predeterminado llamado config-default.txt , y verifica la presencia de un archivo de configuración llamado config.txt .Si existe config.txt , entonces el script usa el programa externo diff para producir un «dif» (un informe de las diferencias entre, en este caso, dos archivos), para que el usuario pueda ver qué configuraciones no predeterminadas están en su lugar. .Si config.txt no existe, entonces el script usa el programa externo cp («copiar») para copiar el archivo de configuración predeterminado en config.txt .
Como puede ver, los programas externos se ejecutan utilizando el mismo tipo de sintaxis que los comandos incorporados (ambos son solo «comandos»).
La versión anterior de este script es muy «detallada», ya que genera una gran cantidad de resultados.Es probable que una secuencia de comandos más típica no incluya los comandos de eco , ya que es poco probable que los usuarios necesiten este nivel de información.En ese caso, podríamos usar la notación # para incluir comentarios que Bash ignore por completo y que no aparezcan al usuario.Tales comentarios son simplemente notas informativas para alguien que lee el script en sí:
if [[ -e config.txt ]]; then# si config.txt existe: diff -u config-default.txt config.txt> config-diff.txt # ver qué ha cambiado else# si config.txt no existe: cp config-default.txt config.txt # toma el valor predeterminadofi
Pero lo anterior es simplemente por el bien de la demostración.En realidad, un script tan simple no requiere ningún comentario.
Comandos simples
Un comando simple consiste en una secuencia de palabras separadas por espacios o tabulaciones.La primera palabra se toma como el nombre de un comando, y las palabras restantes se pasan como argumentos al comando.Ya hemos visto una serie de ejemplos de comandos simples
Aquí algunos comandos más usuales:
cd ..
Este comando usa cd («cambiar directorio»; un comando incorporado para navegar por el sistema de archivos) para navegar «hacia arriba» en un directorio.
La notación .. significa «directorio padre».Por ejemplo, /foo/bar/../baz.txt es equivalente a /foo/baz.txt .
rm foo.txt bar.txt baz.txt
Suponiendo que el programa rm («remove») está instalado, este comando elimina los archivos foo.txt , bar.txt y baz.txt en el directorio actual.
Bash encuentra el programa rm buscando en una lista configurable de directorios un archivo llamado rm que sea ejecutable (según lo determinen sus permisos de archivos).
/foo/bar/baz bip.txt
Este comando ejecuta el programa ubicado en / foo / bar / baz , pasando bip.txt como único argumento.
/ foo / bar / baz debe ser ejecutable (según lo determinado por sus permisos de archivo). Por cierto ,asegúrese de que NO HAY ESPACIO entre la barra inclinada y los archivos que la siguen:por ejemplo, asumiendo que la carpeta «foo» existe en el directorio «raíz», luego ejecute el siguiente comando: «rm -r / foo» destruirá su computadora si se realiza con el acceso «sudo».Usted ha sido advertido.Si no entiende lo anterior, no se preocupe por el momento.
Si / foo / bar / baz es un archivo de texto en lugar de un programa binario, y su primera línea comienza con #!, luego, el resto de esa línea determina el intérprete a usar para ejecutar el archivo.Por ejemplo, si la primera línea de / foo / bar / baz es #! / Bin / bash , entonces el comando anterior es equivalente a / bin / bash / foo / bar / baz bip.txt .
Ese ejemplo con / foo / bar / baz tiene una nota especial, ya que ilustra cómo se puede crear un script Bash que se pueda ejecutar como un programa ordinario: simplemente incluya #! / Bin / bash como la primera línea del script (suponiendo ahí es donde se encuentra Bash en su sistema; de lo contrario, ajuste según sea necesario) y asegúrese de que el script tenga los permisos de archivo correctos para ser legible y ejecutable.Para el resto des ejemplos de scripts de shell completos comenzarán con la línea #! / Bin / bash .
El problema con los espacios
Vimos anteriormente que el comando rm foo.txt bar.txt baz.txt elimina tres archivos separados: foo.txt , bar.txt y baz.txt .Esto sucede porque Bash divide el comando en cuatro palabras separadas basadas en espacios en blanco, y tres de esas palabras se convierten en argumentos para el programa rm .Pero, ¿y si necesitamos eliminar un archivo cuyo nombre contiene un espacio?
Bash ofrece varios mecanismos de cotización que son útiles para este caso;las más utilizadas son comillas simples y comillas dobles.
Cualquiera de estos comandos eliminará un archivo llamado this file.txt :
rm 'este archivo.txt'
rm "este archivo.txt"
Dentro de las comillas, el carácter de espacio pierde su significado especial como separador de palabras.Normalmente envolvemos una palabra completa entre comillas, como se muestra arriba, pero de hecho, solo el espacio en sí necesita ser encerrado;este '' archivo.txt o este "" archivo.txt es equivalente a 'este archivo.txt' .
Otro mecanismo de citación comúnmente usado es la barra invertida \ , pero funciona de manera ligeramente diferente;Cita (o «escapa») un solo carácter.Este comando, por lo tanto, es equivalente al anterior:
rm este \ archivo.txt
En todos estos casos, los mismos caracteres de cita no se pasan al programa.(Esto se denomina eliminación de comillas ). Como resultado, rm no tiene forma de saber si se invocó, por ejemplo, como rm foo.txt o como rm 'foo.txt' .
Comodines de nombre de archivo y expansión de tilde
Bash admite una serie de notaciones especiales, conocidas como expansiones , para pasar tipos de argumentos de uso común a los programas.
Uno de ellos es la expansión del nombre de archivo , donde un patrón como * .txt se reemplaza con los nombres de todos los archivos que coinciden con ese patrón.Por ejemplo, si el directorio actual contiene los archivos foo.txt , bar.txt , este archivo.txt y algo.else , entonces este comando:
echo * .txt
es equivalente a este comando:
echo'bar.txt''foo.txt''este archivo.txt'
Aquí el asterisco significa «cero o más caracteres»;hay algunos otros caracteres de patrón especiales (como el signo de interrogación ?, que significa «exactamente un carácter»), y algunas otras reglas de coincidencia de patrón, pero este uso de es, con mucho, el uso más común de patrones.
La expansión del nombre de archivo no se limita necesariamente a los archivos en el directorio actual.Por ejemplo, si queremos listar todos los archivos que coincidan con t * .sh dentro del directorio / usr / bin , podemos escribir esto:
echo /usr/bin/t*.sh
que puede expandirse a algo como esto:
echo /usr/bin/test.sh /usr/bin/time.sh
Si no hay archivos que coincidan con un patrón específico, no se realizará ninguna sustitución;por ejemplo, este comando:
echo asfasefasef * avzxv
probablemente solo imprima asfasefasef * avzxv .
Si algún nombre de archivo comienza con un guión, entonces la expansión del nombre del archivo a veces puede tener consecuencias sorprendentes.Por ejemplo, si un directorio contiene dos archivos, llamados -n y tmp.txt , cat * se expande a cat -n tmp.txt , y cat interpretará -n como una opción en lugar de un nombre de archivo;en su lugar, es mejor escribir cat ./* o cat - * , que se expande a cat ./-n ./tmp.txt o cat - -n tmp.txt , eliminando este problema.
¿Qué sucede si tenemos un archivo real llamado * .txt al que queremos referirnos? (es decir si los nombres de archivo pueden contener asteriscos). Pues podemos usar cualquiera de los estilos de cita que vimos anteriormente.Cualquiera de estos:
cat '* .txt'
gato "* .txt"
imprimirá el archivo real * .txt , en lugar de imprimir todos los archivos cuyos nombres terminen en .txt .
Otra expansión similar es la expansión de tilde .La expansión de tilde tiene muchas características, pero la principal es esta: en una palabra que consiste únicamente en una tilde ~ , o en una palabra que comienza con ~ / (tilde-barra), la tilde se reemplaza con la ruta completa a directorio de inicio del usuario actual.Por ejemplo, este comando:
echo ~ / *. txt
imprimirá los nombres de todos los archivos nombrados * .txt en el directorio de inicio del usuario actual.
Uso de las llaves
Similar a la expansión de nombre de archivo es la expansión de refuerzo , que es una forma compacta de representar múltiples argumentos similares.Los siguientes cuatro comandos son equivalentes:
ls file1.txt file2.txt file3.txt file4.txt file5.txt
ls archivo {1 , 2,3,4,5 } .txt
ls archivo {1 ..5..1 } .txt
ls archivo {1 ..5 } .txt
El primer comando enumera cada argumento explícitamente.Los otros tres comandos utilizan la expansión de refuerzo para expresar los argumentos de forma más concisa: en el segundo comando, se dan todas las posibilidades 1 a 5 , separadas por comas;en el tercer comando, se da una secuencia numérica («de 1 a 5, incrementando en 1»);y el cuarto comando es el mismo que el tercero, pero deja el … 1 implícito.
También podemos listar los archivos en el orden opuesto:
ls file5.txt file4.txt file3.txt file2.txt file1.txt
ls archivo {5 , 4,3,2,1 } .txt
ls archivo {5 ..1 ..- 1 } .txt
ls archivo {5 ..1 } .txt
siendo el tamaño de incremento predeterminado -1, cuando el punto final de la secuencia es menor que el punto de inicio.
Como en Bash, la primera palabra de un comando es el programa que se ejecuta, también podríamos escribir el comando de esta manera:
{ ls, archivo {1 ..5 } .txt }
pero obviamente eso no es propicio para la legibilidad.(El mismo tipo de cosas, por cierto, se puede hacer con la expansión del nombre de archivo).
La expansión del refuerzo, como la expansión del nombre de archivo, se puede desactivar por cualquiera de los mecanismos de cotización;'{' , "{" , o \ { produce una llave literal real.
Redireccionando la salida
Bash permite que la salida estándar de un comando (descriptor de archivo 1) se envíe a un archivo, en lugar de a la consola.Por ejemplo, el programa de utilidad común cat escribe un archivo en una salida estándar;Si redirigimos su salida estándar a un archivo, tenemos el efecto de copiar el contenido de un archivo en otro archivo.
Si queremos sobrescribir el archivo de destino con la salida del comando, usamos esta notación:
cat input.txt> output.txt
Si queremos mantener los contenidos existentes del archivo de destino como están y simplemente agregar la salida del comando al final, usamos esta notación:
cat input.txt >> output.txt
Sin embargo, no todo lo que un programa escribe en la consola pasa por la salida estándar.Muchos programas utilizan el error estándar (descriptor de archivo 2) para los mensajes de error y algunos tipos de mensajes de «registro» o «canal lateral».Si deseamos que el error estándar se combine con la salida estándar, podemos usar cualquiera de estas notaciones:
cat input.txt y >> output.txt
cat input.txt >> output.txt 2 > &1
Si deseamos que el error estándar se adjunte a un archivo diferente de la salida estándar, usamos esta notación:
cat input.txt >> output.txt 2 >> error.txt
De hecho, podemos redirigir solo el error estándar, dejando solo la salida estándar:
cat input.txt 2 >> error.txt
En todos los ejemplos anteriores, podemos reemplazar >> con > si queremos sobrescribir el objetivo de redireccionamiento en lugar de agregarlo.
Más adelante veremos algunas cosas más avanzadas que podemos hacer con la redirección de salida.
Redireccionando la entrada
Así como Bash permite que la salida de un programa se envíe a un archivo, también permite que la entrada de un programa se tome de un archivo.Por ejemplo, la utilidad común de Unix cat copia su entrada a su salida, de manera que este comando:
cat <input.txt
Escribirá el contenido de input.txt en la consola.
Como ya hemos visto, este truco no es necesario en este caso, ya que a Cat se le puede pedir que copie un archivo específico a su salida;el comando anterior es simplemente equivalente a este:
cat input.txt
Esta es la regla en lugar de la excepción;Las utilidades más comunes de Unix que pueden tomar entrada desde la consola también tienen la funcionalidad incorporada para tomar su entrada de un archivo en su lugar.De hecho, muchos, incluido cat , pueden recibir información de varios archivos, lo que los hace aún más flexibles que los anteriores.El siguiente comando imprime input1.txt seguido de input2.txt :
cat input1.txt input2.txt
No obstante, la redirección de entrada tiene sus usos, algunos de los cuales veremos más adelante.
Tuberías (pipes)
Una canalización o Pipe es una serie de comandos separados por el carácter de canalización |.Cada comando se ejecuta al mismo tiempo, y la salida de cada comando se usa como entrada para el siguiente comando.
Por ejemplo, considere esta tubería:
cat input.txt | grep foo | grep -v bar
Ya hemos visto la utilidad de catcat input.txt simplemente escribe el archivo input.txt en su salida estándar.El programa grep es una utilidad común de Unix que filtra («greps», en el lenguaje de Unix) según un patrón;por ejemplo, el comando grep foo imprimirá en su salida estándar cualquier línea de entrada que contenga la cadena foo .El comando grep -v bar usa la opción -v para invertir el patrón;El comando imprime las líneas de entrada que no contienen la barra de cadena.Dado que la entrada de cada comando es la salida del comando anterior, el resultado neto es que la canalización imprime cualquier línea de input.txt que contenga foo y no contenga barra .
Variables
En un script de Bash, hay algunos tipos diferentes de parámetros que pueden contener valores .Un tipo importante de parámetro son las variables : parámetros nombrados.Si está familiarizado con casi cualquier otro lenguaje de programación imperativo (como C, BASIC, Fortran o Pascal), entonces ya está familiarizado con las variables.La siguiente secuencia de comandos simple utiliza la ubicación variable para mantener el mundo del valor, e imprime un «¡Hola, mundo!»mensaje:
location= world # store "world" en la variable "location"echo"Hola,$ {ubicación}!"# imprimir "¡Hola mundo!"
Como puede ver, la cadena $ {ubicación} en la segunda línea fue reemplazada por world antes de que se ejecutara ese comando.Esta sustitución se conoce como expansión variable , y es más flexible de lo que podría sospechar.Por ejemplo, incluso puede contener el nombre del comando para ejecutar:
cmd_to_run=echo# store "echo" en la variable "cmd_to_run""$ {cmd_to_run}"'¡Hola mundo!'# imprimir "¡Hola mundo!"
En los dos ejemplos anteriores, hemos usado la notación $ { variable_name } para realizar la expansión de la variable.La notación más breve $ nombre_variable , sin llaves, tendría el mismo efecto en estos casos.A veces, los corchetes son necesarios (por ejemplo, $ {foo} bar no se puede escribir como $ foobar , porque este último se interpretaría como $ {foobar} ), pero generalmente se pueden omitir, y los guiones del mundo real por lo general los omiten .
Por supuesto, no hace falta decir que los anteriores no son ejemplos muy realistas;solo demuestran cómo usar las variables, no por qué o cuándo usarlas.Si está familiarizado con otros lenguajes de programación imperativos, entonces probablemente ya sea obvio por qué y cuándo usaría las variables;si no, entonces esto debería quedar claro a medida que lee este libro y vea ejemplos que los usan de manera más realista.
Es posible que haya notado que hemos usado comillas dobles " , en lugar de comillas simples cadenas de caracteres que incluyen expansiones variables':en general, es una buena idea envolver las expansiones variables entre comillas dobles; por ejemplo, use "$ var" en lugar de $ var . . Esto se debe a que las comillas simples evitan la expansión de variables; un comando como echo' $ {location} ' imprimirá la cadena real $ {ubicación} , en lugar de imprimir el valor de una variable llamada ubicación .
En general, es una buena idea envolver las expansiones de las variables con comillas dobles, porque de lo contrario, los resultados de la expansión de las variables sufrirán la expansión del nombre de archivo, así como la división de palabras (donde se utiliza el espacio en blanco para separar las palabras que forman un comando).Por ejemplo, este script:
foo='ab *'# tienda "ab *" en la variable "foo"echo$ foo
es probable que imprima algo como ba.txt bd.sh , que probablemente no sea lo que queremos.Las secuencias de comandos del mundo real con frecuencia no incluyen comillas dobles, excepto cuando son claramente necesarias, pero esta práctica a veces conduce a errores confusos.
Una serie de variables tienen un significado especial.Por ejemplo, la variable PATH determina la lista de directorios en los que Bash debería buscar cuando intenta ejecutar un programa externo;si está configurado en / usr / bin: / bin , entonces el comando cp src.txt dst.txt buscará ejecutar / usr / bin / cp o / bin / cp .La variable HOME está preinicializada en el directorio inicial del usuario actual y determina el comportamiento de la expansión de tilde.Por ejemplo, si una secuencia de comandos establece HOME = / foo , echo ~/bar imprimirá / foo / bar .(Sin embargo, esto no cambiará el directorio de inicio del usuario).
Parámetros posicionales
En la mayoría de los comandos anteriores, tanto los que ejecutan un comando integrado como los que usan un programa externo, hemos suministrado uno o más argumentos , que indican en qué debe funcionar el comando.Por ejemplo, cuando invocamos la utilidad común de Unix mkdir («make directory») para crear un nuevo directorio, lo invocamos con un comando como este:
mkdir tmp
donde tmp es el nombre del nuevo directorio para crear.
Y como hemos visto, los scripts de Bash son programas que pueden ejecutarse.Así que no hace falta decir que ellos también pueden tomar argumentos.Estos argumentos están disponibles para el programa como sus parámetros posicionales .Anteriormente, vimos que las variables son un tipo de parámetro.Los parámetros posicionales son muy similares, pero se identifican por números en lugar de por nombres.Por ejemplo, $ 1 (o $ {1} ) se expande al primer argumento del script.Supongamos que queremos crear un script simple llamado mkfile.sh que tome dos argumentos, un nombre de archivo y una línea de texto, y cree el archivo especificado con el texto especificado.Podemos escribirlo de la siguiente manera:
#! / bin / bashecho"$ 2" > "$ 1"
(Observe la línea #!/bin/bash al principio del archivo; cubrimos esa línea en los comandos básicos . Cuando ejecute este código, esa línea garantizará que será interpretada por el shell Bash, incluso si está ejecutándose desde otro programa o su computadora tiene una configuración no estándar.)
y (después de hacerlo ejecutable ejecutando chmod + x mkfile.sh ) podemos ejecutarlo de la siguiente manera:
./mkfile.sh file-to-create.txt 'línea para poner en el archivo'
También podemos referirnos a todos los argumentos a la vez usando $ @ , que se expande a todos los parámetros posicionales, en orden.Cuando se envuelven en comillas dobles, como "$ @" , cada argumento se convierte en una palabra separada.(Nota: la alternativa $ * es quizás más común, pero "$ *" se convierte en una sola palabra, con espacios entre los parámetros originales. "$ @" Casi siempre es preferible a $ @ o $ * , lo que permite un argumento para dividirse en varias palabras si contiene espacios en blanco, y para "$ *" , que combina múltiples argumentos en una sola palabra.) Esto suele ser útil en concierto con el comando incorporado shift , que elimina el primer parámetro posicional, como que $ 2 se convierte en $ 1 , $ 3 se convierte en $ 2 , y así sucesivamente.Por ejemplo, si cambiamos mkfile.sh de la siguiente manera:
#! / bin / bashfile="$ 1"# guarda el primer argumento como "$ file"shift# suelta el primer argumento de "$ @"echo"$ @" > "$ file"# escribe los argumentos restantes en "$ file"
entonces podemos ejecutarlo de la siguiente manera:
./mkfile.sh file-to-create.txt línea para poner en el archivo
y todos los argumentos, excepto el nombre de archivo, se escribirán en el archivo.
El número de parámetros posicionales está disponible como $ # ;por ejemplo, si $ # es 3 , entonces los parámetros posicionales son $ 1 , $ 2 y $ 3 .
Tenga en cuenta que los parámetros posicionales más allá de $ 9 requieren las llaves;Si necesita referirse al décimo argumento, por ejemplo, debe escribir $ {10} en lugar de $ 10 .(Este último se interpretaría como $ {1} 0 ). Dicho esto, no suele ser una buena idea tener tantos argumentos con significados específicos, ya que es difícil para los usuarios realizar un seguimiento de ellos.Si se encuentra específicamente refiriéndose al décimo argumento de su guión, puede valer la pena volver a evaluar su enfoque.
Si tiene alguna experiencia con Bash, o con las utilidades de Unix, lo más probable es que haya notado que muchos comandos pueden tomar varias «opciones», indicadas con un guión principal, además de sus argumentos habituales.Por ejemplo, rm "$filename" elimina un archivo individual, mientras que rm -r "$dirname" elimina un directorio completo con todo su contenido.(La -r es la abreviatura de «recursivo»: el comando «recursivamente» elimina un árbol completo de directorios). Estas opciones son en realidad solo argumentos.En rm "$filename" , solo hay un argumento ( "$ filename" ), mientras que en rm -r "$dirname" hay dos ( -r y "$ dirname" ).En cierto sentido, no hay nada intrínsecamente especial en estos argumentos, pero esta notación para las opciones está tan extendida que se considera estándar;muchos o la mayoría de los comandos incorporados de Bash pueden aceptar varias opciones, y más adelante veremos varias técnicas para respaldar las opciones como argumentos para nuestros scripts de Bash.
Salida de un script
Cuando se completa un proceso, devuelve un pequeño valor entero no negativo, llamado su estado de salida o su estado de retorno , al sistema operativo.Por convención, devuelve cero(es decir un exit 0 ) si se completó con éxito, y un número positivo si falló con un error ( es decir un exit <>0) y de este modo se puede distinguir varios errores diferentes mediante el uso de diferentes números positivos
Un script Bash puede obedecer esta convención mediante el uso del comando integrado exit .El siguiente comando:
exit4
termina el script de shell, devolviendo un estado de salida de cuatro, indicando algún tipo de error.Cuando no se especifica ningún estado de salida (ya sea porque la salida se ejecuta sin argumentos, o porque la secuencia de comandos finaliza sin llamar a exit ), la secuencia de comandos devuelve el estado de salida del último comando que ejecutó.
Una forma en que se utilizan los estados de salida es con los operadores Bash && («y») y ||(«o»).Si dos comandos están separados por && , entonces el comando de la izquierda se ejecuta primero, y el comando de la derecha solo se ejecuta si el primer comando tiene éxito.Por el contrario, si están separados por ||, entonces el comando de la derecha solo se ejecuta si el comando de la izquierda falla.
Por ejemplo, supongamos que queremos eliminar el archivo file.txt y volver a crearlo como un archivo en blanco.Podemos eliminarlo utilizando la utilidad común de Unix rm («eliminar»), y volver a crearlo utilizando la utilidad común de Unix touch ;así, podríamos escribir esto:
rm file.txt touch file.txt
Pero realmente, si rm falla, no queremos ejecutar el comando touch : no queremos volver a crear el archivo si no pudimos eliminarlo para empezar.Entonces, podemos escribir esto en su lugar:
rm file.txt && touch file.txt
Esto es lo mismo que antes, excepto que no intentará ejecutar touch a menos que rm haya tenido éxito.
Un tercer operador booleano similar, ! («no»), invierte el estado de salida de un comando. Por ejemplo, este comando:
! rm file.txt
es equivalente a rm file.txt, excepto que indicará éxito si rm indica error, y viceversa. (Esto no suele ser útil cuando los estados de salida se usan como estados de salida reales, lo que indica éxito o fracaso, pero pronto veremos algunos usos extendidos de los estados de salida donde una operación «no» es más útil).
El estado de salida de un comando está (breve mente) disponible como $?.Esto puede ser útil cuando es necesario distinguir entre varios estados de falla diferentes; por ejemplo, el comando grep (que busca líneas en un archivo que coinciden con un patrón específico) devuelve 0 si encuentra una coincidencia, 1 si no encuentra coincidencias y 2 si se produce un error genuino.
¿A que no es tan difícil programar en c-shell?Le invitamos a que si tiene un terminal Unix o Linux ( por ejemplo una Raspberry Pi) lo intente!seguro que se le ocurren mil ideas interesantes que hacer !
Debe estar conectado para enviar un comentario.