I'm trying to break down the following plink command and modify it to allow a variable substitution.
plink.exe 192.168.1.1 --% echo """#define MYSPACE""" > /home/my/file.txt
I want to do the following, but But what follows the echo is sent to bash verbatim.
plink.exe 192.168.1.1 --% echo """#define MYSPACE""" > /$(home)/my/file.txt
Constructing the echo string before the plink command doesn't work for the same reason; everything after echo is sent verbatim. How can this be done?
On the PowerShell side, if home is a defined environment variable, that will still be expanded after the --%. So,
plink.exe 192.168.1.1 --% echo """#define MYSPACE""" > /%home%/my/file.txt
For variable expansion on the bash side, your variable substitution syntax is slightly off.
Most shells want the variable name surrounded by curly braces - { }. But it looks like you are using parentheses. In bash, that $(...) construct will try to run a subshell, and you'll get very odd errors.
So, try
plink.exe 192.168.1.1 --% echo """#define MYSPACE""" > /${home}/my/file.txt
Related
I'm trying to interpolate variables inside of a bash heredoc:
var=$1
sudo tee "/path/to/outfile" > /dev/null << "EOF"
Some text that contains my $var
EOF
This isn't working as I'd expect ($var is treated literally, not expanded).
I need to use sudo tee because creating the file requires sudo. Doing something like:
sudo cat > /path/to/outfile <<EOT
my text...
EOT
Doesn't work, because >outfile opens the file in the current shell, which is not using sudo.
In answer to your first question, there's no parameter substitution because you've put the delimiter in quotes - the bash manual says:
The format of here-documents is:
<<[-]word
here-document
delimiter
No parameter expansion, command substitution, arithmetic expansion, or
pathname expansion is performed on word. If any characters in word are
quoted, the delimiter is the result of quote removal on word, and the
lines in the here-document are not expanded. If word is unquoted, all
lines of the here-document are subjected to parameter expansion, command substitution, and arithmetic expansion. [...]
If you change your first example to use <<EOF instead of << "EOF" you'll find that it works.
In your second example, the shell invokes sudo only with the parameter cat, and the redirection applies to the output of sudo cat as the original user. It'll work if you try:
sudo sh -c "cat > /path/to/outfile" <<EOT
my text...
EOT
Don't use quotes with <<EOF:
var=$1
sudo tee "/path/to/outfile" > /dev/null <<EOF
Some text that contains my $var
EOF
Variable expansion is the default behavior inside of here-docs. You disable that behavior by quoting the label (with single or double quotes).
As a late corolloary to the earlier answers here, you probably end up in situations where you want some but not all variables to be interpolated. You can solve that by using backslashes to escape dollar signs and backticks; or you can put the static text in a variable.
Name='Rich Ba$tard'
dough='$$$dollars$$$'
cat <<____HERE
$Name, you can win a lot of $dough this week!
Notice that \`backticks' need escaping if you want
literal text, not `pwd`, just like in variables like
\$HOME (current value: $HOME)
____HERE
Demo: https://ideone.com/rMF2XA
Note that any of the quoting mechanisms -- \____HERE or "____HERE" or '____HERE' -- will disable all variable interpolation, and turn the here-document into a piece of literal text.
A common task is to combine local variables with script which should be evaluated by a different shell, programming language, or remote host.
local=$(uname)
ssh -t remote <<:
echo "$local is the value from the host which ran the ssh command"
# Prevent here doc from expanding locally; remote won't see backslash
remote=\$(uname)
# Same here
echo "\$remote is the value from the host we ssh:ed to"
:
I want to be able to execute remote command in bash instead of default zsh on remote machine (preferable without having to modify settings on remote machine)
Example:
ssh -t -t some-command-that-only-works-in-bash-to-execute-remotely
You can do this :
ssh user#host "bash -c \"some-command-that-only-works-in-bash-to-execute-remotely\""
Please be careful with quoting, however. The arguments to your ssh command will first undergo local expansions and word splitting, then be passed to ssh, to then be submitted to the remote shell and undergo a second round of (remote) expansion and word splitting.
For instance, this will echo the local value of LOGNAME :
ssh user#host "bash -c \"echo $LOGNAME\""
This will echo the remote value of LOGNAME :
ssh user#host "bash -c \"echo \$LOGNAME\""
ssh user#host 'bash -c "echo $LOGNAME"'
If you want to understand why, try replacing ssh with echo and see what command the remote end would receive.
You can also do this :
echo "some-command-that-only-works-in-bash" | ssh user#host bash
ssh user#host bash < <(echo "some-command-that-only-works-in-bash")
You could issue multiple commands with this method (one line each) and they would all be executed remotely. Piping the output of a function designed to issue several commands is useful once in a while, as is redirecting a local script so that it can be executed on the remote machine without having to be copied.
As mentioned in previous comments, here-document can be utilized as such:
me#mycomp ~ $ ssh me#127.0.0.1 "/bin/sh <<%
echo 'user\np#55word!' > ~/cf1
%"
me#127.0.0.1's password:
me#mycomp ~ $ cat ~/cf1
user
p#55word!
From the man page
The following redirection is often
called a “here-document”.
[n]<< delimiter
here-doc-text ...
delimiter
All the text on successive lines up
to the delimiter is saved away and
made available to the command on
standard input, or file descriptor n
if it is specified. If the delimiter
as specified on the initial line is
quoted, then the here-doc-text is
treated literally, otherwise the text
is subjected to parameter expansion,
command substitution, and arithmetic
expansion (as described in the sec‐
tion on “Expansions”). If the opera‐
tor is “<<-” instead of “<<”, then
leading tabs in the here-doc-text are
stripped.
I have a command like this:
ssh user#hostname 'sed -e "s|foo|${bar}|" /home/data/base_out.sql > /home/data/out.sql'
The sed command is working in local shell. But it is not expanding the variable over ssh command. Thanks!
The rule is that within single quotes, parameters are not expanded. You have single quotes around the entire command.
Try this:
ssh user#hostname "sed -e 's|foo|$bar|' /home/data/base_out.sql > /home/data/out.sql"
Now $bar is expanded before the command string is passed as an argument to ssh, which is what you want.
I removed the curly braces around ${bar} because I believe they offer a false sense of security. In this case, they are not protecting you against any of the issues associated using shell variables in sed commands.
I am trying to create and use variables inside heredoc like this,
#!bin/bash
sudo su - postgres <<EOF
IP="XYZ"
echo "$IP"
EOF
This doesn't work right and I get a blank line as echo.
But if I use quotes around EOF like this,
#!bin/bash
sudo su - postgres <<"EOF"
IP="XYZ"
echo "$IP"
EOF
It works. Can someone please explain this? According to what I read in man the behaviour should be opposite.
The shell evaluates the unquoted here document and performs variable interpolation before passing it to the command (in your case, sudo). Because IP is not a defined variable in the parent shell, it gets expanded to an empty string.
With quotes, you prevent variable interpolation by the parent shell, and so the shell run by sudo sees and expands the variable.
i am trying to do this from a Windows command prompt.
C:\cygwin64\bin\bash --login -c "$var="<hallo>" &&
echo "$var""
and i get error :
The system cannot find the file specified.
but this works:
C:\cygwin64\bin\bash --login -c
"var="hello" && echo "$hello""
The login shell seems to cause the problem when it gets a '<'. how can i still assign the string with angle brackets to the shell variable?
When you write
C:\cygwin64\bin\bash --login -c "$var="<hallo>" && echo "$var""
You are expecting the shell to strip off the outer quotes from that argument to -c and end up with a string that looks like
$var="<hallo>" && echo "$var"
but that's not what the shell does.
The shell just matches quotes as it goes along. So the shell sees.
["$var="][<hallo>][" && echo "][$var][""].
You need to escape the inner quotes from the current shell or use different quotes to avoid this parsing problem.
C:\cygwin64\bin\bash --login -c 'var="<hallo>" && echo "$var"'
Note also that I removed the $ from the start of the variable name in the assignment and that I used single quotes on the outside so that the current shell didn't expand $var.
With double quotes on the outside you'd need to use something like this instead.
C:\cygwin64\bin\bash --login -c "var='<hallo>' && echo \"\$var\""
For a similar discussion of shell parsing and how things nest (or don't) with backticks you can see my answer here.