2

I am stuck with this piece of code here

user@server:~$ TEST="ssh rsync@otherserver.example.org 'date; hostname -A; uname -a'"
user@server:~$ $TEST
bash: date; hostname -A; uname -a: Command not found.

I want to use this inside a shell script an don't know what s the problem. Both systems are debian wheezy,

but if execute the command directly:

user@server:~$ ssh rsync@otherserver.example.org 'date; hostname -A; uname -a'
Fre Aug 23 20:02:55 CEST 2013
otherserver.example.org
Linux otherserver 3.2.0-4-amd64 #1 SMP Debian 3.2.46-1 x86_64 GNU/Linux

So whats the big deal here???? I relay am missing out something very trivial, but just cant figure it out... Please help...

The idea behind this, is that i build up a long string inside a script and execute it remotely in a single SSH session (renaming of zfs snapshots all in a row n.0 becomes n.1 and so on)

But its not working a is want it to work...

EDIT/UPDATE: Updated the exaples for beeter understanding of my question (from 'date; date; date' TO 'date; hostname -A; uname -a')

Thanks for all the responses so far. At first eval works but seems to be deprecated as mentioned by Users. So its my job to figure it out again on how to change it.

The string witch hast so be built looks something like this:

echo "Rearanging snapshots..."
        last_backup=7
        first_backup=0
        RENAME_STRING="'sudo zfs destroy $BACKUP_DATASTORE@n.$last_backup; "
        while [ $last_backup -gt $first_backup ]
        do
                RENAME_STRING=$RENAME_STRING"sudo zfs rename $BACKUP_DATASTORE@n.$(($last_backup - 1)) $BACKUP_DATASTORE@n.$last_backup"

            if [ $(($last_backup - 1 )) -gt $first_backup ]
            then
                    RENAME_STRING=$RENAME_STRING"; "
            else
                    RENAME_STRING=$RENAME_STRING"'"
            fi

            last_backup=$(($last_backup - 1 ))
    done

    #CURRENTLY SOLVED WITH EVAL as this one doesn't work...
    #remote_cmd=(/usr/bin/ssh "$BACKUP_USER@$HOST_TO" "$RENAME_STRING")
    #"${remote_cmd[@]}"
    eval /usr/bin/ssh $BACKUP_USER@$HOST_TO $RENAME_STRING

So maybe you guys will have a more elegant way to solve this?

EDIT2:

