Index: /tags/ipp-unions-20250528/Ohana/src/opihi/pantasks/controller_hoststats.c
===================================================================
--- /tags/ipp-unions-20250528/Ohana/src/opihi/pantasks/controller_hoststats.c	(revision 42899)
+++ /tags/ipp-unions-20250528/Ohana/src/opihi/pantasks/controller_hoststats.c	(revision 42900)
@@ -4,5 +4,4 @@
 
   int N, status;
-  char command[1024];
   IOBuffer buffer;
 
@@ -13,7 +12,14 @@
   }
 
+  char *filename = NULL;
+  if ((N = get_argument (argc, argv, "-file"))) {
+    remove_argument (N, &argc, argv);
+    filename = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
   if (argc != 2) {
-    gprint (GP_ERR, "USAGE: controller hoststats (stack)\n");
+    gprint (GP_ERR, "USAGE: controller hoststats (stack) [-reset] [-file output]\n");
     gprint (GP_ERR, "       (stack) : all, idle, busy, done, down, off, resp\n");
+    FREE (filename);
     return (FALSE);
   }
@@ -23,16 +29,18 @@
   if (!status) {
     gprint (GP_LOG, "controller is not active\n");
+    FREE (filename);
     return (TRUE);
   }
 
   // XXX this has an error?  test this out...
-  if (Reset) {
-    sprintf (command, "hoststats %s -reset", argv[1]);
-  } else {
-    sprintf (command, "hoststats %s", argv[1]);
-  }
+  char *command = NULL;
+  strextend (&command, "hoststats %s", argv[1]);
+  if (Reset) { strextend (&command, " -reset"); }
+  if (filename) { strextend (&command, " -file %s", filename); }
+
   InitIOBuffer (&buffer, 0x100);
 
   status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+  free (command);
 
   if (status) {
Index: /tags/ipp-unions-20250528/Ohana/src/opihi/pantasks/test/hoststats.sh
===================================================================
--- /tags/ipp-unions-20250528/Ohana/src/opihi/pantasks/test/hoststats.sh	(revision 42900)
+++ /tags/ipp-unions-20250528/Ohana/src/opihi/pantasks/test/hoststats.sh	(revision 42900)
@@ -0,0 +1,78 @@
+
+$hostname = `hostname`
+$NPASS = 0
+
+controller exit true
+# controller host add $hostname -threads 0
+controller host add $hostname -threads 3
+
+# a basic task which just runs 'sleep'
+task	       basic
+  command      ls -lRrt /tmp 
+  host         anyhost
+
+  periods      -poll 0.1
+  periods      -exec 0.5
+  periods      -timeout 20
+  npending 10
+  
+  stdout output.ls.txt
+  stderr output.ls.txt
+
+  task.exec
+    # echo "create command"
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+    # echo done sleep
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $JOB_STATUS"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
+# a basic task which just runs 'hoststats'
+task	       run.hoststats
+  command      ls /tmp
+  host         anyhost
+
+  periods      -poll 0.1
+  periods      -exec 10
+  periods      -timeout 20
+  npending 1
+  
+  task.exec
+    # output from the external command goes to these files
+    stdout hoststat.out.$NPASS.txt
+    stderr hoststat.err.$NPASS.txt
+    output hoststat.dat.$NPASS.txt
+    controller hoststats all
+    output stdout
+  end
+
+  # success
+  task.exit    0
+    $NPASS ++
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $JOB_STATUS"
+    $NPASS ++
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
Index: /tags/ipp-unions-20250528/Ohana/src/opihi/pcontrol/hoststats.c
===================================================================
--- /tags/ipp-unions-20250528/Ohana/src/opihi/pcontrol/hoststats.c	(revision 42899)
+++ /tags/ipp-unions-20250528/Ohana/src/opihi/pcontrol/hoststats.c	(revision 42900)
@@ -13,7 +13,15 @@
   }
 
+  char *filename = NULL;
+  if ((N = get_argument (argc, argv, "-file"))) {
+    remove_argument (N, &argc, argv);
+    filename = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
   if (argc != 2) {
-    gprint (GP_ERR, "USAGE: hoststats (stack) [-reset]\n");
+    gprint (GP_ERR, "USAGE: hoststats (stack) [-reset] [-file output]\n");
     gprint (GP_ERR, "       (stack) : all, idle, busy, done, down, off, resp\n");
+    FREE (filename);
     return (FALSE);
   }
@@ -22,14 +30,33 @@
   stack = GetHostStackByName (argv[1]);
   if (stack == NULL) {
-    gprint (GP_ERR, "hoststack not found\n");
+    gprint (GP_LOG, "hoststack not found\n");
+    FREE (filename);
     return (FALSE);
+  }
+
+  FILE *fout = NULL;
+  if (filename) {
+    fout = fopen (filename, "a");
+    if (!fout) {
+      gprint (GP_LOG, "error opening %s for output\n", filename);
+      FREE (filename);
+      return (FALSE);
+    }
   }
 
   /* print list */
   LockStack (stack);
-  gprint (GP_LOG, "Nhosts: %d\n", stack[0].Nobject);
+  if (filename) {
+    fprintf (fout, "Nhosts: %d\n", stack[0].Nobject);
+  } else {
+    gprint (GP_LOG, "Nhosts: %d\n", stack[0].Nobject);
+  }
   for (i = 0; i < stack[0].Nobject; i++) {
     host = stack[0].object[i];
-    gprint (GP_LOG, "%lld %s %d : %f %d %d %d\n", host[0].HostID, host[0].hostname, host[0].pid, host[0].read_time, host[0].read_N_Pclient, host[0].read_N_GetJobOutput, host[0].N_jobs_total);
+    if (filename) {
+      fprintf (fout, "%lld %s %d : %f %d %d %d\n", host[0].HostID, host[0].hostname, host[0].pid, host[0].read_time, host[0].read_N_Pclient, host[0].read_N_GetJobOutput, host[0].N_jobs_total);
+    } else {
+      gprint (GP_LOG, "%lld %s %d : %f %d %d %d\n", host[0].HostID, host[0].hostname, host[0].pid, host[0].read_time, host[0].read_N_Pclient, host[0].read_N_GetJobOutput, host[0].N_jobs_total);
+    }
     if (Reset) {
       host[0].read_time = 0.0;
@@ -41,4 +68,8 @@
   UnlockStack (stack);
 
+  if (filename) {
+    free (filename);
+    fclose (fout);
+  }
   return (TRUE);
 }
