I am using the shell module to execute the following command
tasks:
- name: Command
shell: "sshpass -p 123 ssh -o 'StrictHostKeyChecking no' root#10.67.13.50 shell << EOF \n whoami\nEOF | cat"
I am getting the following error
"stderr_lines": [
"/bin/sh: line 2: warning: here-document at line 0 delimited by end-of-file (wanted `EOF')",
"Warning: Permanently added '10.67.13.50' (ECDSA) to the list of known hosts.",
"bash: shell: command not found"
]
What is wrong with my command?
tl;dr you can either a) replace shell with sh or bash, or b) replace shell with whoami and drop the heredoc.
Let's decompose the shell command:
sshpass -p 123 ssh -o 'StrictHostKeyChecking no' root#10.67.13.50 shell << EOF \n whoami\nEOF | cat
There are several processes happening here.
A sh pipeline with two subprocesses:
sshpass which then runs as a subprocess…
ssh which connects to 10.67.13.50 and runs…
shell with \n whoami\n as its standard input
… and cat, which takes the output from the sshpass process hierarchy
There are a couple potential bugs:
You can safely remove cat from the pipeline.
As #KamilCuk mentioned, cat reads from its input and writes it out. It isn't doing anything here; it's neither useful nor harmless.
shell is not a command on the remote server (10.67.13.50). If you want to run a shell, typically sh or bash is used.
Moreover, you can replace the entire shell … EOF sequence with whoami.
The << EOF \n whoami \nEOF is a heredoc to tell the shell on the remote server what commands to execute. However, there is only one command executed.
In summary, the shell: line could be rewritten as:
sshpass -p 123 ssh -o 'StrictHostKeyChecking no' root#10.67.13.50 whoami
… an odd command, since we know the remote user root.
Related
how can I pass $line to the cmd command?
#!/bin/bash
while read line
do
sshpass -p "..." ssh -o StrictHostKeyChecking=no -tt windows#172....-p 333 cmd /c "cd C:\ & download_give_id.exe '$#$line' "
done <apps.txt
Basically, if you want to interpolate a variable into a bash string you need to use double quotes instead of single quotes:
str="${var} foo bar" # works
str='${var} for bar' # works not
In the special case that you are running ssh commands inside a while loop, I strongly recommend to pass /dev/tty explicitly as input to the command since once the remote command should read from stdin - for whatever reason - it will slurp stdin of the while loop otherwise:
while read line ; do
ssh ... -- "${line} ..." < /dev/tty # Pass tty to stdin
done < input.txt
Note: The above command will work only if the process has been started in a terminal. If the process is not running in a terminal, you need to pass something else as stdin for the inner ssh command.
I need your help in understanding this behaviour of sudo.
sudo -s -- 'ls -l' this command works but sudo 'ls -l' throws error saying
sudo: ls -l: command not found I realize it treats the entire string within quote as single command (including the spaces) but what I don't get is how does it work fine with -s flag but fails when -s is not there.
Without -s, the first argument is the name of the command to execute. With -s, the first argument is a string passed to the -c option of whatever shell ($SHELL or your system shell) is used to execute the argument.
That is, assuming $SHELL is sh, the following are equivalent:
sudo -s -- 'ls -l'
sudo -- sh -c 'ls -l'
From the sudo man page:
-s [command]
The -s (shell) option runs the shell specified by the SHELL environment variable if it is set or the shell as specified in
the password database. If a command is specified, it is passed to the
shell for execution via the shell's -c option. If no command is
specified, an interactive shell is executed.
It behaves like it does because a new shell is spawned which breaks up the words in your "quoted command" like shells do.
I would like to connect to different shells (csh, ksh etc.,) and execute command inside each switched shell.
Following is the sample program which reflects my intention:
#!/bin/bash
echo $SHELL
csh
echo $SHELL
exit
ksh
echo $SHELL
exit
Since, i am not well versed with Shell scripting need a pointer on how to achieve this. Any help would be much appreciated.
If you want to execute only one single command, you can use the -c option
csh -c 'echo $SHELL'
ksh -c 'echo $SHELL'
If you want to execute several commands, or even a whole script in a child-shell, you can use the here-document feature of bash and use the -s (read commands from stdin) on the child shells:
#!/bin/bash
echo "this is bash"
csh -s <<- EOF
echo "here go the commands for csh"
echo "and another one..."
EOF
echo "this is bash again"
ksh -s <<- EOF
echo "and now, we're in ksh"
EOF
Note that you can't easily check the shell you are in by echo $SHELL, because the parent shell expands this variable to the text /././bash. If you want to be sure that the child shell works, you should check if a shell-specific syntax is working or not.
It is possible to use the command line options provided by each shell to run a snippet of code.
For example, for bash use the -c option:
bash -c $code
bash -c 'echo hello'
zsh and fish also use the -c option.
Other shells will state the options they use in their man pages.
You need to use the -c command line option if you want to pass commands on bash startup:
#!/bin/bash
# We are in bash already ...
echo $SHELL
csh -c 'echo $SHELL'
ksh -c 'echo $SHELL'
You can pass arbitrary complex scripts to a shell, using the -c option, as in
sh -c 'echo This is the Bourne shell.'
You will save you a lot of headaches related to quotes and variable expansion if you wrap the call in a function reading the script on stdin as:
execute_with_ksh()
{
local script
script=$(cat)
ksh -c "${script}"
}
prepare_complicated_script()
{
# Write shell script on stdout,
# for instance by cat-ting a here-document.
cat <<'EOF'
echo ${SHELL}
EOF
}
prepare_complicated_script | execute_with_ksh
The advantage of this method is that it easy to insert a tee in the pipe or to break the pipe to control the script being passed to the shell.
If you want to execute the script on a remote host through ssh you should consider encode your script in base 64 to transmit it safely to the remote shell.
I have bash script (for example):
ssh -t -t user#domain.com << EOF
cd /home/admin
mkdir test
echo 'Some text'
exit
EOF
Can I display only "echo" command in terminal? It is possible?
Now all commands are displayed.
Thank you
Specifying the commands on standard input with ssh -t causes the commands to be echoed back, but you don't have to do that.
ssh -t user#domain.com "
cd /home/admin
mkdir test
echo 'Some text'"
(The exit isn't really required or useful, so I left it out.)
Use single quotes if you want to prevent the local shell from interpolating variables etc in the string containing the commands.
To selectively display an individual command as well as its output, you can use something like
sh -vc 'echo \"Some text\"'
although the nested quoting can start getting on your nerves pretty quickly.
I have a shell script that I am using to compare directory contents. The script has to ssh to different servers to get a directory listing. When I run the script below, I am getting the contents of the server that I am logged into's /tmp directory listing and not that of the servers I am trying to ssh to. Could you please tell me what I am doing wrong?
The config file used in the script is as follows (called config.txt):
server1,server2,/tmp
The script is as follows
#!/bin/sh
CONFIGFILE="config.txt"
IFS=","
while read a b c
do
SERVER1=$a
SERVER2=$b
COMPDIR=$c
`ssh user#$SERVER1 'ls -l $COMPDIR'`| sed -n '1!p' >> server1.txt
`ssh user#$SERVER2 'ls -l $COMPDIR'`| sed -n '1!p' >> server2.txt
done < $CONFIGFILE
When I look at the outputs of server1.txt and server2.txt, they are both exactly the same - having the contents of /tmp of the server the script is running on (not server1 or 2). Doing the ssh +dir listing on command line works just fine. I am also getting the error "Pseudo-terminal will not be allocated because stdin is not a terminal". Adding the -t -t to the ssh command isnt helping either
Thank you
I have the back ticks in order to execute the command.
Backticks are not needed to execute a command - they are used to expand the standard output of the command into the command line. Certainly you don't want the output of your ssh commands to be interpreted as commands. Thus, it should work fine without the backticks:
ssh user#$SERVER1 "ls -l $COMPDIR" | sed -n '1!p' >>server1.txt
ssh user#$SERVER2 "ls -l $COMPDIR" | sed -n '1!p' >>server2.txt
(provided that double quotes to allow expansion of $COMPDIR are used).
first you need to generate keys to login to remote without keys
ssh-keygen -t rsa
ssh-copy-id -i ~/.ssh/id_rsa.pub remote-host
then try to ssh without pass
ssh remote-host
then try to invoke in your script but first make sanity check
var1=$(ssh remote-host) die "Cannot connect to remote host" unless $var1;