Command (.4Gl) Executed with Ssh.Net Sshclient.Runcommand Fails with "No Such File or Directory"

Command (.4gl) executed with SSH.NET SshClient.RunCommand fails with No such file or directory

The SshClient.RunCommand uses SSH "exec" channel internally. It, by default, (rightfully) does not allocate a pseudo terminal (PTY) for the session. As a consequence a different set of startup scripts is (might be) sourced. And/or different branches in the scripts are taken, based on absence/presence of the TERM environment variable. So the environment might differ from the interactive session, you use with your SSH client.

So, in your case, the PATH is probably set differently; and consequently the shared object cannot be found.

To verify that this is the root cause, disable the pseudo terminal allocation in your SSH client. For example in PuTTY, it's Connection > SSH > TTY > Don't allocate a pseudo terminal. Then, go to Connection > SSH > Remote command and enter your g4l command. Check Session > Close window on exit > Never and open the session. You should get the same "No such file or directory" error.


Ways to fix this, in preference order:

  1. Fix the scripts not to rely on a specific environment.

  2. Fix your startup scripts to set the PATH the same for both interactive and non-interactive sessions.

  3. If the command itself relies on a specific environment setup and you cannot fix the startup scripts, you can change the environment in the command itself. Syntax for that depends on the remote system and/or the shell. In common *nix systems, this works:

     sshclients.RunCommand("PATH=\"$PATH;/path/to/g4l\" && sh ...");
  4. Another (not recommended) approach is to force the pseudo terminal allocation for the "exec" channel.

    Though SSH.NET does not support this. You would have to modify its code issue SendPseudoTerminalRequest request in .RunCommand implementation (I didn't test this).

    You can also try to use "shell" channel using .CreateShell method. For it, SSH.NET does support pseudo terminal allocation.

    Though, using the pseudo terminal to automate a command execution can bring you nasty side effects. See for example Is there a simple way to get rid of junk values that come when you SSH using Python's Paramiko library and fetch output from CLI of a remote machine?


For a similar issues, see

  • Renci SSH.NET - no result string returned for opmnctl
  • Certain Unix commands fail with "... not found", when executed through Java using JSch
  • Commands executed using JSch behaves differently than in SSH terminal (bypasses confirm prompt message of "yes/"no")
  • JSch: Is there a way to expose user environment variables to "exec" channel?

Renci SSH.NET - no result string returned for opmnctl

If the opmnctl fails for some reason, it might have produced a message on the error output.

So check also cmd.Error and cmd.ExitStatus.


Note that the .RunCommand method uses a non-interactive terminal, what may cause problems with some commands.

Particularly a different environment may be set, when the non-interactive terminal is used as a different set of startup scripts is executed and/or different branches in the scripts are taken (based on a value of a TERM environment variable).

In your case, it seems that the opmnctl is not included in PATH on non-interactive terminals.

You should get the same issue when you force the non-interactive terminal in your SSH client.

For example:

  • With OpenSSH command-line ssh client, use -T switch (Disable pseudo-tty allocation).

    ssh -T user@host opmnctl status -l
  • The same with PLink (from PuTTY package)

    plink.exe -T user@host opmnctl status -l

(The -T should be implicit when command is specified on command-line anyway)


Solution are:

  • Fix your startup scripts to include the opmnctl to PATH even on non-interactive terminals (preferred)
  • Use a full path to opmnctl in your command.
  • Modify SSH.NET code to issue SendPseudoTerminalRequest request in .RunCommand implementation (I didn't test this).

Certain Unix commands fail with ... not found , when executed through Java using JSch

The "exec" channel in the JSch (rightfully) does not allocate a pseudo terminal (PTY) for the session. As a consequence a different set of startup scripts is (might be) sourced (particularly for non-interactive sessions, .bash_profile is not sourced). And/or different branches in the scripts are taken, based on absence/presence of the TERM environment variable. So the environment might differ from the interactive session, you use with your SSH client.

So, in your case, the PATH is probably set differently; and consequently the air executable cannot be found.

To verify that this is the root cause, disable the pseudo terminal allocation in your SSH client. For example in PuTTY, it's Connection > SSH > TTY > Don't allocate a pseudo terminal. Then, go to Connection > SSH > Remote command and enter your air ... command. Check Session > Close window on exit > Never and open the session. You should get the same "air not found" error.


Ways to fix this, in preference order:

  1. Fix the command not to rely on a specific environment. Use a full path to air in the command. E.g.:

    /bin/air sandbox run <graph-path>

    If you do not know the full path, on common *nix systems, you can use which air command in your interactive SSH session.

  2. Fix your startup scripts to set the PATH the same for both interactive and non-interactive sessions.

  3. Try running the script explicitly via login shell (use --login switch with common *nix shells):

    bash --login -c "air sandbox run sandbox run <graph-path>"
  4. If the command itself relies on a specific environment setup and you cannot fix the startup scripts, you can change the environment in the command itself. Syntax for that depends on the remote system and/or the shell. In common *nix systems, this works:

    String command="PATH=\"$PATH;/path/to/air\" && air sandbox run <graph-path>";
  5. Another (not recommended) approach is to force the pseudo terminal allocation for the "exec" channel using the .setPty method:

    Channel channel = session.openChannel("exec");
    ((ChannelExec)channel).setPty(true);

    Using the pseudo terminal to automate a command execution can bring you nasty side effects. See for example Is there a simple way to get rid of junk values that come when you SSH using Python's Paramiko library and fetch output from CLI of a remote machine?


For a similar issues, see

  • Certain Unix commands fail with "... not found", when executed through Java using JSch even with setPty enabled
  • Commands executed using JSch behaves differently than in SSH terminal (bypasses confirm prompt message of "yes/"no")
  • JSch: Is there a way to expose user environment variables to "exec" channel?
  • Command (.4gl) executed with SSH.NET SshClient.RunCommand fails with "No such file or directory"

Commands executed using JSch behaves differently than in SSH terminal (bypasses confirm prompt message of yes/ no )

By default the JSch does not request PTY (pseudo-terminal).

That can cause some commands to behave differently than in an interactive SSH terminal.

You can override this using the ChannelSession.setPty.


If the above does not help, see also similar questions:

  • Certain Unix commands fail with "... not found", when executed through Java using JSch
  • Certain Unix commands fail with "... not found", when executed through Java using JSch even with setPty enabled

JSch: Is there a way to expose user environment variables to exec channel?

Since you're not opening up an interactive shell, your environment variables won't be set. However, you can use the bash command with --login (man bash for more details) to get the results you want

bash --login -c 'command arg1 ...'"


Related Topics



Leave a reply



Submit