5
\$\begingroup\$

The following function searches for disks whose partitions all have an attribute that match a filter when listed with lsblk. Comments are in TomDoc format.

# Public: finds drives in /dev/sd* whose partitions all have an attribute that matches a filter.
# 
# $1 - filter (default: '.*')
# $2 - attribute (default: 'MOUNTPOINT')
#
# Returns full paths to disks whose attributes match the filter
#
# Examples
#  lsblk /dev/sda -oTYPE,PATH,MOUNTPOINTS --pairs
#   #  TYPE="disk" PATH="/dev/sda" MOUNTPOINTS=""
#   #  TYPE="part" PATH="/dev/sda1" MOUNTPOINTS="/boot/efi"
#   #  TYPE="part" PATH="/dev/sda2" MOUNTPOINTS="/var/snap/firefox/common/host-hunspell\x0a/"
#  lsblk_disk_filter '(/var/snap/firefox/common/host-hunspell\\x0a/|/boot/efi)' 'MOUNTPOINTS'
#   #  /dev/sda
#
#  lsblk /dev/sda -oTYPE,PATH,FSTYPE --pairs
#   # TYPE="disk" PATH="/dev/sda" FSTYPE=""
#   # TYPE="part" PATH="/dev/sda1" FSTYPE="vfat"
#   # TYPE="part" PATH="/dev/sda2" FSTYPE="ext4"
#  lsblk_disk_filter '(ext4|xfs|vfat)' 'FSTYPE'
#   # /dev/sda
# 
# REQUIRES lsblk >= 2.33 for PATH output option - https://github.com/util-linux/util-linux/blob/master/Documentation/releases/v2.33-ReleaseNotes#L372
# TODO: add support for /dev/nvme* (I don't have hardware to test this...)
lsblk_disk_filter () {
  local filter="${1:-'.*'}";
  local attribute="${2:-'MOUNTPOINT'}"

  local PERLCMD='my ($type, $path, $attr) = /^TYPE\="([^"]*)"\s*PATH\="([^"]*)"\s*$col\="([^"]*)"\s*$/ or next; # VAR ASSIGNMENT \
  if( $type=~/disk/i){          # CHECK IF TYPE IS DISK \
    printf("%s ",$disk);          # PRINTING LAST DISK \
    $disk=$path;                 # ASSIGN NEXT DISK \
  } elsif($attr!~/$filter/){    # PARTITION ATTRIBUTE MISMATCH TEST \
    $disk="";                   # DELETING DISK ON MISMATCH \
  } END{ printf($disk); } # PRINT LAST DISK'

  lsblk --pairs --noheadings --ascii -o TYPE,PATH,$attribute /dev/sd* | perl -s -wnl -E "${PERLCMD}" -- -filter="$filter" -disk='' -col=$attribute
  # perl switch example: https://stackoverflow.com/questions/31155659/how-do-i-pass-command-line-options-to-a-perl-program-with-perl-e
  # /dev/sdx describes SCSI/SATA devices, /dev/sdx\d+ describes their partitions.
  # See the /dev/ naming convention described in the "devices" section in fdisk.
} # END lsblk_disk_filter
\$\endgroup\$
2
  • 1
    \$\begingroup\$ The backslashes before newlines inside $PERLCMD are not needed. \$\endgroup\$
    – choroba
    Commented Aug 29, 2023 at 21:54
  • 4
    \$\begingroup\$ There is a point when leaving bash and switching completely to perl makes sense. \$\endgroup\$
    – mpapec
    Commented Aug 30, 2023 at 6:23

1 Answer 1

5
\$\begingroup\$

Given that the majority of this is Perl, it's probably worth using Perl as the main interpreter and using that to invoke lsblk directly.

Alternatively, use shell more, and consider using JSON output and jq for the parsing. That might fix the bug that we don't correctly parse anything which lsblk prints using hex escapes.

We're not using a tree display from lsblk, so the --ascii option is redundant.


As a starting point for using jq, this simple program finds the disks:

lsblk --json --noheadings -O |
  jq '.blockdevices[]|select(.type=="disk")|.path'

It shouldn't be hard to extend this to add additional selectors to the select() call.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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