Skip to main content
added 46 characters in body
Source Link
phemmer
  • 73.9k
  • 21
  • 199
  • 231

The first thing to understand in this process is how ssh handles its arguments. I don't mean the arguments to the thing you're trying to run, but arguments of ssh. When you invoke ssh, the arguments after the remote host specification (user@server) are concatenated together, and passed through the shell on the remote end. This is important to note, as just because your arguments are properly split on the local side, does not mean they will be properly split on the remote side.

To use your example:

ssh user@server 'echo "hello $1"' sh "world"

These arguments get concatenated as the command:

echo "hello $1" sh world

This is why you get

hello  sh world

The double space between hello and sh is because that's where $1 was supposed to go, but there is no $1.

 

As another example, without the $1, is:

ssh user@server echo "foo      bar" baz

Which results in the output:

foo bar baz

This is because the arguments are being concatenated together, so you end up with the command:

echo foo      bar baz

 

Since there is no way to get around the command being passed through a shell, you just have to ensure that what you passed can survive the shell evaluation. The way I usually accomplish this is with printf "%q "

For example:

cmd=(echo "foo      bar" baz)
ssh user@server "$(printf "%q " "${cmd[@]}")"

Which results in the output:

foo      bar baz

While it's cleaner and easier to understand with cmd being a separate var, it's not required. The following works just the same:

ssh user@server "$(printf "%q " echo "foo      bar" baz)"

 

This also works fine with your shell argument example:

cmd=(sh -c 'echo 1="<$1>" 2="<$2>" 3="<$3>"' sh "my arg1" "my arg2" "my arg3")
ssh user@server "$(printf "%q " "${cmd[@]}")"

Which results in the output:

1=<my arg1> 2=<my arg2> 3=<my arg3>

 


As an alternative solution, you can pass your command as a complete shell script. For example:

ssh user@server <<'EOF'
sh -c 'echo 1="<$1>" 2="<$2>" 3="<$3>"' sh "my arg1" "my arg2" "my arg3"
EOF

There are drawbacks to this solution though as it's harder to do programmatically (generating the doc to pass on STDIN). Also because you're using STDIN, if you want the script on the remote side to read STDIN, you can't (at least not without some trickery).

The first thing to understand in this process is how ssh handles its arguments. I don't mean the arguments to the thing you're trying to run, but arguments of ssh. When you invoke ssh, the arguments after the remote host specification (user@server) are concatenated together, and passed through the shell on the remote end. This is important to note, as just because your arguments are properly split on the local side, does not mean they will be properly split on the remote side.

To use your example:

ssh user@server 'echo "hello $1"' sh "world"

These arguments get concatenated as:

echo "hello $1" sh world

This is why you get

hello  sh world

The double space between hello and sh is because that's where $1 was supposed to go, but there is no $1.

 

As another example, without the $1, is:

ssh user@server echo "foo      bar" baz

Which results in:

foo bar baz

This is because the arguments are being concatenated together, so you end up with:

echo foo      bar baz

 

Since there is no way to get around the command being passed through a shell, you just have to ensure that what you passed can survive the shell evaluation. The way I usually accomplish this is with printf "%q "

For example:

cmd=(echo "foo      bar" baz)
ssh user@server "$(printf "%q " "${cmd[@]}")"

Which results in:

foo      bar baz

While it's cleaner and easier to understand with cmd being a separate var, it's not required. The following works just the same:

ssh user@server "$(printf "%q " echo "foo      bar" baz)"

 

This also works fine with your shell argument example

cmd=(sh -c 'echo 1="<$1>" 2="<$2>" 3="<$3>"' sh "my arg1" "my arg2" "my arg3")
ssh user@server "$(printf "%q " "${cmd[@]}")"

Which results in:

1=<my arg1> 2=<my arg2> 3=<my arg3>

 


As an alternative solution, you can pass your command as a complete shell script. For example:

ssh user@server <<'EOF'
sh -c 'echo 1="<$1>" 2="<$2>" 3="<$3>"' sh "my arg1" "my arg2" "my arg3"
EOF

There are drawbacks to this solution though as it's harder to do programmatically (generating the doc to pass on STDIN). Also because you're using STDIN, if you want the script on the remote side to read STDIN, you can't (at least not without some trickery).

The first thing to understand in this process is how ssh handles its arguments. I don't mean the arguments to the thing you're trying to run, but arguments of ssh. When you invoke ssh, the arguments after the remote host specification (user@server) are concatenated together, and passed through the shell on the remote end. This is important to note, as just because your arguments are properly split on the local side, does not mean they will be properly split on the remote side.

