¿Es posible leer la entrada del usuario desde STDIN mientras se enreda un bloque fuente?

10

¿Es posible leer la entrada del usuario desde STDIN mientras enreda un bloque fuente con org-babel-tangle?

Soy consciente de esto: Org Mode Babel - Evaluación de bloque de código interactivo .

Eso no ayuda a resolver este caso de uso en particular, ya que todavía no permite la entrada STDIN adecuada desde el shell, sino que solo simula una entrada limitada internamente a Emacs.

Antecedentes

Me gustaría usar Org's Babel para aprender nuevos lenguajes de programación (Perl y Bash) ejecutando algunos tutoriales desde un archivo de organización.

El problema es que muchos tutoriales dependen de STDIN. Por ejemplo, si uno ejecuta el siguiente tidbit de perl:

#+BEGIN_SRC perl :tangle hello-name.pl  :results output :export code
use 5.010;
use strict;
use warnings;

say "What is your name?";
my $name=<STDIN>;
say "Hello $name, how are you?";

#+END_SRC

Emacs no esperará a que la interacción del usuario escriba correctamente un nombre en STDIN, e inmediatamente mostrará:

#+RESULTS:
: What is your name?
: Hello , how are you?

Lo mismo usando un ejemplo bash. Esta:

#+BEGIN_SRC sh  :results output :export code :tangle dir-input.sh
#!/bin/bash

if [ -z "$TEST_DIR" ]
then
    echo "TEST_DIR was not set, please enter the path: "
    read input_variable
    export TEST_DIR=$input_variable
fi
#+END_SRC

No esperará la entrada del usuario, y Emacs inmediatamente devolverá esto:

#+RESULTS:
: TEST_DIR was not set, please enter the path: 

¿Hay una forma nativa para que Emacs espere la entrada en un bloque enredado en ejecución?

Si no es así, ¿podría dar algunos consejos sobre cómo escribir algo como una tangle-and-run-via-shell-bufferfunción que:

  • Enrede el bloque de código en el punto, guardando con el nombre de archivo dado,
  • ejecutar el archivo correspondiente en un shellbúfer visible ,
  • posiblemente aceptando aportes del usuario,
  • y finalmente informar STDOUT, si hay alguno, a #+RESULTS:?

Si dicha característica no está implementada (todavía) en Org, ¿cómo podría implementarse con elisp?


Actualización: después de buscar y estudiar más los manuales de Emacs y elisp, parece que la forma de hacerlo sería aprovechar Comint , como tal vez make-comint-in-buffer.

(make-comint-in-buffer "*cmd-buffer*" nil "perl" nil "hello-name.pl")

Desafortunadamente, eso está sobre mi cabeza en este momento 😣

gsl
fuente

Respuestas:

4

Prueba esto

Nota : Se realizaron los siguientes cambios menores en su bloque de código:

  • Se movió #!/bin/bashal encabezado del bloque de código :shebang #!/bin/bashpara establecer automáticamente los permisos de los archivos ejecutables cuando el bloque está enredado dir-input.sh.

  • El código de depuración agregado para mostrar $TEST_DIRse asignó correctamente desde read input_variable.

#+BEGIN_SRC sh  :results output :export code :tangle dir-input.sh :shebang #!/bin/bash

if [ -z "$TEST_DIR" ]
then
    echo "TEST_DIR was not set, please enter the path: "
    read input_variable
    export TEST_DIR=$input_variable
    echo "# export TEST_DIR=$TEST_DIR"
fi
#+END_SRC   

Luego creó un nuevo bloque de código para llamar al archivo enredado ./dir-input.sh.

#+BEGIN_SRC sh :results output :shebang #!/bin/bash  :var USER_INPUT=(read-string "Test Dir: ")
  echo $USER_INPUT | ./dir-input.sh 
#+END_SRC

Encabezado de aviso :var USER_INPUT=(read-string "Test Dir: ")

Este encabezado mostrará un Test Dir:mensaje en la minibufferventana cuando el bloque de código se ejecute usando a C-c C-c.

Ingrese la ruta, por ejemplo / ruta / a / prueba / dir enter

y el bloque pasará la entrada a la ./dir-input.shvía STDIN. Deberías ver lo siguiente#+RESULTS:

#+RESULTS:
: TEST_DIR was not set, please enter the path: 
: # export TEST_DIR=/path/to/test/dir

Espero que haya ayudado!


Código probado con:
GNU Emacs 24.4.1 (x86_64-apple-darwin14.0.0, NS apple-appkit-1343.14) de la
versión 2014-12-25 org-mode: 8.3.2

Melioratus
fuente
Eso ayuda bastante, gracias. Una forma creativa de usar vars, muy instructiva. Me pregunto cómo iría a capturar STDIN "totalmente", es decir, como se podría hacer con una cáscara nativa. Por ejemplo, para poder leer nuevas líneas y caracteres de control (además de CTRL-D)?
gsl
1
@gsl - Por cierto - Todavía estoy trabajando en una nueva respuesta a la pregunta sobre múltiples líneas y caracteres de control. Si lo averiguas antes que yo, publica tu respuesta.
Melioratus
Muchas gracias por preparar una nueva respuesta. Todavía estoy buscando una solución que funcione. No puedo resolverlo por mí mismo, todavía sobre mi cabeza en este momento. Estoy eligiendo su respuesta, y cuando aparezca la nueva, selecciónela más tarde.
gsl
Hola @melioratus: ¿pudiste encontrar una manera de lidiar con múltiples líneas y caracteres de control? Eso sería bastante útil en muchos casos.
GSL
1
@gsl - ¡Gracias por seguir! ¡Estás leyendo correctamente de stdin sería realmente útil y todavía estoy buscando! He progresado leyendo stdin en el búfer con nombre desde la línea de comandos y luego llamando a elisp para leer el búfer multilínea en variable. Esto funciona cuando se lee desde una tubería, pero desafortunadamente aún no para la transmisión estándar, es decir, tail file |funciona pero no tail -f file |. Revisaré mis notas y agregaré mi ejemplo multilínea que funciona parcialmente como nueva respuesta. ¡Gracias por el recordatorio!
Melioratus