How to display size of installed hard drives

Solution 1:

It requires sudo to use but it works:

sudo lshw | grep -Pzo "\*-(disk|namespace)(\n.*)+?\s+size:.*?\(\K\d+\w+" | tr "\0" "\n" | paste -sd/

It gives the output like this:

1TB/750GB

Otherwise you wouldn't know if it was TB/GB/MB so that's why I kept those.

EDIT: I noticed an .* in the regex wasn't needed. The command above has been updated.

Explanation:

sudo lshw: well... list the hardware. We need sudo to see the drives in the system. This will be our source of info.

grep -Pzo: we're going to use regex to get the required info

-P = activate perl regex
-z = treat everything as one long line; this is required because we're going to use regex over multiple lines
-o = instead of showing the output and marking the result red, just show the result

regex (tip: remove the -o (a.k.a -Pz) and add the regexes below one by one to see what happens step by step; the red text is what the regex matched, so you can see step by step how we're getting closer to the desired result and what every step changes to the output):

\*-(disk|namespace) = Find all text that is "*-disk" or "*-namespace". We need to escape (= \) the "*" because in regex it means zero or more, but we don't want that, we want to search for a literal "*".
(\n.*)+ = Keep adding (the "+" = one or more) lines ("\n" = go to the next line; ".*" = everything on that line) to the matched text; you'll see that now everything under the first match of "\*-(disk|namespace)" is red.
?\s+size: = we keep adding lines until we come across the first ("?" = non-greedy a.k.a. the first match instead of the last match) match of one or more whitespaces (= "\s+"; "\s" is whitespace (tab, space, etc.); "+" is one or more) and then "size:"; you'll see that were coming closer to the desired number in the output.
.* = match the rest of that line,
?\( = until the first match (= ?) of a "(", which we need to escape because it is used in regex (you can see it being used in the first part of the regex here).
\K\d+\w+ = match a number (= \d) one or more times (= "+") and after that a word character (= \w) one or more times (= "+"). We now have the desired text in the match, but we don't want all the matched text before that in the output, so we put a "\K" before the desired text to remove the matched text before the "\K" from the output. It is still required to match, it just isn't included in the output. This makes all the regex before it a positive lookbehind (look it up; "positive lookbehind perl regex") with regex capabilities.

tr "\0" "\n": You'll see that we have the desired text matched and nothing more or less. When we add the -o again, you'll see that the results are displayed in a weird way (all after each other). That is because they are seperated with a null character (= \0) which you can't see, instead of a newline. This is an artifact of the "-z" option of grep. To show them in a normal list, were going to replace the null characters with a newline using tr.

paste -sd/: now that we have the results in a list, we can use the paste command to put them after each other, using a "/" as a devider.

Solution 2:

It is not clear what your question is. I hope you don't want somebody to write the whole script but rather you ask how to get the numbers you want to work with.

Use lshw, this is (redacted) output from my system, one NVMe and one SSD present

$ lshw -c disk
  *-namespace               
       description: NVMe namespace
       logical name: /dev/nvme0n1
       size: 953GiB (1024GB)
...
  *-disk
       description: ATA Disk
       product: Samsung SSD 850
       logical name: /dev/sda
       size: 931GiB (1TB)
...