Shell Help with iterating over zfs snapshots

I'm trying to write something so I can handle zfs snapshots for backups of my iocage zpool, but I'm a bit stumped on how zfs prints snapshots. Currently, I have something like this

Code:
zfs list -Hp -d 1 -t snapshot -o name | grep iocage

My intent here is to have no headers and just go one layer in so I'm not getting any sub-datasets. This doesn't seem to create something I can iterate over though, just read. My goal in the end is to have something like this:

Code:
datasets=$(zfs list -Hp -d 1 -t snapshot -o name | grep iocage)

for i in $datasets; do
    do_something
done

According to the man pages, using -H and -p should make this parsable, I can't find anything else that might make this clear in the man pages, how can I make it easier to parse my snapshots in for loops?
 
This is one way to do it if you don't care about saving the read in lines:

Code:
zfs list -Hp -d1 -t snapshot -t snapshot -o name | grep iocage |  \
    while read line; do
        do_something "$line"
    done

If you want to capture the lines and then use later as in your version, do it with double quotes on the datasets variable:

Code:
datasets=$(zfs list -Hp -d 1 -t snapshot -o name | grep iocage)

for i in "$datasets"; do
    do_something "$i"
done

There's an important difference between $datasets and "$datasets" in bourne shells. Without the double quotes the shell does its own parsing and splitting by IFS (by default the set of space, tab and newline characters) on the variable's value and you'll get just one long string with the lines concatenated together with single space characters. With the double quotes the shell does variable expansions but doesn't touch the whitespace characters and leaves any newlines in place. You should almost always use "$variable" or even more preferably "${variable}" in every instance where a variable is used, it can save you from a lot of nasty hidden bugs caused by values that have whitespace in them, for example file names.
 
I prefer using date(1) to create the snapshot name. This allows you to easily check the time between snapshots by having date calculate the whole thing for you.
Code:
peter@zefiris:/home/peter $ zfs list -rt all zroot/home
NAME                USED  AVAIL  REFER  MOUNTPOINT
zroot/home         33.8G  33.2G  31.6G  /home
zroot/home@181118   184M      -  30.6G  -
zroot/home@191118  77.5M      -  30.8G  -
zroot/home@201118   169M      -  31.1G  -
zroot/home@211118   148M      -  31.1G  -
zroot/home@221118  87.8M      -  31.6G  -
zroot/home@231118   194M      -  31.7G  -
zroot/home@241118  76.3M      -  31.6G  -
So I basically use:
Code:
CURDAT=$(date "+%d%m%y");
PRVDAT=$(date -v-${RETENTION}d "+%d%m%y");
This allows me not having to worry about grabbing snapshot names, because I created those myself. The main setup is basically:
Code:
        # Make & clean snapshot(s)
        for a in $FS; do
                ZFS=$(zfs list -r ${POOL} | grep -e "${a}$" | cut -d ' ' -f1);
                if [ "$ZFS/" == "/" ]; then
                        echo "${PROG}: Can't process ${a}: not a ZFS filesystem." >/dev/stderr;
                else
                        $(zfs snapshot ${OPTS} ${ZFS}@${CURDAT} > /dev/null 2>&1) || echo "${PROG}: Error creating snapshot ${ZFS}@${CURDAT}" > /dev/stderr
                        $(zfs destroy ${OPTS} ${ZFS}@${PRVDAT} > /dev/null 2>&1) || echo "${PROG}: Error destroying snapshot ${ZFS}@${PRVDAT}" > /dev/stderr
                fi
Where FS is simply a list of directories (mountpoints) which point to actual ZFS filesystems.
 
There's an important difference between $datasets and "$datasets" in bourne shells. Without the double quotes the shell does its own parsing and splitting by IFS (by default the set of space, tab and newline characters) on the variable's value and you'll get just one long string with the lines concatenated together with single space characters. With the double quotes the shell does variable expansions but doesn't touch the whitespace characters and leaves any newlines in place. You should almost always use "$variable" or even more preferably "${variable}" in every instance where a variable is used, it can save you from a lot of nasty hidden bugs caused by values that have whitespace in them, for example file names.

This is exactly what I needed to know, thank you very much! Thank you also for some clarification on the differences in how its parsed

This allows me not having to worry about grabbing snapshot names, because I created those myself.

That's basically what I'm trying to do here. I'm using date and then a tag for days/weeks/months for retention and that's what I want to do the iteration for. I'm doing it a bit differently than you are though it appears. Thanks for the code snippet :)
 
Back
Top