To use your example:

ssh user@server 'echo "hello $1"' sh "world"

These arguments get concatenated as the command:

echo "hello $1" sh world

This is why you get

hello  sh world

The double space between hello and sh is because that's where $1 was supposed to go, but there is no $1.

 

As another example, without the $1, is:

ssh user@server echo "foo      bar" baz

Which results in the output:

foo bar baz

This is because the arguments are being concatenated together, so you end up with the command:

echo foo      bar baz

 

Since there is no way to get around the command being passed through a shell, you just have to ensure that what you passed can survive the shell evaluation. The way I usually accomplish this is with printf "%q "

For example:

cmd=(echo "foo      bar" baz)
ssh user@server "$(printf "%q " "${cmd[@]}")"

Which results in the output:

foo      bar baz

While it's cleaner and easier to understand with cmd being a separate var, it's not required. The following works just the same:

ssh user@server "$(printf "%q " echo "foo      bar" baz)"

 

This also works fine with your shell argument example:

cmd=(sh -c 'echo 1="<$1>" 2="<$2>" 3="<$3>"' sh "my arg1" "my arg2" "my arg3")
ssh user@server "$(printf "%q " "${cmd[@]}")"

Which results in the output:

1=<my arg1> 2=<my arg2> 3=<my arg3>

 


As an alternative solution, you can pass your command as a complete shell script. For example:

ssh user@server <<'EOF'
sh -c 'echo 1="<$1>" 2="<$2>" 3="<$3>"' sh "my arg1" "my arg2" "my arg3"
EOF

There are drawbacks to this solution though as it's harder to do programmatically (generating the doc to pass on STDIN). Also because you're using STDIN, if you want the script on the remote side to read STDIN, you can't (at least not without some trickery).

added 270 characters in body
Source Link
phemmer
  • 73.9k
  • 21
  • 199
  • 231

The first thing to understand in this process is how ssh handles its arguments. I don't mean the arguments to the thing you're trying to run, but arguments of ssh. When you invoke ssh, the arguments after the remote host specification (user@server) are concatenated together, and passed through the shell on the remote end. This is important to note, as just because your arguments are properly split on the local side, does not mean they will be properly split on the remote side.

To use your example:

ssh user@server 'echo "hello $1"' sh "world"

These arguments get concatenated as:

echo "hello $1" sh world

This is why you get

hello  sh world

The double space between hello and sh is because that's where $1 was supposed to go, but there is no $1.

 

As another example, without the $1, is:

ssh user@server echo "foo      bar" baz

Which results in:

foo bar baz

This is because the arguments are being concatenated together, so you end up with:

echo foo      bar baz

 

Since there is no way to get around the command being passed through a shell, you just have to ensure that what you passed can survive the shell evaluation. The way I usually accomplish this is with printf "%q""%q "

For example:

cmd=(echo "foo      bar" baz)
ssh user@server "$(printf "%q " "${cmd[@]}")"

Which results in:

foo      bar baz

While it's cleaner and easier to understand with cmd being a separate var, it's not required. The following works just the same:

ssh user@server "$(printf "%q " echo "foo      bar" baz)"

 

This also works fine with your shell argument example

cmd=(sh -c 'echo 1="<$1>" 2="<$2>" 3="<$3>"' sh "my arg1" "my arg2" "my arg3")
ssh user@server "$(printf "%q " "${cmd[@]}")"

Which results in:

1=<my arg1> 2=<my arg2> 3=<my arg3>

 


As an alternative solution, you can pass your command as a complete shell script. For example:

ssh user@server <<'EOF'
sh -c 'echo 1="<$1>" 2="<$2>" 3="<$3>"' sh "my arg1" "my arg2" "my arg3"
EOF

sh -c 'echo 1="<$1>" 2="<$2>" 3="<$3>"' sh "my arg1" "my arg2" "my arg3" EOF

There are drawbacks to this solution though as it's harder to do programmatically (generating the doc to pass on STDIN). Also because you're using STDIN, if you want the script on the remote side to read STDIN, you can't (at least not without some trickery).

The first thing to understand in this process is how ssh handles its arguments. I don't mean the arguments to the thing you're trying to run, but arguments of ssh. When you invoke ssh, the arguments after the remote host specification (user@server) are concatenated together, and passed through the shell on the remote end. This is important to note, as just because your arguments are properly split on the local side, does not mean they will be properly split on the remote side.

To use your example:

ssh user@server 'echo "hello $1"' sh "world"

These arguments get concatenated as:

echo "hello $1" sh world

This is why you get

hello  sh world

