239

I have the following script I wrote by searching Google, and it backs up my Linux system to an archive:

#!/bin/bash
# init

DATE=$(date +20%y%m%d)

tar -cvpzf /share/Recovery/Snapshots/$HOSTNAME_$DATE.tar.gz --exclude=/proc --exclude=/lost+found --exclude=/sys --exclude=/mnt --exclude=/media --exclude=/dev --exclude=/share/Archive /

This works, but I am wondering if I can format the script to show the command over multiple lines, something like this, so it is easy to edit later:

tar -cvpzf /share/Recovery/Snapshots/$HOSTNAME_$DATE.tar.gz 
--exclude=/proc 
--exclude=/lost+found 
--exclude=/sys 
--exclude=/mnt 
--exclude=/media 
--exclude=/dev 
--exclude=/share/Archive 
/

That way it is easier to read and edit later. Is it possible to format a Bash script this way?

Jay LaCroix
  • 2,411
  • 2
  • 13
  • 5

5 Answers5

319

All you should need to do is add a \(backslash) at the end of each line and it should be good to go.

So yours will look like:

tar -cvpzf /share/Recovery/Snapshots/$(hostname)_$(date +%Y%m%d).tar.gz \
    --exclude=/proc \
    --exclude=/lost+found \
    --exclude=/sys \
    --exclude=/mnt \
    --exclude=/media \ 
    --exclude=/dev \
    --exclude=/share/Archive \
    /

A Few Shortcuts

(based on your comment update for setting $HOSTNAME)

$HOSTNAME

Two options to set that:

  1. Set HOSTNAME

HOSTNAME=$(hostname)

  1. Use command substitution (e.g. $(command))

So it would look like above. That just makes the command run before using it.

$DATE

Another variable avoided would be easily:

$(hostname)_$(date +%Y%m%d).tar.gz \

$ man date will have the formats for the date options, the above is YYYYmmdd

unrealapex
  • 132
  • 8
nerdwaller
  • 17,054
  • 2
  • 44
  • 44
  • Thanks guys. One last thing. There seems to be a problem with the file name portion of my script: $HOSTNAME_$DATE.tar.gz When I run the script now, the output file is: 20121120.tar.gz – Jay LaCroix Nov 21 '12 at 03:26
  • If you want your actual "hostname" put it in back ticks (the tilde "~" key above tab): `/share/Recovery/Snapshots/\`hostname\`_$DATE.tar.gz` – nerdwaller Nov 21 '12 at 03:36
  • Anytime @JayLaCroix - Welcome to SU! – nerdwaller Nov 21 '12 at 03:40
  • 2
    It is [recommended](http://stackoverflow.com/a/4708569/1083697) to use `$(command)` instead of `\`command\``. – andrybak Jan 25 '15 at 10:46
  • This works with withspaces as a separator. If I have `ssh` on the first line and commands on the following lines, I think I need a semicolon as well besides backslash. – Timo Nov 22 '20 at 08:23
  • what do you need to do for the last argument do you still need the `\`? – Charlie Parker Dec 01 '21 at 19:40
  • Something to watch out for that caught me out and is hard to find by eye sometimes is a trailing space after one of the backslash characters which can cause the command to fail. – Jeremy Oct 26 '22 at 13:40
24

Use the backslash to continue a command on the next line:

tar -cvpzf /share/Recovery/Snapshots/$HOSTNAME_$DATE.tar.gz \
--exclude=/proc \
--exclude=/lost+found \
--exclude=/sys  \
--exclude=/mnt  \
--exclude=/media  \
--exclude=/dev \
--exclude=/share/Archive \
/
Paul
  • 59,223
  • 18
  • 147
  • 168
21

You can use this in bash

PARAMS=(
    -cvpzf /share/Recovery/Snapshots/$HOSTNAME_$DATE.tar.gz 
    --exclude=`enter code here`/proc 
    --exclude=/lost+found 
    --exclude=/sys 
    --exclude=/mnt
    # this is a comment 
    --exclude=/media 
    --exclude=/dev 
    # --exclude=/something
    --exclude=/share/Archive 
    /
)
# the quotes are needed to preserve params with spaces
tar "${PARAMS[@]}"
Axel Heider
  • 310
  • 2
  • 6
  • 3
    Should be `tar "${PARAMS[@]}"`, so that params with spaces get preserved. – timkay Mar 19 '22 at 17:25
  • Note that these are just PARAMS for the previous app, if you want to chain it with another action, such as redirecting stout and stderr to a file, that still has to go after. For example, `tar "${PARAMS[@]}" &> my-logs.log` – metinsenturk Apr 29 '23 at 20:04
13

The same command, but with comments for each line, would be:

tar -cvpzf /share/Recovery/Snapshots/$(hostname)_$(date +%Y%m%d).tar.gz `#first comment` \
    --exclude=/proc `#second comment` \
    --exclude=/lost+found `# and so on...` \
    --exclude=/sys \
    --exclude=/mnt \
    --exclude=/media \ 
    --exclude=/dev \
    --exclude=/share/Archive \
    /
Alter Lagos
  • 242
  • 2
  • 8
  • This technique is not recommended when performances matter: "note that this technique is expensive because it creates a subshell for each of such “inline comments” during execution. It is only suitable if the commands performance or cost is not a problem." Source: https://www.systutorials.com/how-to-add-inline-comments-for-multi-line-command-in-bash-script/ – roneo.org Apr 10 '22 at 04:54
3

Axel Heider provided a good alternative to backslashes. Two notes:

  1. The command can be included in the list, and
  2. The use of the list should be in double quotes "${PARAMS[@]}", so that any spaces in parameters get preserved.
#!/bin/bash

params=(
    show
    hello,
    world
    "multi word"
)

function show {
    echo --- Showing \""$@"\" as parameters ---
    for i in "$@"; do
        echo i=$i
    done
}

${params[@]}
"${params[@]}"

outputs

$ bash test.sh
--- Showing "hello, world multi word" as parameters ---
i=hello,
i=world
i=multi
i=word
--- Showing "hello, world multi word" as parameters ---
i=hello,
i=world
i=multi word
timkay
  • 131
  • 2