how can i expect ssh and run shell command? - shell

i want to mkdir at remote machine, but i dont know if the dir exists, how can i do this?
i use spawn ssh username#ip bash -c [ -d $dest_file ] && echo ok || mkdir -p $dest_file
and returns
while executing
"-d $dest_file "
invoked from within
"[ -d $dest_file ] && echo ok || mkdir -p $dest_file"
(file "mkdir.exp" line 22)
i cant use ssh-key because my ip is dynamic

First, you probably don't need bash -c because ssh is already executing the command with your remote shell.
Secondly, you're not sufficiently quoting your ssh arguments. You're writing an expect script, which uses the tcl programming language, and [ is a special character that will attempt to evaluate its contents as a tcl command and return the output (read more here). For this to work properly, you would need to escape the opening [ to get tcl to interpret it literally:
spawn ssh localhost \[ -d $dest_file ] && echo ok || mkdir -p $dest_file
This seems to work correctly on my system, but as I indicate in a comment it would be much easier to drop all the conditionals and just run:
spawn ssh localhost mkdir -p $dest_file
This accomplishes the same thing and doesn't run afoul of any quoting issues.

Related

Passing variables to SSH [duplicate]

This question already has answers here:
Passing external shell script variable via ssh
(2 answers)
Variable issues in SSH
(1 answer)
Closed 1 year ago.
The following code loops through states in a array and passes a state to a server via ssh -
STATES="NY CO"
arr_states=(${STATES//' /'/ })
for i in "${arr_states[#]}"; do
state=$i
ssh -o SendEnv=state jenkins#server sh -s << 'EOF'
sudo su
cd /home/jenkins/report
psql -d db -c "$(sed 's/state_name/'"$state"'/' county.sql)" -U user
echo $state
EOF
done
The output of echo $state in the above is an empty string even if I pass it NY.
When I change the 'EOF' to EOF, the output of echo $state is the string I passed (NY). But then it says, the file county.sql does not exist.
How do I get it to recognize both the variable I pass and the file on the remote I am trying to run.
As an approach that doesn't require you to do any manual escaping of your code (which frequently becomes a maintenance nightmare, since it means that code needs to be changed whenever you modify where it's expected to run) -- consider defining a function, and using declare -f to ask the shell to generate code that will output that function for you.
The same can be done with variables, using declare -p. Thus, passing both a function with the remote code, and the variables that remote code needs to operate that way:
#!/usr/bin/env bash
# This is run on the remote server _as root_ (behind sudo su)
remotePostEscalationFunc() {
cd /home/jenkins/report || return
if psql -d db -U user -c "$(sed -e "s/state_name/${state}/" county.sql)"; then
echo "Success processing $state" >&2
else
rc=$?
echo "Failure processing $state" >&2
return "$rc"
fi
}
# This is run on the remote server as the jenkins user (before sudo).
remoteFunc() {
sudo su -c "$(declare -p state); $(declare -f remotePostEscalationFunc); remotePostEscalationFunc"
}
# Everything below here is run locally.
arr_states=( NY CO )
for state in "${arr_states[#]}"; do
ssh jenkins#server 'bash -s' <<EOF
$(declare -f remoteFunc remotePostEscalationFunc); $(declare -p state); remoteFunc
EOF
done
You were almost right with the change from 'EOF' to EOF. You are just missing a backslash (\) before $(sed. So the following should work:
arr_states=(${STATES//' /'/ })
for i in "${arr_states[#]}"; do
state=$i
ssh -o SendEnv=state jenkins#server sh -s << EOF
sudo su
cd /home/jenkins/report
psql -d db -c "\$(sed 's/state_name/'"$state"'/' county.sql)" -U user
echo $state
EOF
done

Bash Echo passing to another script, not working as expected

I created a bash file to write some content into a file, which should be written into another users home directory, with the users account.
It should work the follwing:
sudo ./USER.sh run 49b087ef9cb6753f "echo test > test.txt"
Basically USER.sh contains this:
if [ "$1" = "run" ]; then
cd /home/${2}/;
sudo -u ${2} ${3};
fi
But it does not write any stuff into test.txt, it just direct executes the Bash command, instead of writing it into the file.
Did anyone got an Idea how I can fix it, that it does actually write the Content into a file instead of direct executing it?
Thanks.
You want:
sudo -u "$2" sh -c "$3"
The curlies are useless. They don't prevent splitting and file-globbing.
The double quotes do.
With the double quotes "$3" expands to "echo test > test.txt" (without them, it's "echo" "test" ">" and "test.txt"). This needs to be executed by a shell, hence the sh -c (a POSIX shell is sufficient in this case and if it's dash, it'll start a few ms faster than bash does).
You could also do:
if [ "$1" = "run" ]; then
sudo -u "$2" --set-home sh -c "$(printf '%s\n' 'cd "$HOME"' "$3")"
fi
which would be more robust in the general case where user home directories aren't necessarily /home/$username, but whatever the appropriate field in /etc/passwd is.

ssh bash receive variable from a remote file

I need to read the variable from a remote file over SSH and compare it. But I get a variable in the wrong format. how to do it correctly?
#!/bin/bash
pass='dpassspass'
user='root#10.10.19.18'
IP="10.2.1.41"
path=/sys/variable/serv
#not work## No such file or directory# write=$(sshpass -p $ovhpass ssh -t $user echo "$IP" > $path)
sshpass -p $pass ssh -t $user << EOF
echo "$IP" > $path
EOF
my_var=$(sshpass -p $pass ssh -t $user "cd /sys_ovh; ./serv.bash")
echo mystart-"$my_var"-myend
read=$(sshpass -p $pass ssh -t $user cat $path)
echo start-"$read"-end
echo start-"$IP"-end
if [ "$read" == "$IP" ]; then
echo "run"
fi
output:
Connection to 10.10.19.18 closed.
-myendt-10.2.1.41
Connection to 10.10.19.18 closed.
-endt-10.2.1.41
start-10.2.1.41-end
Where I make a mistake? How to take data from the SSH?
The vars my_var and read are filled with a string ending with '\r', telling echo to go back to the first column. I think this is a problem with your local script. You can correct that with
tr -d "\r" < myfile > myfile2
Your fundamental problem comes from using unquoted here documents for the commands. You should properly understand in which order the shell interprets these contructs.
ssh remote cmd >file
executes cmd remotely, but first redirects the output from the ssh command to the local file.
ssh remote "cmd >’$file'"
The quotes cause the redirection to be part of the remote command line. The variable file is interpreted first, by the local shell, though.
ssh remote 'cmd >"$file"`
The single quotes prevent the local shell from modifying the command before sending it. Thus, he variable interpolation and the redirection are both handled by the remote shell, in this order.
So your commented-out "not work" command could easily be fixed with proper quoting. However, it will be much more elegant and efficient to use a single remote session, and execute all the commands in one go. Mixing the local variable IP with remote variables calls for some rather elaborate escaping, though. A major simplification would be to pass the value on standard input, so that the entire remote script can be single quoted.
#!/bin/bash
pass='dpassspass'
user='root#10.10.19.18'
IP="10.2.1.41"
result=$(echo "$IP" |
sshpass -p "$pass" ssh -t "$user" '
path=/sys/variable/serv
cat > "$path"
cd /sys_ovh
./serv.bash
cat "$path"')
echo mystart-"${result%$'\n'*}"-myend
echo start-"${result#*$'\n'}"-end
echo start-"$IP"-end
if [ "${result#*$'\n'}" == "$IP" ]; then
echo "run"
fi
The output from the remote shell is two lines; we pick it apart by using the shell's prefix and suffix substitution operators.

Run 'export' command Over SSH

When I run the following from my bash shell:
bash -c '(export abc=123 && echo $abc)'
The output is "123". But when I run it over ssh:
ssh remote-host "bash -c '(export abc=123 && echo $abc)'"
There is no output. Why is this? Is there a way around this? That is, is there a way to set an environment variable for a command I run over ssh?
Note: When I replace echo $abc with something standard like echo $USER the ssh command prints out the username on the remote machine as expected since it is already set.
I am running RHEL 5 Linux with OpenSSH 4.3
That is because when using
ssh remote-host "bash -c '(export abc=123 && echo $abc)'"
the variable gets expanded by the local shell (as it is the case with $USER) before ssh executes. Escape the $ by using \$ and it should do fine
ssh remote-host "bash -c '(export abc=123 && echo \$abc)'"
On a side note:
You don't need to export just for this.
You don't need to wrap it in ()
Like so:
ssh remote-host "bash -c 'abc=123 && echo \$abc'"
Heck, you can even leave out the bash -c ... stuff, as the ssh manpage states:
If command is specified, it is executed on the remote host instead of a login shell.
But these may be specific to your task ;)

using ssh with stat command bash [duplicate]

I'd like to execute several commands in sequence on a remote machine, and some of the later commands depend on earlier ones. In the simplest possible example I get this:
ssh my_server "echo this is my_server; abc=2;"
this is my_server
abc=2: Command not found.
ssh my_server "echo this is my_server; abc=2; echo abc is $abc"
abc: undefined variable
For a bit of background info, what I actually want to do is piece together a path and launch a java application:
ssh my_server 'nohup sh -c "( ( echo this is my_server; jabref_exe=`which jabref`; jabref_dir=`dirname $jabref_exe`; java -jar $jabref_dir/../jabref.jar` $1 &/dev/null ) & )"' &
jabref_dir: Undefined variable.
That way, whenever jabref gets updated to a new version on the server, I won't have to manually update the path to the jar file. The jabref executable doesn't take arguments, but launching it with java -jar does, which is why I have to juggle the path a bit.
At the moment I have the list of commands in a separate script file and call
ssh my_server 'nohup sh -c "( ( my_script.sh &/dev/null ) & )"' &
which works, but since the ssh call is already inside one script file it would be nice to have everything together.
In this example
ssh my_server "echo this is my_server; abc=2;"
abc is set on the remote side, so it should be clear why it is not set on your local machine.
In the next example,
ssh my_server "echo this is my_server; abc=2; echo abc is $abc"
your local shell tries to expand $abc in the argument before it is ever sent to the remote host. A slight modification would work as you expected:
ssh my_server 'echo this is my_server; abc=2; echo abc is $abc'
The single quotes prevent your local shell from trying to expand $abc, and so the literal text makes it to the remote host.
To finally address your real question, try this:
jabref_dir=$( ssh my_server 'jabref_exe=$(which jabref); jabref_dir=$(dirname $jabref_exe);
java -jar $jabref_dir/../jabref.jar > /dev/null; echo $jabref_dir' )
This will run the quoted string as a command on your remote server, and output exactly one string: $jabref_dir. That string is captured and stored in a variable on your local host.
With some inspiration from chepner, I now have a solution that works, but only when called from a bash shell or bash script. It doesn't work from tcsh.
ssh my_server "bash -c 'echo this is \$HOSTNAME; abc=2; echo abc is \$abc;'"
Based on this, the code below is a local script which runs jabref on a remote server (although with X-forwarding by default and passwordless authentication the user can't tell it's remote):
#!/bin/bash
if [ -f "$1" ]
then
fname_start=$(echo ${1:0:4})
if [ "$fname_start" = "/tmp" ]
then
scp $1 my_server:$1
ssh my_server "bash -c 'source load_module jdk; source load_module jabref; java_exe=\$(which java); jabref_exe=\$(which jabref); jabref_dir=\$(echo \${jabref_exe%/bin/jabref});eval \$(java -jar \$jabref_dir/jabref.jar $1)'" &
else
echo input argument must be a file in /tmp.
else
echo this function requires 1 argument
fi
and this is the 1-line script load_module, since modulecmd sets environment variables and I couldn't figure out how to do that without sourcing a script.
eval `/path/to/modulecmd bash load $1`;
I also looked at heredocs, inspired by How to use SSH to run a shell script on a remote machine? and http://tldp.org/LDP/abs/html/here-docs.html. The nice part is that it works even from tcsh. I got this working from the command line, but not inside a script. That's probably easy enough to fix, but I've got a solution now so I'm happy :-)
ssh my_server 'bash -s' << EOF
echo this is \$HOSTNAME; abc=2; echo abc is \$abc;
EOF

Resources