The double space between hello and sh is because that's where $1 was supposed to go, but there is no $1.

 

As another example, without the $1, is:

ssh user@server echo "foo      bar" baz

Which results in:

foo bar baz

This is because the arguments are being concatenated together, so you end up with:

echo foo      bar baz

 

Since there is no way to get around the command being passed through a shell, you just have to ensure that what you passed can survive the shell evaluation. The way I usually accomplish this is with printf "%q"

For example:

cmd=(echo "foo      bar" baz)
ssh user@server "$(printf "%q " "${cmd[@]}")"

Which results in:

foo      bar baz

While it's cleaner and easier to understand with cmd being a separate var, it's not required. The following works just the same:

ssh user@server "$(printf "%q " echo "foo      bar" baz)"

 

This also works fine with your shell argument example

cmd=(sh -c 'echo 1="<$1>" 2="<$2>" 3="<$3>"' sh "my arg1" "my arg2" "my arg3")
ssh user@server "$(printf "%q " "${cmd[@]}")"

Which results in:

1=<my arg1> 2=<my arg2> 3=<my arg3>

 


As an alternative solution, you can pass your command as a complete shell script. For example:

ssh user@server <<'EOF'

sh -c 'echo 1="<$1>" 2="<$2>" 3="<$3>"' sh "my arg1" "my arg2" "my arg3" EOF

There are drawbacks to this solution though as it's harder to do programmatically (generating the doc to pass on STDIN). Also because you're using STDIN, if you want the script on the remote side to read STDIN, you can't (at least not without some trickery).

The first thing to understand in this process is how ssh handles its arguments. I don't mean the arguments to the thing you're trying to run, but arguments of ssh. When you invoke ssh, the arguments after the remote host specification (user@server) are concatenated together, and passed through the shell on the remote end. This is important to note, as just because your arguments are properly split on the local side, does not mean they will be properly split on the remote side.

To use your example:

ssh user@server 'echo "hello $1"' sh "world"

These arguments get concatenated as:

echo "hello $1" sh world

This is why you get

hello  sh world

The double space between hello and sh is because that's where $1 was supposed to go, but there is no $1.

 

As another example, without the $1, is:

ssh user@server echo "foo      bar" baz

Which results in:

foo bar baz

This is because the arguments are being concatenated together, so you end up with:

echo foo      bar baz

 

Since there is no way to get around the command being passed through a shell, you just have to ensure that what you passed can survive the shell evaluation. The way I usually accomplish this is with printf "%q "

For example:

cmd=(echo "foo      bar" baz)
ssh user@server "$(printf "%q " "${cmd[@]}")"

Which results in:

foo      bar baz

While it's cleaner and easier to understand with cmd being a separate var, it's not required. The following works just the same:

ssh user@server "$(printf "%q " echo "foo      bar" baz)"

 

This also works fine with your shell argument example

cmd=(sh -c 'echo 1="<$1>" 2="<$2>" 3="<$3>"' sh "my arg1" "my arg2" "my arg3")
ssh user@server "$(printf "%q " "${cmd[@]}")"

Which results in:

1=<my arg1> 2=<my arg2> 3=<my arg3>

 


As an alternative solution, you can pass your command as a complete shell script. For example:

ssh user@server <<'EOF'
sh -c 'echo 1="<$1>" 2="<$2>" 3="<$3>"' sh "my arg1" "my arg2" "my arg3"
EOF

There are drawbacks to this solution though as it's harder to do programmatically (generating the doc to pass on STDIN). Also because you're using STDIN, if you want the script on the remote side to read STDIN, you can't (at least not without some trickery).

added 270 characters in body
Source Link
phemmer
  • 73.9k
  • 21
  • 199
  • 231

The first thing to understand in this process is how ssh handles its arguments. I don't mean the arguments to the thing you're trying to run, but arguments of ssh. When you invoke ssh, the arguments after the remote host specification (user@server,) are concatenated together, and passed through the shell on the remote end. This is important to note, as just because your arguments are properly split on the local side, does not mean they will be properly split on the remote side.

To use your example:

ssh user@server 'echo "hello $1"' sh "world"

These arguments get concatenated as:

echo "hello $1" sh world

This is why you get

hello  sh world

The double space between hello and sh is because that's where $1 was supposed to go, but there is no $1.

 

As another example, without the $1, is:

ssh user@server echo "foo      bar" baz

Which results in:

foo bar baz

This is because the arguments are being concatenated together, so you end up with:

echo foo      bar baz

 

Since there is no way to get around the command being passed through a shell, you just have to ensure that what you passed can survive the shell evaluation. The way I usually accomplish this is with printf "%q"

