简体   繁体   中英

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>

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. Eg:

     /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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM