Ejecutar un comando externo (Windows / Unix) de forma interactiva desde IRIS
¡Hola! Me gustaría revisitar hoy con vosotros un artículo muy útil de Amir Samary donde se detalla cómo ejecutar un comando del sistema operativo desde IRIS / Caché / Ensemble de forma interactiva.
Con frecuencia es necesario ejecutar algún comando externo, como por ejemplo un programa Python o un script de bash desde Caché/Ensemble. Existen dos formas principales:
- $ZF(-100) - Ejecuta un comando como un proceso hijo. La ejecución puede ser síncrona o asíncrona. Permite redireccionar la entrada y salida estándar a ficheros. Esta función reemplaza a las obsoletas
$ZF(-1), $ZF(-2). - CPIPE - Ejecuta el comando y abre un dispositivo para leer la salida o incluso escribir de forma interactiva la entrada.
$ZF(-100) es una opción interesante para ejecutar el comando y recuperar el resultado, sin embargo, para capturarla información de salida que se produce, es necesario redirigirla a un fichero temporal. Además, no permite ejecutar un programa de forma interactiva.
La ejecución utilizando CPIPE resulta conveniente cuando no queremos tener que procesar ficheros temporales o cuando nos interesa ejecutar el programa de forma interactiva.
La mejor forma de verlo es comprobar rápidamente su funcionamiento.
Este ejemplo funcionará en la mayoría de sistemas Unix / Linux / Mac. Crea un fichero de texto con contenido "Hellow World!" utilizando el editor de texto vi de forma interactiva desde IRIS (fijaos cómo pasa las instrucciones para el editor vi incluido el Insert y el Write and Quit).
USER>set tCurrentDevice=$IO USER>set tDevice="|CPIPE|" USER>set tCmd="bash" USER>set tInstructions="vi /Users/afuentes/test.txt"_$c(13,10)_"i"_"Hellow World!"_$c(27)_":wq"_$c(13,10)_"exit"_$c(13,10) USER>open tDevice:(tCmd:"W") USER>use tDevice Write tInstructions Use tCurrentDevice USER>close tDevice
Si lo único que precisamos es ejecutar un comando y obtener la salida, podemos hacer como en el siguiente ejemplo en el que se muestra la salida de un listado de directorios.
USER>set tDevice="|CPIPE|" USER>set tCmd="ls -l" USER>open tDevice:(tCmd:"R") USER>use tDevice Read line Use 0 Write line total 1028 USER>use tDevice Read line Use 0 Write line -rw-rw---- 1 root irisusr 1048576 Jul 1 14:58 IRIS.DAT USER>use tDevice Read line Use 0 Write line -rw-rw---- 1 root irisusr 40 Jul 1 14:07 iris.lck USER>use tDevice Read line Use 0 Write line drwxrwxr-x 2 root irisusr 64 Apr 30 19:12 stream USER>use tDevice Read line Use 0 Write line USE tDevice READ line USE 0 WRITE line ^ <ENDOFFILE> USER>
Como veis, podemos continuar leyendo del dispositivo CPIPE hasta alcanzar el <ENDOFFILE>. Así que probablemente en el código habría que añadir un bloque try-catch. Fijaos como además se utiliza directamente Use 0 porque estamos ejecutándolo desde un Terminal, pero en general, al igual que en el ejemplo anterior, podemos hacer uso de $IO para obtener el dispositivo abierto actual.
Elegiremos el método de ejecución que más nos convenga según las necesidades. En cualquier caso, merece la pena echarle un vistazo a la clase %Net.Remote.Utility y los métodos RunCommandViaZF y RunCommandViaCPIPE que encapsulan el funcionamiento de estas funciones de ejecución de comandos externos.
USER>set cmd="/bin/bash" USER>set arg=2 USER>set arg(1)="-c" USER>set arg(2)="ps -fea | grep iris" USER>set ret = ##class(%Net.Remote.Utility).RunCommandViaCPIPE(cmd, .device, .out,,,.arg) USER>close device USER>set outList = $listfromstring(out, $c(13,10)) USER>write $listget(outList, 1) root 1 0 0 04:25 ? 00:00:00 /dev/init -- /iris-main --key /external/iris.key USER>write $listget(outList, 2) root 7 1 0 04:25 ? 00:00:01 /iris-main --key /external/iris.key USER>write $listget(outList, 3) root 772 1 0 04:25 ? 00:00:01 /usr/irissys/bin/irisdb -s/external/durable/mgr/ -w/external/durable/mgr/ -cc -B -C/external/durable/iris.cpf*IRIS USER>write $listget(outList, 4) root 773 772 0 04:25 ? 00:00:00 /usr/irissys/bin/irisdb WD
Tened en cuenta que si la salida de nuestro comando es mayor que el tamaño máximo de un string (3 641 144 caracteres), podemos redirigir la salida a un fichero temporal.
USER>set cmd="/bin/bash" USER>set arg=2 USER>set arg(1)="-c" USER>set arg(2)="ls -l > /tmp/ls.txt" USER>set ret=##class(%Net.Remote.Utility).RunCommandViaCPIPE(cmd, .device, .out,,,.arg) USER>write ret 1 USER>close device USER>set file=##class(%Stream.FileCharacter).%New() USER>set file.Filename="/tmp/ls.txt" USER>write file.ReadLine() total 1028 USER>write file.ReadLine() -rw-rw---- 1 root irisusr 1048576 Jul 1 14:58 IRIS.DAT USER>write file.ReadLine() -rw-rw---- 1 root irisusr 40 Jul 1 14:07 iris.lck