Piping commands in powershell to ssh not working - powershell

I'm trying to pipe commands to a host running a different OS via ssh. I need to send the commands as one string. Sending one at a time isn't an option. I can get this to work using quotes and newlines when I test on the ps cli. For example, sending 3 commands:
>Write-Output "Command1`nCommand2s`nCommand3`n" | ssh -tt user#host > out.txt
The out.txt file gets populated with my command output.
$ Command1
<output omitted>
$ Command2
<output omitted>
$ Command3
<output omitted>
When I try the same thing in ps script it doesn't work:
$cmds="`"Command1``nCommand2``nCommand3``n`""
Write-Output "commands to be sent:" $cmds
Write-Output $cmds | ssh -tt user#host > out.txt
The output I get shows that the string in $cmds is being formatted correctly as per the manual cli command:
commands to be sent:
"Command1`nCommand2`nCommand3`n"
But on my ssh host it's being interpreted as:
Error: command 'Command1`nCommand2`nCommand3`n' not recognized
Any idea why?

By escaping the $cmds string as you have you are literally sending the " and ` and n characters to the remote system. Did you try it as just:
$cmds="Command1`nCommand2`nCommand3`n"
This way the output from Write-Output "stuff" and $cmds="stuff" ; Write-Output $cmds would be the same.

Related

Filename on Windows that works with pipes in batch or PowerShell

I have a Linux shell script with a line that looks like this:
./cmd1 -o /dev/stdout | ./cmd2
I'm trying to port it to Windows. I don't care whether it ends up as a batch file or a PowerShell script. I tried this in a batch file at first:
cmd1 -o con | cmd2
But this sent the output of cmd1 to the console, and cmd2 didn't receive any input. The problem is that con is the equivalent of /dev/tty, not /dev/stdout. What output filename can I pass that will be the equivalent of /dev/stdout, or at least close enough to make the pipe work?
Can you try this?
./cmd1 -o /dev/stdout | ./cmd2 -i /dev/stdin
The equivalent command on Windows using batch or PowerShell would be:
cmd1 -o - | cmd2 -i -
you can pipe it to foreach-object
cmd1 | % { cmd2 $_ }
the $_ stands for the result of the output that is sent to the next command, sometimes with certain programs you may need to use write-output (cmd1) | % {cmd2} but the first one should suffice for what you want to do.
You can also store the output of the first command like this
$var = cmd1
or like this
cmd1 > $var
If you can specify the file output and file input for both commands you could try this.
$file = "C:\file"
cmd1 -o $file ; cmd2 -i $file
but as phuclv commented, there's no stdout equivalents in Windows by default

How to pipe into grep using backticks in Linux shell script?

I am trying to execute, what I thought would be, a simple shell command within a script. When I execute this from the command prompt, it works well:
$ sudo cat /etc/sysconfig/network-scripts/ifcfg-en0 | grep "IPADDR"
IPADDR=192.168.1.10
However, if I put this into a shell script:
#!/usr/bin/sh
my_command=`sudo cat /etc/sysconfig/network-scripts/ifcfg-en0 | grep "IPADDR"`
${my_command}
echo $?
I get this error:
$ sudo ./myscript.sh
./myscript.sh: line 3: IPADDR=192.168.1.10: command not found
So, how can I successfully execute this line within my shell script?
Thanks!
The problem in your case is that you are executing the result of the command...
This line executes the code as it's between "``" that are special characters for executing the given string as a command:
my_command=`sudo cat /etc/sysconfig/network-scripts/ifcfg-en0 | grep "IPADDR"`
as a result, $my_command is "IPADDR=192.168.1.10"
Then you are trying to execute it for the second time:
${my_command}
Thats why you are getting this error. There is no such a command as "IPADDR=192.168.1.10".
Just use $my_command as a result that contains your desired grepped part and skip the ${my_command} line:
#!/usr/bin/sh
my_command=`sudo cat /etc/sysconfig/network-scripts/ifcfg-en0 | grep "IPADDR"`
echo $my_command

Passing piped commands via SSH

