こんなスクリプトをつくって(とりあえず名前はexecsとした)
#!/bin/sh

PROG="$0"
MODE="EXEC"
MARK="%%"
BACKGOUND=NO

while [ $# -gt 0 ]; do
    case "$1" in
    -n) MODE=DRYRUN
        ;;
    -m) shift
        MARK="$1"
        ;;
    -b) MODE=BACKGROUND
        ;;
    -*)
        echo "$PROG: invalid argument: $1"
        exit 1
        ;;
    *) break ;;
    esac
    shift
done

if [ $# -eq 0 ]; then
    echo "$PROG: no command specified."
    exit 1
fi
TEMPLATE="$*"

while read ARG; do
    CMD=$(echo "$TEMPLATE" | sed "s|$MARK|$ARG|g")
    case $MODE in
    DRYRUN)
        echo "$CMD"
        ;;
    EXEC)
        echo "+ $CMD"
        $CMD
        ;;
    BACKGROUND)
        echo "+ $CMD &"
        $CMD &
        ;;
    *)
        echo "$PROG: INTERNAL ERROR"
        exit 1
        ;;
    esac
done

case $MODE in
BACKGROUND)
    wait
    ;;
esac

exit 0
こんなふうにするとよい。
zfs list -t snapshot -r -H -o name tank/foo/bar | \
   execs -b zfs destroy %%
複数を同時実行した方が(たぶんuberblockの書き込み回数が減るから?)かなり速くなるので execs -bオプションは必須。ちなみにzfs destroyは1引数しか取れないので、xargsをつかうときには以下のように-n1オプションを付ける必要がある。
zfs list ... | xargs -n1 zfs destroy
zfs listとexecsのあいだにgrepでフィルタすることもできる。たとえばスナップショット名が "dailyなんとか" のものだけを消したい場合には
zfs list -t snapshot -r -H -o name tank | \
  grep "@daily" | execs -b zfs destroy %%
tank/fooの下は消したいけどtank/foo/barは残したいなら
zfs list -t snapshot -r -H -o name tank/foo | \
  grep -v ^tank/foo/bar | execs -b zfs destroy %%
ちなみに、tank/foo直下のスナップショットだけリストするには
zfs list -t snapshot -r -d 1 tank/foo