¡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