PureBasic
PureBasic
SSH Remote Shell with Multiple Commands
See more SSH Examples
Demonstrates running several commands one after another in a single remote shell session, retrieving each command's output separately. No PTY is requested, so each command is followed by an echoed marker that signals where its output ends.
Background: Interleaving commands and reads in one shell requires a way to know where each command's output stops. An interactive session would use the shell prompt, but without a PTY the shell is non-interactive and never prints one — so the dependable technique is to append
echo SOME_MARKER to each command and read up to that marker. Retrieving the text also clears the receive buffer, which matters here: if a marker were left buffered, the next receive would match it immediately and return the wrong output.Chilkat PureBasic Downloads
IncludeFile "CkSsh.pb"
Procedure ChilkatExample()
success.i = 0
; This example requires the Chilkat API to have been previously unlocked.
; See Global Unlock Sample for sample code.
; Demonstrates running several commands one after another in a single remote shell session,
; retrieving each command's output separately.
;
; No PTY is requested, so the shell runs non-interactively and never prints a command prompt.
; Because there is no prompt to synchronize on, each command is followed by an "echo" of a
; unique marker, and the output is read up to that marker.
ssh.i = CkSsh::ckCreate()
If ssh.i = 0
Debug "Failed to create object."
ProcedureReturn
EndIf
hostname.s = "ssh.example.com"
port.i = 22
success = CkSsh::ckConnect(ssh,hostname,port)
If success = 0
Debug CkSsh::ckLastErrorText(ssh)
CkSsh::ckDispose(ssh)
ProcedureReturn
EndIf
; Normally you would not hard-code the password in source. You should instead obtain it
; from an interactive prompt, environment variable, or a secrets vault.
password.s = "mySshPassword"
success = CkSsh::ckAuthenticatePw(ssh,"mySshLogin",password)
If success = 0
Debug CkSsh::ckLastErrorText(ssh)
CkSsh::ckDispose(ssh)
ProcedureReturn
EndIf
channelNum.i = CkSsh::ckOpenSessionChannel(ssh)
If channelNum < 0
Debug CkSsh::ckLastErrorText(ssh)
CkSsh::ckDispose(ssh)
ProcedureReturn
EndIf
; Start a shell WITHOUT requesting a PTY. Without a PTY the shell sees it is connected to a
; raw data pipe and enters non-interactive mode: it suppresses the command prompt, does not
; echo typed commands back, and skips interactive login scripts. The output is therefore
; clean and easily parseable -- but because no prompt is ever sent, code must not wait for
; one. Instead, have each command print a predictable marker, or wait for EOF / channel close.
success = CkSsh::ckSendReqShell(ssh,channelNum)
If success = 0
Debug CkSsh::ckLastErrorText(ssh)
CkSsh::ckDispose(ssh)
ProcedureReturn
EndIf
; IMPORTANT: Set a read timeout before receiving until a match. ReadTimeoutMs defaults to 0,
; which means no limit -- without it, this call waits forever if the received data never
; contains a match.
CkSsh::setCkReadTimeoutMs(ssh, 15000)
caseSensitive.i = 1
; ---- 1st command: change directory ----
success = CkSsh::ckChannelSendString(ssh,channelNum,"cd /tmp; echo END_OF_CMD_1" + Chr(10),"utf-8")
If success = 0
Debug CkSsh::ckLastErrorText(ssh)
CkSsh::ckDispose(ssh)
ProcedureReturn
EndIf
success = CkSsh::ckChannelReceiveUntilMatch(ssh,channelNum,"END_OF_CMD_1","utf-8",caseSensitive)
If success = 0
Debug CkSsh::ckLastErrorText(ssh)
CkSsh::ckDispose(ssh)
ProcedureReturn
EndIf
; Retrieving the text also clears the receive buffer, which matters: if the next marker were
; left in the buffer, the following receive would match immediately.
cmdOutput.s = CkSsh::ckGetReceivedText(ssh,channelNum,"utf-8")
If CkSsh::ckLastMethodSuccess(ssh) = 0
Debug CkSsh::ckLastErrorText(ssh)
CkSsh::ckDispose(ssh)
ProcedureReturn
EndIf
Debug cmdOutput
; ---- 2nd command: directory listing ----
success = CkSsh::ckChannelSendString(ssh,channelNum,"ls; echo END_OF_CMD_2" + Chr(10),"utf-8")
If success = 0
Debug CkSsh::ckLastErrorText(ssh)
CkSsh::ckDispose(ssh)
ProcedureReturn
EndIf
success = CkSsh::ckChannelReceiveUntilMatch(ssh,channelNum,"END_OF_CMD_2","utf-8",caseSensitive)
If success = 0
Debug CkSsh::ckLastErrorText(ssh)
CkSsh::ckDispose(ssh)
ProcedureReturn
EndIf
cmdOutput = CkSsh::ckGetReceivedText(ssh,channelNum,"utf-8")
If CkSsh::ckLastMethodSuccess(ssh) = 0
Debug CkSsh::ckLastErrorText(ssh)
CkSsh::ckDispose(ssh)
ProcedureReturn
EndIf
Debug cmdOutput
; ---- 3rd command: long listing, then finish the session ----
success = CkSsh::ckChannelSendString(ssh,channelNum,"ls -l" + Chr(10),"utf-8")
If success = 0
Debug CkSsh::ckLastErrorText(ssh)
CkSsh::ckDispose(ssh)
ProcedureReturn
EndIf
; No more input will be sent.
success = CkSsh::ckChannelSendEof(ssh,channelNum)
If success = 0
Debug CkSsh::ckLastErrorText(ssh)
CkSsh::ckDispose(ssh)
ProcedureReturn
EndIf
success = CkSsh::ckChannelReceiveToClose(ssh,channelNum)
If success = 0
Debug CkSsh::ckLastErrorText(ssh)
CkSsh::ckDispose(ssh)
ProcedureReturn
EndIf
cmdOutput = CkSsh::ckGetReceivedText(ssh,channelNum,"utf-8")
If CkSsh::ckLastMethodSuccess(ssh) = 0
Debug CkSsh::ckLastErrorText(ssh)
CkSsh::ckDispose(ssh)
ProcedureReturn
EndIf
Debug cmdOutput
CkSsh::ckDisconnect(ssh)
CkSsh::ckDispose(ssh)
ProcedureReturn
EndProcedure