Passing parameter from one batch file to another not working [duplicate] - linux

I need to execute a shell script remotely inside the Linux box from Windows
#!/bin/bash
if [ "$#" -ne 1 ]; then
echo "Illegal number of parameters"
exit
fi
echo "$1"
Here is the command I ran from Windows command prompt
cmd> plink.exe -ssh username#host -pw gbG32s4D/ -m C:\myscript.sh 5
I am getting output as
"Illegal number of parameters"
Is there any way I can pass command line parameter to shell script which will execute on remote server?

You misunderstand how the -m switch works.
It is just a way to make plink load the commands to send to the server from a local file.
The file is NOT uploaded and executed on the remote server (with arguments).
It's contents is read locally and sent to the server and executed there as if you typed it on a (remote) command line. You cannot give it arguments.
A workaround is to generate the file on the fly locally before running plink from a batch file (say run.bat):
echo echo %1 > script.tmp
plink.exe -ssh username#host -pw gbG32s4D/ -m script.tmp
Then run the batch file with the argument:
run.bat 5
The above will make the script execute echo 5 on the server.
If the script is complex, instead of assembling it locally, have it ready on the server (as #MarcelKuiper suggested) and execute just the script via Plink.
plink.exe -ssh username#host -pw gbG32s4D/ "./myscript.sh %1"
In this case, as we execute just one command, you can pass it on Plink command line, including the arguments. You do not have to use the -m switch with a (temporary) file.

I triggered the Shell script in "commands.txt" from Plink which worked for me like a charm with below method I tried:
You can define your script as an one liner using && in a file (I defined in one liner)
You need to run your command in <
Note: Use first EOF in quote like <<'EOF' but not the last one. Else you will see you code will behave weirdly.
Please see below.
Example:
sudo -i <<'EOF'
<your script here>
EOF
Then, finally run it using Plink:
plink -ssh username#hostname -pw password -m commands.txt

Have you tried putting the command and argument in quotes:
i.e. -m "C:\myscript.sh 5"

Related

Running bash script over SSH [duplicate]

I have to run a local shell script (windows/Linux) on a remote machine.
I have SSH configured on both machine A and B. My script is on machine A which will run some of my code on a remote machine, machine B.
The local and remote computers can be either Windows or Unix based system.
Is there a way to run do this using plink/ssh?
If Machine A is a Windows box, you can use Plink (part of PuTTY) with the -m parameter, and it will execute the local script on the remote server.
plink root#MachineB -m local_script.sh
If Machine A is a Unix-based system, you can use:
ssh root#MachineB 'bash -s' < local_script.sh
You shouldn't have to copy the script to the remote server to run it.
This is an old question, and Jason's answer works fine, but I would like to add this:
ssh user#host <<'ENDSSH'
#commands to run on remote host
ENDSSH
This can also be used with su and commands which require user input. (note the ' escaped heredoc)
Since this answer keeps getting bits of traffic, I would add even more info to this wonderful use of heredoc:
You can nest commands with this syntax, and that's the only way nesting seems to work (in a sane way)
ssh user#host <<'ENDSSH'
#commands to run on remote host
ssh user#host2 <<'END2'
# Another bunch of commands on another host
wall <<'ENDWALL'
Error: Out of cheese
ENDWALL
ftp ftp.example.com <<'ENDFTP'
test
test
ls
ENDFTP
END2
ENDSSH
You can actually have a conversation with some services like telnet, ftp, etc. But remember that heredoc just sends the stdin as text, it doesn't wait for response between lines
I just found out that you can indent the insides with tabs if you use <<-END!
ssh user#host <<-'ENDSSH'
#commands to run on remote host
ssh user#host2 <<-'END2'
# Another bunch of commands on another host
wall <<-'ENDWALL'
Error: Out of cheese
ENDWALL
ftp ftp.example.com <<-'ENDFTP'
test
test
ls
ENDFTP
END2
ENDSSH
(I think this should work)
Also see
http://tldp.org/LDP/abs/html/here-docs.html
Also, don't forget to escape variables if you want to pick them up from the destination host.
This has caught me out in the past.
For example:
user#host> ssh user2#host2 "echo \$HOME"
prints out /home/user2
while
user#host> ssh user2#host2 "echo $HOME"
prints out /home/user
Another example:
user#host> ssh user2#host2 "echo hello world | awk '{print \$1}'"
prints out "hello" correctly.
This is an extension to YarekT's answer to combine inline remote commands with passing ENV variables from the local machine to the remote host so you can parameterize your scripts on the remote side:
ssh user#host ARG1=$ARG1 ARG2=$ARG2 'bash -s' <<'ENDSSH'
# commands to run on remote host
echo $ARG1 $ARG2
ENDSSH
I found this exceptionally helpful by keeping it all in one script so it's very readable and maintainable.
Why this works. ssh supports the following syntax:
ssh user#host remote_command
In bash we can specify environment variables to define prior to running a command on a single line like so:
ENV_VAR_1='value1' ENV_VAR_2='value2' bash -c 'echo $ENV_VAR_1 $ENV_VAR_2'
That makes it easy to define variables prior to running a command. In this case echo is our command we're running. Everything before echo defines environment variables.
So we combine those two features and YarekT's answer to get:
ssh user#host ARG1=$ARG1 ARG2=$ARG2 'bash -s' <<'ENDSSH'...
In this case we are setting ARG1 and ARG2 to local values. Sending everything after user#host as the remote_command. When the remote machine executes the command ARG1 and ARG2 are set the local values, thanks to local command line evaluation, which defines environment variables on the remote server, then executes the bash -s command using those variables. Voila.
<hostA_shell_prompt>$ ssh user#hostB "ls -la"
That will prompt you for password, unless you have copied your hostA user's public key to the authorized_keys file on the home of user .ssh's directory. That will allow for passwordless authentication (if accepted as an auth method on the ssh server's configuration)
I've started using Fabric for more sophisticated operations. Fabric requires Python and a couple of other dependencies, but only on the client machine. The server need only be a ssh server. I find this tool to be much more powerful than shell scripts handed off to SSH, and well worth the trouble of getting set up (particularly if you enjoy programming in Python). Fabric handles running scripts on multiple hosts (or hosts of certain roles), helps facilitate idempotent operations (such as adding a line to a config script, but not if it's already there), and allows construction of more complex logic (such as the Python language can provide).
cat ./script.sh | ssh <user>#<host>
chmod +x script.sh
ssh -i key-file root#111.222.3.444 < ./script.sh
Try running ssh user#remote sh ./script.unx.
Assuming you mean you want to do this automatically from a "local" machine, without manually logging into the "remote" machine, you should look into a TCL extension known as Expect, it is designed precisely for this sort of situation. I've also provided a link to a script for logging-in/interacting via SSH.
https://www.nist.gov/services-resources/software/expect
http://bash.cyberciti.biz/security/expect-ssh-login-script/
ssh user#hostname ". ~/.bashrc;/cd path-to-file/;. filename.sh"
highly recommended to source the environment file(.bashrc/.bashprofile/.profile). before running something in remote host because target and source hosts environment variables may be deffer.
I use this one to run a shell script on a remote machine (tested on /bin/bash):
ssh deploy#host . /home/deploy/path/to/script.sh
if you wanna execute command like this
temp=`ls -a`
echo $temp
command in `` will cause errors.
below command will solve this problem
ssh user#host '''
temp=`ls -a`
echo $temp
'''
If the script is short and is meant to be embedded inside your script and you are running under bash shell and also bash shell is available on the remote side, you may use declare to transfer local context to remote. Define variables and functions containing the state that will be transferred to the remote. Define a function that will be executed on the remote side. Then inside a here document read by bash -s you can use declare -p to transfer the variable values and use declare -f to transfer function definitions to the remote.
Because declare takes care of the quoting and will be parsed by the remote bash, the variables are properly quoted and functions are properly transferred. You may just write the script locally, usually I do one long function with the work I need to do on the remote side. The context has to be hand-picked, but the following method is "good enough" for any short scripts and is safe - should properly handle all corner cases.
somevar="spaces or other special characters"
somevar2="!##$%^"
another_func() {
mkdir -p "$1"
}
work() {
another_func "$somevar"
touch "$somevar"/"$somevar2"
}
ssh user#server 'bash -s' <<EOT
$(declare -p somevar somevar2) # transfer variables values
$(declare -f work another_func) # transfer function definitions
work # call the function
EOT
The answer here (https://stackoverflow.com/a/2732991/4752883) works great if
you're trying to run a script on a remote linux machine using plink or ssh.
It will work if the script has multiple lines on linux.
**However, if you are trying to run a batch script located on a local
linux/windows machine and your remote machine is Windows, and it consists
of multiple lines using **
plink root#MachineB -m local_script.bat
wont work.
Only the first line of the script will be executed. This is probably a
limitation of plink.
Solution 1:
To run a multiline batch script (especially if it's relatively simple,
consisting of a few lines):
If your original batch script is as follows
cd C:\Users\ipython_user\Desktop
python filename.py
you can combine the lines together using the "&&" separator as follows in your
local_script.bat file:
https://stackoverflow.com/a/8055390/4752883:
cd C:\Users\ipython_user\Desktop && python filename.py
After this change, you can then run the script as pointed out here by
#JasonR.Coombs: https://stackoverflow.com/a/2732991/4752883 with:
`plink root#MachineB -m local_script.bat`
Solution 2:
If your batch script is relatively complicated, it may be better to use a batch
script which encapsulates the plink command as well as follows as pointed out
here by #Martin https://stackoverflow.com/a/32196999/4752883:
rem Open tunnel in the background
start plink.exe -ssh [username]#[hostname] -L 3307:127.0.0.1:3306 -i "[SSH
key]" -N
rem Wait a second to let Plink establish the tunnel
timeout /t 1
rem Run the task using the tunnel
"C:\Program Files\R\R-3.2.1\bin\x64\R.exe" CMD BATCH qidash.R
rem Kill the tunnel
taskkill /im plink.exe
This bash script does ssh into a target remote machine, and run some command in the remote machine, do not forget to install expect before running it (on mac brew install expect )
#!/usr/bin/expect
set username "enterusenamehere"
set password "enterpasswordhere"
set hosts "enteripaddressofhosthere"
spawn ssh $username#$hosts
expect "$username#$hosts's password:"
send -- "$password\n"
expect "$"
send -- "somecommand on target remote machine here\n"
sleep 5
expect "$"
send -- "exit\n"
You can use runoverssh:
sudo apt install runoverssh
runoverssh -s localscript.sh user host1 host2 host3...
-s runs a local script remotely
Useful flags:
-g use a global password for all hosts (single password prompt)
-n use SSH instead of sshpass, useful for public-key authentication
If it's one script it's fine with the above solution.
I would set up Ansible to do the Job. It works in the same way (Ansible uses ssh to execute the scripts on the remote machine for both Unix or Windows).
It will be more structured and maintainable.
It is unclear if the local script uses locally set variables, functions, or aliases.
If it does this should work:
myscript.sh:
#!/bin/bash
myalias $myvar
myfunction $myvar
It uses $myvar, myfunction, and myalias. Let us assume they is set locally and not on the remote machine.
Make a bash function that contains the script:
eval "myfun() { `cat myscript.sh`; }"
Set variable, function, and alias:
myvar=works
alias myalias='echo This alias'
myfunction() { echo This function "$#"; }
And "export" myfun, myfunction, myvar, and myalias to server using env_parallel from GNU Parallel:
env_parallel -S server -N0 --nonall myfun ::: dummy
Extending answer from #cglotr. In order to write inline command use printf, it useful for simple command and it support multiline using char escaping '\n'
example :
printf "cd /to/path/your/remote/machine/log \n tail -n 100 Server.log" | ssh <user>#<host> 'bash -s'
See don't forget to add bash -s
There is another approach ,you can copy your script in your host with scp command then execute it easily .
First, copy the script over to Machine B using scp
[user#machineA]$ scp /path/to/script user#machineB:/home/user/path
Then, just run the script
[user#machineA]$ ssh user#machineB "/home/user/path/script"
This will work if you have given executable permission to the script.

PLINK command executes but no file created

I have wrote a bash file so If an user select a specific option then on a linux server a specific .sh file will execute.
For example:
If a user presses 1:
for %%? in (1) do if /I "%C%"=="%%?" goto print
:print
CLS
start C:\tools\PLINK.EXE -ssh -pw <password> -t <user>#10.111.11.111 "echo <password> | sudo -S /var/www/test/test.sh"
I can see the shell script starting but on my linux server "test.sh" has commands to create a .txt file.
echo $NAME "test" >> test.txt (for example)
My question now is... why is it if I run test.sh on the linux server directly the test.txt has been succesfully created.
If I run test.sh trough my windows batch file, I can see the command is activated but no test.txt file is created on the linux server.
Thank you for the help!
Either the test.txt will be in your HOME directory or you can give the entire path along with the test.txt to verify.

Running Nested Command Lines with HERE

This command myprogram.sh command in CygWin installed with Chocolatey, called from the Windows Command Line, with an alias server01 created at the .ssh folder, everything works fine:
# File myprogram.sh
ssh -p 66622 user#localhost << HERE
ssh server01 << EOF
command1
command2
EOF
HERE
Because i have several servers, i have to build several .sh files for different set of commands, so i have to create a lot of .sh files
But i've been unable to run the same instructions from a single line from the command line. Is that possible, in order to run these chain of instructions from a same place?
#!/bin/bash
array=(server1 server2 server3 .... serverN)
for i in ${array[#]}
do
echo ${i}
ssh -p 66622 user#${i} "command1"
done
you can change the "command1" to "command.sh"

Best way to script remote SSH commands in Batch (Windows)

I am looking to script something in batch which will need to run remote ssh commands on Linux. I would want the output returned so I can either display it on the screen or log it.
I tried putty.exe -ssh user#host -pw password -m command_run but it doesn't return anything on my screen.
Anyone done this before?
The -m switch of PuTTY takes a path to a script file as an argument, not a command.
Reference: https://the.earth.li/~sgtatham/putty/latest/htmldoc/Chapter3.html#using-cmdline-m
So you have to save your command (command_run) to a plain text file (e.g. c:\path\command.txt) and pass that to PuTTY:
putty.exe -ssh user#host -pw password -m c:\path\command.txt
Though note that you should use Plink (a command-line connection tool from PuTTY suite). It's a console application, so you can redirect its output to a file (what you cannot do with PuTTY).
A command-line syntax is identical, an output redirection added:
plink.exe -ssh user#host -pw password -m c:\path\command.txt > output.txt
See Using the command-line connection tool Plink.
And with Plink, you can actually provide the command directly on its command-line:
plink.exe -ssh user#host -pw password command > output.txt
Similar questions:
Automating running command on Linux from Windows using PuTTY
Executing command in Plink from a batch file
You can also use Bash on Ubuntu on Windows directly. E.g.,
bash -c "ssh -t user#computer 'cd /; sudo my-command'"
Per Martin Prikryl's comment below:
The -t enables terminal emulation. Whether you need the terminal emulation for sudo depends on configuration (and by default you do no need it, while many distributions override the default). On the contrary, many other commands need terminal emulation.
As an alternative option you could install OpenSSH http://www.mls-software.com/opensshd.html and then simply ssh user#host -pw password -m command_run
Edit: After a response from user2687375 when installing, select client only. Once this is done you should be able to initiate SSH from command.
Then you can create an ssh batch script such as
ECHO OFF
CLS
:MENU
ECHO.
ECHO ........................
ECHO SSH servers
ECHO ........................
ECHO.
ECHO 1 - Web Server 1
ECHO 2 - Web Server 2
ECHO E - EXIT
ECHO.
SET /P M=Type 1 - 2 then press ENTER:
IF %M%==1 GOTO WEB1
IF %M%==2 GOTO WEB2
IF %M%==E GOTO EOF
REM ------------------------------
REM SSH Server details
REM ------------------------------
:WEB1
CLS
call ssh user#xxx.xxx.xxx.xxx
cmd /k
:WEB2
CLS
call ssh user#xxx.xxx.xxx.xxx
cmd /k

Pseudo-terminal will not be allocated because stdin is not a terminal

I am trying to write a shell script that creates some directories on a remote server and then uses scp to copy files from my local machine onto the remote. Here's what I have so far:
ssh -t user#server<<EOT
DEP_ROOT='/home/matthewr/releases'
datestamp=$(date +%Y%m%d%H%M%S)
REL_DIR=$DEP_ROOT"/"$datestamp
if [ ! -d "$DEP_ROOT" ]; then
echo "creating the root directory"
mkdir $DEP_ROOT
fi
mkdir $REL_DIR
exit
EOT
scp ./dir1 user#server:$REL_DIR
scp ./dir2 user#server:$REL_DIR
Whenever I run it I get this message:
Pseudo-terminal will not be allocated because stdin is not a terminal.
And the script just hangs forever.
My public key is trusted on the server and I can run all the commands outside of the script just fine. Any ideas?
Try ssh -t -t(or ssh -tt for short) to force pseudo-tty allocation even if stdin isn't a terminal.
See also: Terminating SSH session executed by bash script
From ssh manpage:
-T Disable pseudo-tty allocation.
-t Force pseudo-tty allocation. This can be used to execute arbitrary
screen-based programs on a remote machine, which can be very useful,
e.g. when implementing menu services. Multiple -t options force tty
allocation, even if ssh has no local tty.
Also with option -T from manual
Disable pseudo-tty allocation
Per zanco's answer, you're not providing a remote command to ssh, given how the shell parses the command line. To solve this problem, change the syntax of your ssh command invocation so that the remote command is comprised of a syntactically correct, multi-line string.
There are a variety of syntaxes that can be used. For example, since commands can be piped into bash and sh, and probably other shells too, the simplest solution is to just combine ssh shell invocation with heredocs:
ssh user#server /bin/bash <<'EOT'
echo "These commands will be run on: $( uname -a )"
echo "They are executed by: $( whoami )"
EOT
Note that executing the above without /bin/bash will result in the warning Pseudo-terminal will not be allocated because stdin is not a terminal. Also note that EOT is surrounded by single-quotes, so that bash recognizes the heredoc as a nowdoc, turning off local variable interpolation so that the command text will be passed as-is to ssh.
If you are a fan of pipes, you can rewrite the above as follows:
cat <<'EOT' | ssh user#server /bin/bash
echo "These commands will be run on: $( uname -a )"
echo "They are executed by: $( whoami )"
EOT
The same caveat about /bin/bash applies to the above.
Another valid approach is to pass the multi-line remote command as a single string, using multiple layers of bash variable interpolation as follows:
ssh user#server "$( cat <<'EOT'
echo "These commands will be run on: $( uname -a )"
echo "They are executed by: $( whoami )"
EOT
)"
The solution above fixes this problem in the following manner:
ssh user#server is parsed by bash, and is interpreted to be the ssh command, followed by an argument user#server to be passed to the ssh command
" begins an interpolated string, which when completed, will comprise an argument to be passed to the ssh command, which in this case will be interpreted by ssh to be the remote command to execute as user#server
$( begins a command to be executed, with the output being captured by the surrounding interpolated string
cat is a command to output the contents of whatever file follows. The output of cat will be passed back into the capturing interpolated string
<< begins a bash heredoc
'EOT' specifies that the name of the heredoc is EOT. The single quotes ' surrounding EOT specifies that the heredoc should be parsed as a nowdoc, which is a special form of heredoc in which the contents do not get interpolated by bash, but rather passed on in literal format
Any content that is encountered between <<'EOT' and <newline>EOT<newline> will be appended to the nowdoc output
EOT terminates the nowdoc, resulting in a nowdoc temporary file being created and passed back to the calling cat command. cat outputs the nowdoc and passes the output back to the capturing interpolated string
) concludes the command to be executed
" concludes the capturing interpolated string. The contents of the interpolated string will be passed back to ssh as a single command line argument, which ssh will interpret as the remote command to execute as user#server
If you need to avoid using external tools like cat, and don't mind having two statements instead of one, use the read built-in with a heredoc to generate the SSH command:
IFS='' read -r -d '' SSH_COMMAND <<'EOT'
echo "These commands will be run on: $( uname -a )"
echo "They are executed by: $( whoami )"
EOT
ssh user#server "${SSH_COMMAND}"
I'm adding this answer because it solved a related problem that I was having with the same error message.
Problem: I had installed cygwin under Windows and was getting this error: Pseudo-terminal will not be allocated because stdin is not a terminal
Resolution: It turns out that I had not installed the openssh client program and utilities. Because of that cygwin was using the Windows implementation of ssh, not the cygwin version. The solution was to install the openssh cygwin package.
All relevant information is in the existing answers, but let me attempt a pragmatic summary:
tl;dr:
DO pass the commands to run using a command-line argument:
ssh jdoe#server '...'
'...' strings can span multiple lines, so you can keep your code readable even without the use of a here-document:
ssh jdoe#server ' ... '
Do NOT pass the commands via stdin, as is the case when you use a here-document:
ssh jdoe#server <<'EOF' # Do NOT do this ... EOF
Passing the commands as an argument works as-is, and:
the problem with the pseudo-terminal will not even arise.
you won't need an exit statement at the end of your commands, because the session will automatically exit after the commands have been processed.
In short: passing commands via stdin is a mechanism that is at odds with ssh's design and causes problems that must then be worked around.
Read on, if you want to know more.
Optional background information:
ssh's mechanism for accepting commands to execute on the target server is a command-line argument: the final operand (non-option argument) accepts a string containing one or more shell commands.
By default, these commands run unattended, in a non-interactive shell, without the use of a (pseudo) terminal (option -T is implied), and the session automatically ends when the last command finishes processing.
In the event that your commands require user interaction, such as responding to an interactive prompt, you can explicitly request the creation of a pty (pseudo-tty), a pseudo terminal, that enables interacting with the remote session, using the -t option; e.g.:
ssh -t jdoe#server 'read -p "Enter something: "; echo "Entered: [$REPLY]"'
Note that the interactive read prompt only works correctly with a pty, so the -t option is needed.
Using a pty has a notable side effect: stdout and stderr are combined and both reported via stdout; in other words: you lose the distinction between regular and error output; e.g.:
ssh jdoe#server 'echo out; echo err >&2' # OK - stdout and stderr separate
ssh -t jdoe#server 'echo out; echo err >&2' # !! stdout + stderr -> stdout
In the absence of this argument, ssh creates an interactive shell - including when you send commands via stdin, which is where the trouble begins:
For an interactive shell, ssh normally allocates a pty (pseudo-terminal) by default, except if its stdin is not connected to a (real) terminal.
Sending commands via stdin means that ssh's stdin is no longer connected to a terminal, so no pty is created, and ssh warns you accordingly:
Pseudo-terminal will not be allocated because stdin is not a terminal.
Even the -t option, whose express purpose is to request creation of a pty, is not enough in this case: you'll get the same warning.
Somewhat curiously, you must then double the -t option to force creation of a pty: ssh -t -t ... or ssh -tt ... shows that you really, really mean it.
Perhaps the rationale for requiring this very deliberate step is that things may not work as expected. For instance, on macOS 10.12, the apparent equivalent of the above command, providing the commands via stdin and using -tt, does not work properly; the session gets stuck after responding to the read prompt:
ssh -tt jdoe#server <<<'read -p "Enter something: "; echo "Entered: [$REPLY]"'
In the unlikely event that the commands you want to pass as an argument make the command line too long for your system (if its length approaches getconf ARG_MAX - see this article), consider copying the code to the remote system in the form of a script first (using, e.g., scp), and then send a command to execute that script.
In a pinch, use -T, and provide the commands via stdin, with a trailing exit command, but note that if you also need interactive features, using -tt in lieu of -T may not work.
The warning message Pseudo-terminal will not be allocated because stdin is not a terminal. is due to the fact that no command is specified for ssh while stdin is redirected from a here document.
Due to the lack of a specified command as an argument ssh first expects an interactive login session (which would require the allocation of a pty on the remote host) but then has to realize that its local stdin is no tty/pty. Redirecting ssh's stdin from a here document normally requires a command (such as /bin/sh) to be specified as an argument to ssh - and in such a case no pty will be allocated on the remote host by default.
Since there are no commands to be executed via ssh that require the presence of a tty/pty (such as vim or top) the -t switch to ssh is superfluous.
Just use ssh -T user#server <<EOT ... or ssh user#server /bin/bash <<EOT ... and the warning will go away.
If <<EOF is not escaped or single-quoted (i. e. <<\EOT or <<'EOT') variables inside the here document will be expanded by the local shell before it is executing ssh .... The effect is that the variables inside the here document will remain empty because they are defined only in the remote shell.
So, if $REL_DIR should be both accessible by the local shell and defined in the remote shell, $REL_DIR has to be defined outside the here document before the ssh command (version 1 below); or, if <<\EOT or <<'EOT' is used, the output of the ssh command can be assigned to REL_DIR if the only output of the ssh command to stdout is genererated by echo "$REL_DIR" inside the escaped/single-quoted here document (version 2 below).
A third option would be to store the here document in a variable and then pass this variable as a command argument to ssh -t user#server "$heredoc" (version 3 below).
And, last but not least, it would be no bad idea to check if the directories on the remote host were created successfully (see: check if file exists on remote host with ssh).
# version 1
unset DEP_ROOT REL_DIR
DEP_ROOT='/tmp'
datestamp=$(date +%Y%m%d%H%M%S)
REL_DIR="${DEP_ROOT}/${datestamp}"
ssh localhost /bin/bash <<EOF
if [ ! -d "$DEP_ROOT" ] && [ ! -e "$DEP_ROOT" ]; then
echo "creating the root directory" 1>&2
mkdir "$DEP_ROOT"
fi
mkdir "$REL_DIR"
#echo "$REL_DIR"
exit
EOF
scp -r ./dir1 user#server:"$REL_DIR"
scp -r ./dir2 user#server:"$REL_DIR"
# version 2
REL_DIR="$(
ssh localhost /bin/bash <<\EOF
DEP_ROOT='/tmp'
datestamp=$(date +%Y%m%d%H%M%S)
REL_DIR="${DEP_ROOT}/${datestamp}"
if [ ! -d "$DEP_ROOT" ] && [ ! -e "$DEP_ROOT" ]; then
echo "creating the root directory" 1>&2
mkdir "$DEP_ROOT"
fi
mkdir "$REL_DIR"
echo "$REL_DIR"
exit
EOF
)"
scp -r ./dir1 user#server:"$REL_DIR"
scp -r ./dir2 user#server:"$REL_DIR"
# version 3
heredoc="$(cat <<'EOF'
# -onlcr: prevent the terminal from converting bare line feeds to carriage return/line feed pairs
stty -echo -onlcr
DEP_ROOT='/tmp'
datestamp="$(date +%Y%m%d%H%M%S)"
REL_DIR="${DEP_ROOT}/${datestamp}"
if [ ! -d "$DEP_ROOT" ] && [ ! -e "$DEP_ROOT" ]; then
echo "creating the root directory" 1>&2
mkdir "$DEP_ROOT"
fi
mkdir "$REL_DIR"
echo "$REL_DIR"
stty echo onlcr
exit
EOF
)"
REL_DIR="$(ssh -t localhost "$heredoc")"
scp -r ./dir1 user#server:"$REL_DIR"
scp -r ./dir2 user#server:"$REL_DIR"
I don't know where the hang comes from, but redirecting (or piping) commands into an interactive ssh is in general a recipe for problems. It is more robust to use the command-to-run-as-a-last-argument style and pass the script on the ssh command line:
ssh user#server 'DEP_ROOT="/home/matthewr/releases"
datestamp=$(date +%Y%m%d%H%M%S)
REL_DIR=$DEP_ROOT"/"$datestamp
if [ ! -d "$DEP_ROOT" ]; then
echo "creating the root directory"
mkdir $DEP_ROOT
fi
mkdir $REL_DIR'
(All in one giant '-delimited multiline command-line argument).
The pseudo-terminal message is because of your -t which asks ssh to try to make the environment it runs on the remote machine look like an actual terminal to the programs that run there. Your ssh client is refusing to do that because its own standard input is not a terminal, so it has no way to pass the special terminal APIs onwards from the remote machine to your actual terminal at the local end.
What were you trying to achieve with -t anyway?
After reading a lot of these answers I thought I would share my resulting solution. All I added is /bin/bash before the heredoc and it doesn't give the error anymore.
Use this:
ssh user#machine /bin/bash <<'ENDSSH'
hostname
ENDSSH
Instead of this (gives error):
ssh user#machine <<'ENDSSH'
hostname
ENDSSH
Or use this:
ssh user#machine /bin/bash < run-command.sh
Instead of this (gives error):
ssh user#machine < run-command.sh
EXTRA:
If you still want a remote interactive prompt e.g. if the script you're running remotely prompts you for a password or other information, because the previous solutions won't allow you to type into the prompts.
ssh -t user#machine "$(<run-command.sh)"
And if you also want to log the entire session in a file logfile.log:
ssh -t user#machine "$(<run-command.sh)" | tee -a logfile.log
I was having the same error under Windows using emacs 24.5.1 to connect to some company servers through /ssh:user#host. What solved my problem was setting the "tramp-default-method" variable to "plink" and whenever I connect to a server I ommit the ssh protocol. You need to have PuTTY's plink.exe installed for this to work.
Solution
M-x customize-variable (and then hit Enter)
tramp-default-method (and then hit Enter again)
On the text field put plink and then Apply and Save the buffer
Whenever I try to access a remote server I now use C-x-f /user#host: and then input the password. The connection is now correctly made under Emacs on Windows to my remote server.

Resources