Is this output "OK"? (OK as in the means of NOT deprecated, and good to work with?

user@server:~$ TEST="date ; hostname -A ; uname -a"
user@server:~$ ssh rsync@otherserver.example.org <<< "$(printf '%s ' $TEST)"
Pseudo-terminal will not be allocated because stdin is not a terminal.
Linux otherserver 3.2.0-4-amd64 #1 SMP Debian 3.2.46-1 x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Fre Aug 23 20:13:53 CEST 2013
otherserver.example.org 
Linux otherserver 3.2.0-4-amd64 #1 SMP Debian 3.2.46-1 x86_64 GNU/Linux

EDI3: Updated/improved (?) while loop eval still used as <<< wont work in the script...

Daywalker
  • 485
  • 5
  • 25
  • Your quotes are wrong. Also `eval` is descouraged. Dont use it. `TEST="ssh rsync@example.org $(date) $(date) $(date)"` – Valentin Bajrami Aug 23 '13 at 14:21
  • @val0x00ff Your suggestion didn't work, or did you mean something else? eval actualy worked (like in the answer from dsumsky) – Daywalker Aug 23 '13 at 14:39
  • For readability, consider constructing the commands to run within SSH separately from the SSH invocation itself. In other words, `remote_commands='date; date; date'` and `remote_run() { local host="$1" ; local cmd="$2" ; ssh "$host" "$cmd" }`. Then `remote_run rsync@example.org "$remote_commands"`. – 200_success Aug 23 '13 at 15:09
  • Please see [BashFAQ/050](http://mywiki.wooledge.org/BashFAQ/050). – Dennis Williamson Aug 23 '13 at 19:03
  • @DennisWilliamson you mean especially point 3 ? as i have some trouble executing ssh someuser@somehost < $TEST inside shell script? And <<< as suggested in one answer also doesn't work... – Daywalker Aug 23 '13 at 19:22

5 Answers5

3

The suggested way to execute local commands remotely via ssh is not to use eval. This is discouraged. Since you are storing the whole line in a variable, instead you could

mydate=$(date; date; date); 
ssh rsync@example.org bash <<< "$(printf 'echo %q ' "$mydate")"

Eval should only be used in legacy systems who do not provide safe tools like the example I'm showing you.

This produces exactly the same result as you were running these commands using the interactive shell.

Hope it gives you some ideas at least.

UPDATE to demonstrate how commands can run remotely too, without defining a local var.

ssh rsync@example.com bash <<< "$(printf '%s ' "whoami && ps aux")"
Valentin Bajrami
  • 3,870
  • 1
  • 17
  • 25
  • You've changed the meaning of the code. In your solution, `date` executes locally rather than remotely. That defeats the purpose, which was to assemble a string of commands to run remotely. – 200_success Aug 23 '13 at 15:02
  • There are appropriate and inappropriate uses of `eval`. Please elaborate why `eval` is discouraged. – 200_success Aug 23 '13 at 15:04
  • 1
    I've updated my answer to add another example of how you can use commands remotely. @200_success for pesimists like you. http://mywiki.wooledge.org/BashFAQ/048. By the way, read this page. It's a life saving wiki. – Valentin Bajrami Aug 23 '13 at 15:10
  • @val0x00ff I just updated my Post. How do you mean I should solve it to be "correct"? – Daywalker Aug 23 '13 at 18:10
  • @Daywalker the `TEST` variable should be `TEST=$(date; hostname -A; uname -a)`. Execute it as `ssh rsync@example.com bash <<< "$(printf 'echo %q' "$TEST")"` This will work. you just have to copy and paste. NOTE: Don't use CAPITALIZED variable names. Use proper variable names so you can distinguish one from another and lowercase. Only environment variables like "PATH", "HOME", etc... are capitalized. – Valentin Bajrami Aug 23 '13 at 19:53
2

Try shell/bash build-in eval command:

$ help eval
eval: eval [arg ...]

Execute arguments as a shell command.

Combine ARGs into a single string, use the result as input to the shell,
and execute the resulting commands.

Exit Status:
Returns exit status of command or success if command is null.
dsmsk80
  • 5,757
  • 17
  • 22
2

There is no reason to compose such a long string of commands to run via SSH. Just make a permanent script on the remote host called /usr/local/sbin/rotate_backups and call it with ssh $USER@$HOST 'sudo /usr/local/sbin/rotate_backups'.

If you were asking this on the "Unix & Linux" StackExchange, then this could be an interesting question. However, as a System Administrator, your goals should be maintainability and security. If you are having trouble understanding your script, your colleagues and successors will curse you.

200_success
  • 4,701
  • 1
  • 24
  • 42
  • Thanks for the response, but I understanding the script is not the problem. In fact Security an manageability IS one goal t archive. I know its not one of the best scripts, but thats why I'm here ;) I just thought of to create just one script, which could be easily ported to other/new hosts. Maybe you are right an I should over think the concept. So you mean that I should create script and call it with all necessary variables like $DATASTORE_NAME and $MAX_SNAPSHOTS would also be ok, now as I think of it. Any other suggestion why this would be good/bad? – Daywalker Aug 23 '13 at 18:50
  • Your new plan sounds much better. – 200_success Aug 23 '13 at 18:59
2

Try this:

user@server:~$ remote_cmd=(ssh rsync@otherserver.example.org 'date; hostname -A; uname -a')
user@server:~$ "${remote_cmd[@]}"

It creates an array and executes it as suggested in BashFAQ/050 item 3.

Dennis Williamson
  • 60,515
  • 14
  • 113
  • 148
  • IS working on the command line directly, but not inside a script. remote_cmd="(/usr/bin/ssh $BACKUP_USER@$HOST_TO $RENAME_STRING)" and in the next line "${remote_cmd[@]}" I get a "Bad substitution" Error for the last line... (so eval is still in use...) – Daywalker Aug 24 '13 at 05:37
  • @Daywalker: Don't put quotes around the parentheses in the assignment. That prevents the array from being created. You should put them around the variables, though: `remote_cmd=(/usr/bin/ssh "$BACKUP_USER@$HOST_TO" "$RENAME_STRING")` – Dennis Williamson Aug 24 '13 at 05:50
  • Now i get a `Syntax error: "(" unexpected (expecting "fi")` on your line `remote_cmd=(/usr/bin/ssh "$BACKUP_USER@$HOST_TO" "$RENAME_STRING")` – Daywalker Aug 24 '13 at 06:17
  • @Daywalker: If you have a space after the equal sign, it shouldn't be there. – Dennis Williamson Aug 24 '13 at 11:18
  • No there is no space. This is somethign I would have recognized. (I updated my original post to show you what i have done – Daywalker Aug 24 '13 at 14:58
  • @Daywalker: I can't see anything else that would cause that error in the update you posted. – Dennis Williamson Aug 24 '13 at 15:48
  • That's also my problem... :( As you can see it works if its commented out, so there should be no syntax problem inside the script. But thanks anyway! As I don't have much time this weekend, I will search again next Monday. – Daywalker Aug 24 '13 at 16:08
0

PROBLEM FOUND

I started my long string of with a singe ' and endet with a single '

NOW i keep all thos single quotes (') by myself create the string and add a double quote at the end, an ALL WORKS!

#!/bin/sh
...
#[SOME_CODE_HERE]
...
echo "Rearanging snapshots..."
last_backup=7
first_backup=0
RENAME_STRING="sudo zfs destroy $BACKUP_DATASTORE@n.$last_backup; "
while [ $last_backup -gt $first_backup ]
do
        RENAME_STRING=$RENAME_STRING"sudo zfs rename $BACKUP_DATASTORE@n.$(($last_backup - 1)) $BACKUP_DATASTORE@n.$last_backup; "
        last_backup=$(($last_backup - 1 ))
done
/usr/bin/ssh $BACKUP_USER@$HOST_TO "$RENAME_STRING"

So can someone explain this to me, why i can not set the quotes Inside my Variable?

And this construct now works just fine. Thank you all for your help! I hope that my answer will also help someone! (the ALL_CAPITAL thing will be sorted out later ;) )

Daywalker
  • 485
  • 5
  • 25