Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Commands fail with “<command> not found”, when executed using SSH.NET CreateCommand in C#

I was trying to use the SSH.NET NuGet package to remotely execute a command to grab an app version that is installed on one iPhone connected to a Mac.

If executed on the Mac itself with below command, will get me its version:

ideviceinstaller -l|grep <bundleIdOfMyAppPackage>

So I build a small utility in C# with this package, hoping that I would leverage it. However, all I get is just an empty string. Would anyone let me know what I could do to get the result I want ? Thank you !

var host = "myhost";
var username = "username";
var password = "password";

using (var client = new SshClient(host, username, password))
{
    client.HostKeyReceived += delegate(object sender, HostKeyEventArgs e) { e.CanTrust = true; };

    client.Connect();
    var command = client.CreateCommand("ideviceinstaller -l|grep <bundleIdOfMyAppPackage>");
    command.Execute();

    var result = command.Result;
    Console.WriteLine(result);

    client.Disconnect();
}

The error that I've got from command.Error is

zsh1: command not found ideviceinstaller`

which is weird because I can see ideviceinstaller inside that folder if I browse to there.


I've got it working thanks to @Martin Prikryl by changing the command to:

/usr/local/bin/ideviceinstaller -l|grep <myAppBundleId>
like image 703
Hoang Minh Avatar asked May 20 '26 20:05

Hoang Minh


1 Answers

The SSH.NET SshClient.CreateCommand (or SshClient.RunCommand) does not run shell in "login" mode and does not allocate a pseudo terminal 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), than in your regular interactive SSH session. And/or different branches in the scripts are taken, based on an absence/presence of TERM environment variable.

Possible solutions (in preference order):

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

     /path/to/ideviceinstaller ...
    

    If you do not know the full path, on common *nix systems, you can use which ideviceinstaller 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 "ideviceinstaller ..."
    
  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:

     PATH="$PATH;/path/to/ideviceinstaller" && ideviceinstaller ...
    
  5. Another (not recommended) is to use "shell" channel to execute the command via SshClient.CreateShellStream or SshClient.CreateShell as these allocate pseudo terminal

     ShellStream shellStream = client.CreateShellStream(string.Empty, 0, 0, 0, 0, 0);
     shellStream.Write("ideviceinstaller\n");
    
     while (true)
     {
         string s = shellStream.Read();
         Console.Write(s);
     }
    

    Using the shell and pseudo terminal to automate a command execution can bring you nasty side effects.

like image 111
Martin Prikryl Avatar answered May 23 '26 09:05

Martin Prikryl



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!