Shell Script – Handling Whitespaces in ZFS Dataset Names

shell-scriptzfs

I'd like to take output from two commands –

zpool list
zfs list

and for each found pool:

zpool get all nameofpool

and for each found file system:

zfs get all nameoffilesystem

Background and environment

I'm making local changes to script that's integral to OS X,

/usr/bin/sysdiagnose

  • #!/bin/sh for starters
  • always run with superuser privileges
  • sometimes effectively headless (triggered by a key chord), so the output must be a file.

First experiment

Based on the example at #65 Remove all ZFS snapshots:

#!/bin/sh
for dataset in `zfs list -H | cut -f 1`
do
  zfs get all $dataset
done

That works, but not where there's a space in the name of the dataset. For example where the file system is zhandy/Pocket Time Machine the output includes:

cannot open 'zhandy/Pocket': dataset does not exist
cannot open 'Time': dataset does not exist
cannot open 'Machine': dataset does not exist

 Second experiment

… was based on the first answer to this question – using IFS – and made the script more like Apple's. See revision 4.

Third experiment

Based on the accepted answer to this question – with IFS, and quotation marks for "$dataset":

#!/bin/sh
data_directory_path=~/Desktop
ECHO=/bin/echo
ZFS=/usr/sbin/zfs
ZPOOL=/usr/sbin/zpool
# If there exists a zfs binary, get some ZFS information
if [ -f "${ZFS}" ]
then
    "${ECHO}" "Recording ZFS pool version information ..."
    "${ZPOOL}" upgrade &> ${data_directory_path}/zpool\ upgrade.txt
    "${ECHO}" "    listing all ZFS pools ..."
    "${ZPOOL}" list &> ${data_directory_path}/zpool\ list.txt
    "${ECHO}" "    detailed health status and verbose data error information ..."
    "${ZPOOL}" status -v &> ${data_directory_path}/zpool\ status.txt
    "${ECHO}" "    pools that are available but not currently imported"
    "${ZPOOL}" import &> ${data_directory_path}/zpool\ import.txt
    "${ECHO}" "Recording ZFS file system version information ..."
    "${ZFS}" upgrade &> ${data_directory_path}/zfs\ upgrade.txt
    "${ECHO}" "    listing all ZFS file systems ..."
    "${ZFS}" list &> ${data_directory_path}/zfs\ list.txt
    "${ECHO}" "    all properties of each file system"
    OLD_IFS=$IFS
    IFS=$'\n'
    for dataset in `zfs list -H | cut -f 1`
    do
        "${ZFS}" get all "$dataset" &> ${data_directory_path}/ZFS\ file\ system\ properties.txt
    done
    IFS=$OLD_IFS
    "${ECHO}" "Listing the contents of /dev/dsk"
    "${LS}" -@adel /Volumes &> ${data_directory_path}/ls-dev-dsk.txt
    "${ECHO}" "Listing the contents of /var/zfs/dsk"
    "${LS}" -@adel /Volumes &> ${data_directory_path}/ls-var-zfs-dsk.txt
fi

Amongst the resulting files, ZFS file system properties.txt lists properties for just one ZFS file system … a dataset with white space in its name.

The most desirable end result is properties:

  • for all ZFS file systems
  • in a file.

Removing the following string –

&> ${data_directory_path}/ZFS\ file\ system\ properties.txt

– does get properties for all ZFS file systems, in a window of Terminal but not in a file. That's enough for me to accept an answer.


The output to file criterion, which wasn't in my first edition of the question, may be easily answered elsewhere.

Best Answer

Not splitting at spaces

bash's for loop splits the argument at all whitespace characters by default. You could somehow escape each line - or simply switch to another delimiter character. cut will return one value per line, so there's a newline in between which we can choose.

Watch out for the double quotes in zfs get all, so each $dataset doesn't get split up, too.

#!/bin/bash
IFS=$'\n'
for dataset in `zfs list -H | cut -f 1`
do
  zfs get all "$dataset"
done

Resetting IFS

Afterwards, you might want to reset IFS to the value before, store it to some temporary variable.

OLD_IFS=$IFS
# the whitespaces-sensitive lines
IFS=$OLD_IFS
Related Question