Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to run interactive commands in another application window from powershell

I have another command line program which I invoke from my powershell script and would like to run some interactive commands in that window once it is opened from power shell.

In other words - I do a Invoke-Item $link_to_app which opens up the interactive command line for that application and now I would like to use the application specific commands from within powershell scripts.

e.g. app.exe -help to invoke the help command of the app.exe.

Any pointers would help. Thanks!

like image 949
user1575430 Avatar asked Oct 22 '22 07:10

user1575430


2 Answers

Try this:

$app = 'app.exe -help'
Invoke-Expression $app

Tested with this and it worked as expected:

$pingTest = 'ping -n 8 127.0.0.1'
Invoke-Expression $pingTest

From your expanded explanation you appear to want to run 2 commands within the same command prompt. This is possible, however, I'm not sure it will work in your scenario. For example:

test1.bat: echo "hello!"

test2.bat: echo "goodbye!"

$batchTest = "test1.bat && test2.bat"
cmd /c $batchTest 

output:

D:\Test>echo "hello!"
"hello!"

D:\Test>echo "goodbye!"
"goodbye!"

Hope this helps.

like image 94
Kyle Muir Avatar answered Oct 24 '22 11:10

Kyle Muir


I'm not sure, but I think what you want is the ability to have a script send input to and receive output from another program, where the other program has "state" that your script needs to be able to interact with. Below is an example of a script that drives CMD.EXE. CMD has state, such as current working directory and environment variables.

Note, that you could do what the other answerer suggested and just start the program, give all the input on the command line, and then do what you need to with the output. However for CMD if you need to make decisions based on the output, and then give CMD more input based on the previous output, you'd have to save and restore the environment and current working directories between each time you executed CMD. The approach below doesn't require that.

However the approach below does have several caveats. First it is dependent on the PS "host". It works (for me) on the command line PS, but not in ISE. This dependency is due to using the Raw host interface to determine if a key is available. Second it is timing dependent, based on the behavior of CMD (or whatever you use instead). You'll see a few sleep commands in the script. I had to experiment a whole lot to get this script to show CMD's output for a particular sub-command when that command was entered, versus CMD giving the output of previous commands after another command was entered. Comment out the sleeps to see what I mean. Third it is easy to hang Powershell. Killing CMD in task manager gets you out of the hung state, which I had to do many times.

You'll see that I added a couple of commands that the script deals with specially. This is to demonstrate that input to command can come from a PS script (versus input from the keyboard).

$global:ver++
if ($ExecutionContext.Host.name -match "ISE Host$") {
    write-warning "This script relies on RawUI functionality not implemented in ISE"
    return
}
$in = $null
$flExiting = $false

$doDebug = $false
function dot-debug {param($color) 
  if ($doDebug) {
    write-host "." -NoNewline -ForegroundColor $color
  }
}
#function dot-debug {param($color) }

$procInfo = new diagnostics.processstartinfo
$procInfo.RedirectStandardOutput=1
$procInfo.RedirectStandardInput=1
$procInfo.RedirectStandardError=1
$procInfo.FileName="cmd.exe"
$procInfo.UseShellExecute=0
$p=[diagnostics.process]::start($procInfo)
$outBuf = new char[] 4096
write-host "Version $ver"
sleep -Milliseconds 300
do {
   dot-debug red

  # This while loop determines whether input is available from either
  #  CMD's standard output or from the user typing. You don't want to
  #  get stuck waiting for input from either one if it doesn't really have input.

  :WaitIO  while ($true) {
    if (-1 -ne $p.StandardOutput.peek()) {
      dot-debug yellow
      $cnt = $p.StandardOutput.read( $outBuf, 0, 4096)
    } else {
      dot-debug Gray
      if ($host.ui.rawui.KeyAvailable -or $flExiting) {break}
    }
    $str = $outBuf[0..($cnt-1)] -join ""
    write-host "$str" -NoNewline

    while (-1 -eq ($rc =$p.StandardOutput.peek())) {
      if ($host.ui.rawui.KeyAvailable -or $flExiting) {
        break WaitIO
      }
      dot-debug DarkGray
      sleep -milli 200
    }
    dot-debug cyan
  }
  dot-debug green

  # read-host echoes input, so commands get echoed twice (cmd also echoes)
  #
  # $host.ui.rawui.ReadKey("NoEcho, IncludeKeyDown") doesn't work on ISE,
  # but does work in the PS cli shell
  if ($in -ne "exit") {$in = read-host}
  if ($in -eq "td") { # toggle debug
    $doDebug = -not $doDebug
    $p.StandardInput.WriteLine( "echo debug toggled")
    sleep -milli 300
    continue
  }
  if ($in -eq "xxx") {
    # Example of script driven output being sent to CMD
    $p.StandardInput.WriteLine( "echo This is a very long command that I do not want to have to type in everytime I want to use it")
    # You have to give CMD enough time to process command before you read stdout,
    # otherwise stdout gets "stuck" until the next time you write to stdin
    sleep -milli 1
    continue
  }
  if ($in -eq "exit") {
    $flExiting = $true
    $p.StandardInput.WriteLine($in)
    continue
  }

  foreach ($char in [char[]]$in) {
    $p.StandardInput.Write($char)
  }
  $p.StandardInput.Write("`n")
  sleep -milli 1

} until ($p.StandardOutput.EndOfStream)
like image 37
Χpẘ Avatar answered Oct 24 '22 11:10

Χpẘ