0

Is it possible to do something like this?

if
rsync $folder/coopshop/prod $folder2/coopshop
rsync $folder/coopweb/prod $folder2/coopweb
rsync $folder/unihobbyshop/shop $folder2/unihobbyshop
rsync $folder/unnihobbyweb/web $folder2/unihobbyweb
then
  echo "OK! rsync done!"
else
  echo "Error! rsync crashed!"
fi

I know there is a possibility to do with \ character at the end of the line, but this is not quite an elegant solution. Is there a better way to do this?

  • Possible duplicate of stackoverflow.com/questions/5130847/… – FedonKadifeli Jun 28 '19 at 15:31
  • Did you search a bit for yourself on how bash scripting works? – vanadium Jun 28 '19 at 15:44
  • 2
    Exactly what behavior do you want? If one of the rsync command fails, do you want it to run the rest, or stop on the failure? Also, if some of the rsyncs succeed and some fail, should it print "OK" or "Error"? – Gordon Davisson Jul 1 '19 at 7:44
  • @GordonDavisson Hello, in case of one command is false, then message would be false. I think this is possible to do with commands in variable command=$(commands) divided by enter, and simply with if($commands). I will test and let you know. – Stanislav Hosek Jul 1 '19 at 8:37
  • your example doesn't have the condition part right.. it just runs the rsync commands.. or is a pseudocode? – user688056 Jul 1 '19 at 8:41
4

Consider set -e.

If this is in a script, then you'll probably want to remove the if...then entirely and just put set -e at the top of the script. This causes the script to stop immediately if any command fails. When any of those rsync commands fail, they'll display error messages explaining the failure, so you probably don't need to write your own message at all.

If you want to show the commands that are run before running them, use set -x. You can combine the two by writing set -ex.

With &&, you don't need \.

If you do decide to write something along the lines of what you've shown, then as Ronny Blomme says you can separate the commands with &&. Note that you don't need to run the commands in subshells, i.e., you don't need to enclose them in parentheses.

If you write an && operator at the end of a line, then you don't need to write \ to specify line continuation. A command can't end in && (logical AND) or || (logical OR) so the shell will continue parsing onto the next line.

if
    rsync "$folder/coopshop/prod" "$folder2/coopshop" &&
    rsync "$folder/coopweb/prod" "$folder2/coopweb" &&
    rsync "$folder/unihobbyshop/shop" "$folder2/unihobbyshop" &&
    rsync "$folder/unnihobbyweb/web" "$folder2/unihobbyweb"
then
    echo 'OK! rsync commands completed successfully!'
else
    echo 'Error! some rsync command failed!'
fi

You don't have to put && at the end of a line. You can write multiple commands on the same line separated by && if you like.

Note that the final command being tested doesn't end with && because && separates the commands. If any command fails, the others will not run.

Running all the commands even if some of them fail

You may instead want to run all the rsync commands even if some of them fail, then test if any of them failed and operate accordingly. For this, you can trap ERR. This is not an actual signal, but the ERR signal_spec in bash catches commands that fail for any reason, including a crash (and including SIGINT). See pLumo's answer (but omit set -e to keep going) and this post by William Pursell; for more general information, see the output of help trap.

The way I like to do it (with commands like yours plugged in) is:

failed=
trap 'failed=yes' ERR

rsync "$folder/coopshop/prod" "$folder2/coopshop"
rsync "$folder/coopweb/prod" "$folder2/coopweb"
rsync "$folder/unihobbyshop/shop" "$folder2/unihobbyshop"
rsync "$folder/unnihobbyweb/web" "$folder2/unihobbyweb"

if [ -z "$failed" ]; then
    echo 'OK! rsync commands completed successfully!'
else
    echo 'Error! some rsync command failed!'
fi

About the other changes I've made... and what if command really tests.

Your arguments to rsync use parameter expansion so I've taken the liberty of double-quoting them. Otherwise, word splitting and globbing will be performed. From context it appears you don't want those additional expansions. (Usually you don't.)

Note that, contrary to the message your script prints, you are not actually testing if any rsync command crashed. You are instead testing if any rsync command failed. Crashing is abnormal program termination, where a process is terminated by a signal, typically due to a bug or a condition that cannot be reasonably handled. This is far rarer than normal program termination, which may itself occur with an exit status indicating success (zero) or failure (some other number). If rsync does crash, the code above will catch that too. But most of what it's catching, in practice, is normal program termination with an exit status indicating failure.

| improve this answer | |
  • I like the use of trap without exit on error. Never thought about it :-) I would have used rsync ... || failed=yes, but the trap is much more elegant. – pLumo Jul 1 '19 at 9:46
  • "if/then/else" feels too much like something other than bash. You might also consider syntax like: [ -z $failed ] && echo "OK" || echo "Error". – Ray Butterworth Jul 1 '19 at 12:53
  • 1
    @RayButterworth if is commonly used in bash and shouldn't be feared. You might consider not using a && b || c as a ternary conditional, as it's wrong. Even echo can fail in a write error, like with >/dev/full; it can even happen unexpectedly. Even when b can't fail, testing it expresses the wrong idea. I do consider && and || when I write single-branch ifs. a && b for if a; then b; fi, and a || b for if ! a; then b; fi, are often reasonable, albeit not completely equivalent (if succeeds when its condition fails). – Eliah Kagan Jul 1 '19 at 15:33
2

For a script like yours, I would not use if/else, but enable exit on error (set -e) and trapping.

Something like this:

#!/bin/bash

set -euo pipefail
trap 'echo "Error! rsync crashed!"' ERR INT

rsync $folder/coopshop/prod $folder2/coopshop
rsync $folder/coopweb/prod $folder2/coopweb
rsync $folder/unihobbyshop/shop $folder2/unihobbyshop
rsync $folder/unnihobbyweb/web $folder2/unihobbyweb

echo "OK! rsync done!"
| improve this answer | |
  • Is it necessary to list INT? I believe ERR covers all cases in which the shell detects abnormal program termination, in addition to cases of normal program termination that causes a command to have nonzero exit status. (I've tested this by running trap 'echo "trap triggered"' ERR followed by a couple runs of sleep 10 during which I send the sleep process SIGINT. Whether SIGINT is sent by pressing Ctrl+C on the terminal or with killall -INT sleep on another terminal, the message is printed.) – Eliah Kagan Jul 1 '19 at 9:42
  • maybe right, I didn't test, see also "Something like this"^^, so deviations are allowed, and you give a good one in your answer. – pLumo Jul 1 '19 at 9:45
1

try this syntax:

#!/bin/bash
mkdir dir1 dir2
echo "test1" > dir1/file1
echo "test2" > dir1/file2

if (rsync dir1/file1 dir2) && (rsync dir1/file2 dir2) 
then
  echo "OK! rsync done!"
else
  echo "Error! rsync crashed!"
fi
| improve this answer | |
  • Much better Ronny! +1 from me (cc @Melebius ) ;-) – Fabby Jul 1 '19 at 21:51
-1

I tried this.

commands=$(
mkdir 1
mkdir 2
)

if($commands); then
  echo 'Ok.'
else
  echo 'False.'
fi

It works. Problem is when I add for example ls -la to commands, it failed with this.

./test: line 7: total: command not found

I don't know why.

| improve this answer | |
  • 2
    It might work coincidentally but this is not the solution to your question. The part commands=$(...) collects the standard outputs of the commands. The part if($commands) is trying to use the text outputs as a command which is generally a wrong idea. – Melebius Jul 1 '19 at 10:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.