For example:

cmd=(echo "foo      bar" baz)
ssh user@server "$(printf "%q " "${cmd[@]}")"

Which results in:

foo      bar baz

While it's cleaner and easier to understand with cmd being a separate var, it's not required. The following works just the same:

ssh user@server "$(printf "%q " echo "foo      bar" baz)"

 

This also works fine with your shell argument example

cmd=(sh -c 'echo 1="<$1>" 2="<$2>" 3="<$3>"' sh "my arg1" "my arg2" "my arg3")
ssh user@server "$(printf "%q " "${cmd[@]}")"

Which results in:

1=<my arg1> 2=<my arg2> 3=<my arg3>

 


As an alternative solution, you can pass your command as a complete shell script. For example:

ssh user@server <<'EOF'

sh -c 'echo 1="<$1>" 2="<$2>" 3="<$3>"' sh "my arg1" "my arg2" "my arg3" EOF

There are drawbacks to this solution though as it's harder to do programmatically (generating the doc to pass on STDIN). Also because you're using STDIN, if you want the script on the remote side to read STDIN, you can't (at least not without some trickery).

The first thing to understand in this process is how ssh handles its arguments. I don't mean the arguments to the thing you're trying to run, but arguments of ssh. When you invoke ssh, the arguments after the remote host specification user@server, are concatenated together, and passed through the shell on the remote end. This is important to note, as just because your arguments are properly split on the local side, does not mean they will be properly split on the remote side.

To use your example:

ssh user@server 'echo "hello $1"' sh "world"

These arguments get concatenated as:

echo "hello $1" sh world

This is why you get

hello  sh world

The double space between hello and sh is because that's where $1 was supposed to go, but there is no $1.

 

As another example, without the $1, is:

ssh user@server echo "foo      bar" baz

Which results in:

foo bar baz

This is because the arguments are being concatenated together, so you end up with:

echo foo      bar baz

 

Since there is no way to get around the command being passed through a shell, you just have to ensure that what you passed can survive the shell evaluation. The way I usually accomplish this is with printf "%q"

For example:

cmd=(echo "foo      bar" baz)
ssh user@server "$(printf "%q " "${cmd[@]}")"

Which results in:

foo      bar baz

While it's cleaner and easier to understand with cmd being a separate var, it's not required. The following works just the same:

ssh user@server "$(printf "%q " echo "foo      bar" baz)"

The first thing to understand in this process is how ssh handles its arguments. I don't mean the arguments to the thing you're trying to run, but arguments of ssh. When you invoke ssh, the arguments after the remote host specification (user@server) are concatenated together, and passed through the shell on the remote end. This is important to note, as just because your arguments are properly split on the local side, does not mean they will be properly split on the remote side.

To use your example:

ssh user@server 'echo "hello $1"' sh "world"

These arguments get concatenated as:

echo "hello $1" sh world

This is why you get

hello  sh world

The double space between hello and sh is because that's where $1 was supposed to go, but there is no $1.

 

As another example, without the $1, is:

ssh user@server echo "foo      bar" baz

Which results in:

foo bar baz

This is because the arguments are being concatenated together, so you end up with:

echo foo      bar baz

 

Since there is no way to get around the command being passed through a shell, you just have to ensure that what you passed can survive the shell evaluation. The way I usually accomplish this is with printf "%q"

For example:

cmd=(echo "foo      bar" baz)
ssh user@server "$(printf "%q " "${cmd[@]}")"

Which results in:

foo      bar baz

While it's cleaner and easier to understand with cmd being a separate var, it's not required. The following works just the same:

ssh user@server "$(printf "%q " echo "foo      bar" baz)"

 

This also works fine with your shell argument example

cmd=(sh -c 'echo 1="<$1>" 2="<$2>" 3="<$3>"' sh "my arg1" "my arg2" "my arg3")
ssh user@server "$(printf "%q " "${cmd[@]}")"

Which results in:

1=<my arg1> 2=<my arg2> 3=<my arg3>

 


As an alternative solution, you can pass your command as a complete shell script. For example:

ssh user@server <<'EOF'

sh -c 'echo 1="<$1>" 2="<$2>" 3="<$3>"' sh "my arg1" "my arg2" "my arg3" EOF

There are drawbacks to this solution though as it's harder to do programmatically (generating the doc to pass on STDIN). Also because you're using STDIN, if you want the script on the remote side to read STDIN, you can't (at least not without some trickery).

Source Link
phemmer
  • 73.9k
  • 21
  • 199
  • 231
Loading