Cuaderno de GNUtas

Revoltijo Elisp (VI): desmontar volúmenes y mandar archivos a un ordenador remoto

Omnia vincit Elisp, así que en esta breve GNUta daré cuenta de un par de apaños en nuestro lenguaje de programación favorito para sendos escenarios que me venían fastidiando un poco en mi trabajo y usos cotidianos. Bueno, tampoco es que el fastidio fuera tan insalvable, pero siempre hay una excusa para escribir unos cuantos paréntesis que nos relajen de la ajetreada vida.

Primer escenario: una función para desmontar volúmenes

O sea, aquellos volúmenes (pen drives, discos externos, etc.) que GNU/Linux monta automáticamente y de oficio en el directorio /run/media/. Desmontarlos es tan simple como tirar un comando en la terminal o abrir un gestor de archivos. Pero si estás en Emacs es como cuando, cómodos en el sofá, nos resistimos a levantarnos para cerrar la ventana. Emacs, ya ven, nos vuelve perezosos. Así que se me ocurrió escribir una función desmontadora de volúmenes montados que me mostrase éstos mediante una lista autocompletiva Ido. Y, escogido el candidato, se desmonta.

Primero definimos una función para generar la lista de volúmenes montados:

(defun crea-lista-run-media ()
    (mapcar 'identity (cl-remove-if (lambda (k)
                                      (string-match ".+/\\." k))
                                    (directory-files "/run/media/juanmanuel" t))))

Y, por último, nuestra función que lanza Ido y desmonta el volumen escogido:

(defun ido-desmontar-run-media (dir)
  (interactive  (list (ido-completing-read "Desmontar: " (crea-lista-run-media) nil t)))
  (let ((default-directory "/sudo::"))
    (shell-command (concat "sudo umount -R " dir))))

Segundo escenario: mandar un archivo desde Dired a un equipo remoto

Aquí el equipo remoto es mi raspberry. Los archivos los mandaremos de forma asíncrona mediante el comando rsync. Y, según sea el tipo de archivo o carpeta, hay cuatro opciones con cuatro destinos posibles en la raspberry: Películas, Música y Series.

Definimos primero la función que manda el archivo, que toma como argumento la ruta. El password de la raspberry lo añade con un process-send-string a la salida del comando rsync, que debe estar en el búfer temporal *rsync-raspi* tras un temporizador (ver línea 17). Y lo extrae de mi archivo authinfo (ver línea 8):

(defun archivo-a-raspi (ruta)
  (interactive)
  (let*
      ((archivo (dired-get-file-for-visit))
       (auth
        (nth 0
             (auth-source-search :host "192.168.1.36"
                                 :requires '(user secret))))
       (password (funcall (plist-get auth :secret))))
    (start-process-shell-command "rsync" "*rsync-raspi*" (concat
                                                          "rsync "
                                                          " -avz "
                                                          " -e ssh "
                                                          (shell-quote-argument archivo)
                                                          " [email protected]:/home/pi"
                                                          ruta))
    (run-with-timer 0.5 nil
                    (lambda ()
                      (process-send-string "*rsync-raspi*" password)
                      (process-send-string "*rsync-raspi*" "\r")))))

Aquí, la lista de opciones:

(setq lista-rutas-raspi '((?1 "Películas" (lambda () (archivo-a-raspi "/multimedia/Películas")))
                          (?2 "Música" (lambda () (archivo-a-raspi "/multimedia/Música")))
                          (?3 "Series" (lambda () (archivo-a-raspi "/multimedia/series")))))

Y la función que despliega las opciones y ejecuta el candidato escogido:

(defun dired-manda-archivo-a-raspi ()
  "Manda el archivo marcado en Dired a mi raspberry mediante
rsync. Hay varias opciones"
  (interactive)
  (let ((opcion-rsync (read-char-choice (concat "Enviar a la carpeta >>\n"
                                                (mapconcat (lambda (item)
                                                             (format "%c: %s" (car item) (nth 1 item)))
                                                           lista-rutas-raspi " -- "))
                                        (mapcar #'car lista-rutas-raspi))))
    (funcall (nth 2 (assoc opcion-rsync lista-rutas-raspi)))))

Luego, bastaría con añadir cualquier atajo a nuestro gusto al dired-mode-map.

Publicado: 15/08/21

Última actualización: 07/08/22


Índice general

Acerca de...

Esta obra está bajo una licencia de Creative Commons Reconocimiento-NoComercial 4.0 Internacional.

© Juan Manuel Macías
Creado con esmero en
GNU Emacs