I'm using the Powershell module Posh-SSH to ssh into an Ubuntu server and run commands. I'm not having any difficulty passing simple lines such as:
$sshSession = New-SSHSession -ComputerName server001 -Credential $credential
$sshStream = New-SSHShellStream -Index $sshSession.sessionID
$sshStream.WriteLine("history")
$sshStream.Read()
The last line outputs exactly what it's supposed to. I want to run the following on the server:
for guest in `nova list --all-tenants --host serverName | grep Shutdown | awk '{ print $2 }'`; do nova start $guest; sleep 5; done"
Pasting this line right into $sshStream.WriteLine("") doesn't work at all as ` is an escape character in Powershell and $'s are used for variables already. I attempted to work around this by escaping some characters and putting it into a variable:
$block = "for guest in ``nova list --all-tenants --host server001 | grep Shutdown | awk '{ print `$2 }'`; do nova start `$guest; sleep 5; done"
$sshStream.WriteLine("$block")
$sshStream.WriteLine($block)
Both of my attempts above do not get read properly on the server. Any idea how I can work around this or if there's a better way to do this?
Thanks in advance
As TessellatingHeckler suggested, I used single-quotes and it worked:
$block = 'for guest in `nova list --all-tenants | grep Shutdown | awk ''{ print $2 }''`; do nova stop $guest; done'
$sshStream.WriteLine($block)
Try this
$block = #"
for guest in ``nova list --all-tenants --host server001 | grep Shutdown | awk '{ print `$2 }'`; do nova start `$guest; sleep 5; done
"#
$sshStream.WriteLine($block)
It will treat the "block" as a literal string (i.e. no escaping).

PowerShell equivalent of curl http://www.example.com/script.sh | bash -s <arguments>

How to run a remote script in PowerShell with arguments.
For linux I'm using curl http://www.example.com/script.sh | bash -s <arguments>.
Is there a similar equivalent in PowerShell.
Use Case: User-Data script for Amazon EC2
Thanks
You'll often find examples out there like this:
iex (New-Object System.Net.WebClient).DownloadString('http://domain/script.ps1')
iex is an alias for Invoke-Expression (think of it like exec or eval in some other languages).
The rest of it is creating a [System.Net.WebClient] object, which has a method to download the contents of a URL and return it as a string.

Quoting in bash and perl in recursive ssh command

I am writing a perl script to login in to a server with ssh and do some shell commands on the server. The problem is that the server is only accessible by first logging into another server.
(I am using password-less login with ssh keys).
The following bash script is working correctly, and illustrates the problem:
#! /bin/bash
server1="login.uib.no"
server2="cipr-cluster01"
ssh "$server1" "ssh $server2 \"echo \\\"\\\$HOSTNAME\\\"\""
It prints the correct host name to my screen: cipr-cluster01. However, when trying to do same thing in Perl:
my $server1="login.uib.no";
my $server2="cipr-cluster01";
print qx/ssh "$server1" "ssh $server2 \"echo \\\"\\\$HOSTNAME\\\"\""/;
I get the following output: login.uib.no. So I guess, there is some problems with the quoting for the perl script..
qx works like double quotes. You have to backslash some more:
print qx/ssh "$server1" "ssh $server2 \"echo \\\\"\\\$HOSTNAME\\\\"\""/;
Using single quotes might simplify the command a lot:
print qx/ssh "$server1" 'ssh $server2 "echo \\\$HOSTNAME"'/;
You can simplify the quoting a bit by using the ProxyCommand option that tells ssh to connect to $server2 via $server1, rather than explicitly running ssh on $server1.
print qx/ssh -o ProxyCommand="ssh -W %h:%p $server1" "$server2" 'echo \$HOSTNAME'/;
(There is some residual output from the proxy command (Killed by signal 1) that I'm not sure how to get rid of.)
You can use Net::OpenSSH that is able to do the quoting automatically:
my $ssh_gw = Net::OpenSSH->new($gateway);
my $proxy_command = $ssh_gw->make_remote_command({tunnel => 1}, $host, 22);
my $ssh = Net::OpenSSH->new($host, proxy_command => $proxy_command);
$ssh->system('echo $HOSTNAME');