there is a problem with the invoked via ssh bash, although i have read mans about it i still can't explain the following:
Here is a script, very simple
#!/bin/bash
theUser=$1
theHost=$2
ssh -tt $theUser#$theHost 'bash' << EOF
a=1
echo 'dat '$a
exit
EOF
and here is the result:
victor#moria:~$ bash thelast.sh victor 10.0.0.8
victor#10.0.0.8's password:
a=1
echo 'dat '
exit
victor#mordor:~$ a=1
victor#mordor:~$ echo 'dat '
dat
victor#mordor:~$ exit
exit
Connection to 10.0.0.8 closed.
As you may see, the environment doesn't store the value of the variable "a" so it can't echo it, but any other commands like ls or date return the result.
So the question is what i am doing wrong and how to avoid such behavior?
p.s. i can't replace ssh -tt, but any other command may be freely replaced.
Thanks in advance
Inside the here document, the $a is expanded locally before feeding the input to the ssh command. You can prevent that by quoting the terminator after the << operator as in
ssh -tt $theUser#$theHost 'bash' << 'EOF'
$a is being expanded in the local shell, where it is undefined. In order to prevent this from happening, you should escape it:
echo "dat \$a"
Escaping the $ causes it to be passed literally to the remote shell, rather than being interpreted as an expansion locally. I have also added some double quotes, as it is good practice to enclose parameter expansions inside them.
Related
I'm trying to run a commands remotely by doing SSH but variable which is defined inside EOF is not getting picked up.
Variable $BASE_PATH is not getting called inside another variable which i'm defining by name FOLDER_NAME . Mentioned inside script too.
I couldn't use EOF in single quotes ('EOF') because i have to use variable from exiting shell too.
#!/usr/bin/ksh
FILE_NAME=$2
jvm_list=$1
for jvm in `echo $jvm_list`
do
ssh $jvm << EOF
echo FILE_NAME=${FILE_NAME}
export BASE_PATH="\${WEB_DOMAIN_HOME}/Server";
echo BASE_PATH=\${BASE_PATH}; ##Value of BASE_PATH is getting picked up##
export FOLDER_NAME=\`ls -1d $"{BASE_PATH}"/properties* | grep -i -v old\` ##Value of BASE_PATH is coming blank here##
echo $FOLDER_NAME
EOF
done
The construct started with << EOF acts like double-quoted string, so dollar signs are special within it, unless escaped.
Here, you escaped the $, so the shell eventually started by ssh evaluates that variable
echo BASE_PATH=\${BASE_PATH}; ##Value of BASE_PATH is getting picked up##
Here, you didn't escape it, so the evaluation happens in the outer shell:
export FOLDER_NAME=\`ls -1d $"{BASE_PATH}"/properties* ...\`
I do also suspect that the quotes there are misplaced. In Bash $".." is a locale-specific translation, and it seems to be the same in ksh. You probably don't have a translation for that, so the string should come back as is: {BASE_PATH}.
Somewhat related to this may be the backticks, since they need to be quoted too. You could use the $( ... ) form of command substitution, so you'd again, only need to think about the $.
I think this may be what you want:
ssh "$jvm" << EOF
export FOLDER_NAME=\$(ls -1d "\${BASE_PATH}"/properties* | grep -i -v old)
EOF
Sanity check:
$ foo=out; bash <<EOF
> foo=in; echo $foo
> EOF
out
$ foo=out; bash <<EOF
> foo=in; echo \$foo
> EOF
in
I am using the below command on the local machine and it gives me the expected result:
sed -n 's/^fname\(.*\)".*/\1/p' file.txt
When I use the same command(only changed ' to ") to a same file present in the remote system, I do not get any output.
ssh remote-system "sed -n "s/^fname\(.*\)".*/\1/p" file.txt"
Please help me to get this corrected. Thanks for your help.
" and ' are different things in bash, and they are not interchangeable (they're not interchangeable in many languages, however the differences are more subtle) The single quote means 'pretend everything inside here is a string'. The only thing that will be interpreted is the next single quote.
The double quote allows bash to interpret stuff inside
For example,
echo "$TERM"
and
echo '$TERM'
return different things.
(Untested) you should be able to use single quotes and escape the internal single quotes :
ssh remote-system 'sed -n \'s/^fname(.)"./\1/p\' file.txt'
Looks like you can send a single quote with the sequence '"'"' (from this question)
so :
ssh remote-machine 'sed -n '"'"'s/^fname\(.*\)".*/\1/p'"'"' file.txt'
This runs on my machine if I ssh into localhost, there's no output because file.txt is empty, but it's a proof-of-concept.
Or - can you do the ssh session interactively/with a heredoc?
ssh remote-system
[sed command]
exit
or (again untested, look up heredocs for more info)
ssh remote-system <<-EOF
[sed command]
EOF
Trying to run commands defined in variables inside a for loop:
somevar="Bit of text"
cmd1="command \"search '$somevar' here\""
cmd2="command \"search '$somevar' there\""
for cmd in cmd1 cmd2 ; do
eval \$$cmd
ssh server1 eval \$$cmd
done
I've put in the variations I have to consider such as the ssh inside the loop etc as these are needed in my script. I think the eval is the right direction, but the way that the quotes inside the command get interpreted comes through wrong.
Consider this broken example:
$ cmd1="touch \"file with spaces\""
$ $cmd1
Quoting is handled before $cmd1 is expanded, so instead of one file this will create three files called "file, with, and spaces". One can use eval $cmd to force quote removal after the expansion.
Even though it uses eval, the line eval \$$cmd has that same quoting problem since \$$cmd expands to $cmd1, which is then evaluated by eval with the same behaviour as the broken example.
The argument to eval must be the actual command, not the expression $cmd1. This can be done using variable indirection: eval "${!cmd}".
When running this through SSH there is no need for the eval because the remote shell also performs quote removal.
So here is the fixed loop:
for cmd in cmd1 cmd2 ; do
eval "${!cmd}"
ssh server1 "${!cmd}"
done
An alternative to indirection is to iterate over the values of cmd1 and cmd2 instead of their names:
for cmd in "$cmd1" "$cmd2" ; do
eval "$cmd"
ssh server1 "$cmd"
done
I see two solutions, either you change your loop to:
for cmd in "$cmd1" "$cmd2" ; do
ssh server1 $cmd
done
or to:
for cmd in cmd1 cmd2 ; do
ssh server1 ${!cmd}
done
Instead of eval \$$cmd you need to use:
res=$(eval "$cmd")
ssh server1 "$res"
This is what I am trying to do...
#!/bin/bash
array_local=(1 2 3 4 5)
ssh user#server << EOF
index_remote=1
echo \$index_remote
echo \${array_local[\$index_remote]}
EOF
When I try to run the above script I get the O/P as 1 and a null value (blank space). I wanted ${array_local[$index_remote} value to be 2 instead of null, I need to access this local array using remote variable for my further work in the script..
<<EOF results variable expansion happening on the local machine, but you only defined the variable i on the remote machine. You need to think carefully about where you want to do the expansion. You haven't explained in your question whether the value of i is defined client-side or server-side, but I'm guessing from your subsequent comments that you want it done server-side. In that case you'll need to pass the array contents over ssh, which requires careful quoting:
ssh hostname#server <<EOF
i=1
eval `typeset -p array_local`
echo \${array_local[\$i]}
EOF
typeset -p array_local will output the string
declare -a array_local='([0]="1" [1]="2" [2]="3" [3]="4" [4]="5")'
Since this is inside backticks, it will get expanded client-side within the EOF-delimited heredoc, and then evaluated server-side by the eval. In other words it's equivalent to:
ssh hostname#server <<'EOF'
i=1
declare -a array_local='([0]="1" [1]="2" [2]="3" [3]="4" [4]="5")'
echo ${array_local[$i]}
EOF
Notice the difference in EOF quoting between the two examples. The first one allows parameter and shell expansion, and the second doesn't. That's why the echo line in the first one needs quoting, to ensure that parameter expansion happens server-side not client-side.
I'm writing a bash script that is intended to execute some command, and depending on some flag, this command should be either executed locally or remotely. This command's output should be redirected to some file, and this file should be on the box that executes the command, that is, on the remote box if the command is executed remotely.
I'm trying things like
#!/bin/bash
REMOTE=1
function f
{
CMD="$#"
if [ "${REMOTE}" == "1" ]
then
ssh some_host "$CMD"
else
$CMD
fi
}
# This executes "echo huhu" remotely and redirects the output into "out" on the remote box.
REMOTE=1 f echo huhu \> out
# This executes "echo haha > out" remotely (without redirection).
REMOTE=0 f echo haha \> out
When I don't escape the > sign, any output of f is redirected to "out" on the local box, of course.
How could I avoid this behavior?
Don't use eval; use arrays instead. And a solution for the SSH command.
Write eval $CMD instead of $CMD. When $CMD is expanded the interpretation of redirection has already happened and redirections operations will simply passed as ordinary arguments.