Skip to main content
Interactive shell sessions allow you to maintain a persistent connection with a command-line interface on the remote server. This is useful for commands that require continuous interaction or when you need to maintain state across multiple commands.

Starting a Shell Session

Use the startShell() method to initiate an interactive shell session:
import SSHClient, { PtyType } from '@dylankenneally/react-native-ssh-sftp';

const client = await SSHClient.connectWithPassword(
  '10.0.0.10',
  22,
  'user',
  'password'
);

await client.startShell(PtyType.VANILLA);

PTY (Pseudo-Terminal) Types

The library supports multiple PTY types for different terminal emulation needs:
client.startShell(PtyType.VANILLA)
Basic terminal type with minimal features. Use this for simple command execution without special terminal requirements.
Choose the PTY type based on your needs:
  • Use VANILLA for simple scripts and basic command execution
  • Use XTERM for full-featured interactive sessions
  • Use VT100/VT102 for compatibility with older systems

Reading from the Shell

Set up an event listener to receive output from the shell:
1

Register the Shell event handler

client.on('Shell', (output) => {
  if (output) {
    console.log('Shell output:', output);
  }
});
2

Start the shell session

await client.startShell(PtyType.XTERM);
3

Process incoming data

The Shell event will fire whenever the remote shell sends output:
let buffer = '';

client.on('Shell', (output) => {
  if (output) {
    buffer += output;
    
    // Process complete lines
    const lines = buffer.split('\n');
    buffer = lines.pop() || ''; // Keep incomplete line in buffer
    
    lines.forEach(line => {
      console.log('Complete line:', line);
    });
  }
});

Writing to the Shell

Send commands to the active shell session using writeToShell():
// Execute a command
await client.writeToShell('ls -la\n');

// Change directory (state persists)
await client.writeToShell('cd /var/www\n');

// Run command in new directory
await client.writeToShell('pwd\n');
Don’t forget to include the newline character (\n) at the end of your commands to execute them. Without it, the command will be typed but not executed.

Complete Example

Here’s a full example of an interactive shell session:
import SSHClient, { PtyType } from '@dylankenneally/react-native-ssh-sftp';

const runInteractiveSession = async () => {
  try {
    // Connect to the server
    const client = await SSHClient.connectWithPassword(
      '10.0.0.10',
      22,
      'user',
      'password'
    );
    
    // Set up shell output handler
    client.on('Shell', (output) => {
      if (output) {
        console.log('[Shell]', output);
      }
    });
    
    // Start the shell
    await client.startShell(PtyType.XTERM);
    console.log('Shell started');
    
    // Execute commands with state persistence
    await client.writeToShell('cd /var/log\n');
    await new Promise(resolve => setTimeout(resolve, 500));
    
    await client.writeToShell('pwd\n');
    await new Promise(resolve => setTimeout(resolve, 500));
    
    await client.writeToShell('ls -l\n');
    await new Promise(resolve => setTimeout(resolve, 1000));
    
    // Close the shell when done
    client.closeShell();
    console.log('Shell closed');
    
    // Disconnect
    client.disconnect();
  } catch (error) {
    console.error('Error:', error);
  }
};

runInteractiveSession();

Automatic Shell Initialization

The writeToShell() method automatically starts a shell session (using PtyType.VANILLA) if one isn’t already active. However, it’s recommended to explicitly call startShell() with your desired PTY type for better control.
// This will auto-start a VANILLA shell
await client.writeToShell('ls\n');

// Equivalent to:
// await client.startShell(PtyType.VANILLA);
// await client.writeToShell('ls\n');

Closing the Shell

When you’re done with the shell session, close it to free up resources:
client.closeShell();
  • Closing the shell will unregister the Shell event listener
  • Any pending output may be lost when the shell is closed
  • After closing, you need to call startShell() again to start a new session

State Persistence

One of the key advantages of shell sessions over execute() is state persistence:
// With execute() - each command runs in a fresh context
await client.execute('cd /var/www'); // Changes directory
await client.execute('pwd'); // Still in home directory!

// With shell - state persists
await client.writeToShell('cd /var/www\n');
await client.writeToShell('pwd\n'); // Shows /var/www
// Set an environment variable
await client.writeToShell('export MY_VAR=hello\n');

// Use it in subsequent commands
await client.writeToShell('echo $MY_VAR\n'); // Outputs: hello
// Start an interactive program
await client.writeToShell('python3\n');

// Send Python commands
await client.writeToShell('x = 5\n');
await client.writeToShell('print(x * 2)\n');

// Exit Python
await client.writeToShell('exit()\n');

Best Practices

  1. Always register event handlers before starting the shell to avoid missing initial output
  2. Add delays between commands if you need to wait for output (or implement a proper response parser)
  3. Use appropriate PTY types for your use case
  4. Close the shell when done to free resources
  5. Handle errors appropriately as shell operations can fail

Use Cases

  • Running interactive programs (database CLIs, REPLs, etc.)
  • Executing a series of related commands that depend on shared state
  • Monitoring real-time output from long-running processes
  • Working with shell scripts that require user input
  • Maintaining a persistent working directory across commands

Next Steps