Score:0

How to display size of installed hard drives

in flag

I want to write simple linux script which will display what are the real size of installed hard drive. If there is 128GB SSD, lsblk shows less than 128. I need to check the real size of memory and print it like this: 128GB installed = script gives output

128

If there are two hard drives installed, it gives output

128/512

It should work on both sata and nvme drives

Artur Meinild avatar
vn flag
You need to understand the [difference between Gigabytes and Gibibytes](https://docs.ukcloud.com/articles/other/other-ref-gib.html). A 128 GB harddrive is 119 GiB (Gibibytes), which is usually what is reported by the OS. It's not clear what you mean by "real size" - there is no such thing as "fake size" AFAIK.
Grzegorz Michalak avatar
in flag
Yes, I know that, sorry. I mean Gigabytes, "full" size of harddrive
Artur Meinild avatar
vn flag
The full size is the same, no matter what unit you report it in. There's also tools to convert between [units](https://linux.die.net/man/1/units).
Grzegorz Michalak avatar
in flag
I understand it.. just need a command to display harddrives size in GB (not GiB)
Artur Meinild avatar
vn flag
`lsblk -b` shows the size in bytes - which you can then extract and convert to your liking.
Grzegorz Michalak avatar
in flag
But how to extract only installed drives?
Artur Meinild avatar
vn flag
This is where your scripting skills comes into play. However, it sounds your script will not be much different from `lsblk` or `df` with the correct parameters. Consider `alias dfs='\df -hl -T -x"squashfs" -x"tmpfs" | grep -v "/var/lib/docker" | (read h; echo "$h"; sort -V)'` which is sorted `df` but without docker volumes or squashfs and tmpfs devices.
Score:1
in flag
Cas

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.

Cas avatar
in flag
Cas
The regex is quite complex, but I find it fun to write so it was no problem. I find it funny that marosg was talking about not wanting to write a whole script when I did it in one pipe, so don't listen too much to people that complain like that, though I do agree that you should always know what you're doing and not just blankly copy a script someone made for you. Understanding what the writer did is a great way of learning!
Grzegorz Michalak avatar
in flag
Thanks, great to hear that :) Can you describe what every part of your script is doing?
Grzegorz Michalak avatar
in flag
Thanks, it's great! I used this for now: `lsscsi -s | awk '/N:/ {printf $NF}' | rev | cut -c 3- | rev && lsscsi -s | awk '$3 ~ /ATA/ {printf $NF}' | rev | cut -c 3- | rev `
Cas avatar
in flag
Cas
See the edit on my answer!
sudodus avatar
jp flag
+1; But I needed the prefix LANG=C for your command to work, because I do not use English. Maybe you can edit your answer to include that.
Score:1
vn flag

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)
...
Grzegorz Michalak avatar
in flag
Yes, its good command but I want it to display only size and, if possible this size in brackets (1TB, 1024GB etc)
marosg avatar
vn flag
It is not too complicated to parse it and display as you want. Don't expect people to write you the whole script.
Score:0
jp flag

The following one-line command should do what you want.

for i in $(lsblk -bdno name,size|tr -s ' ' '_'); do j=${i##*_};j=$(((j+500000000)/1000000000));printf "${i%_*}\t$j\tGB\n";done

You can put a function with it in your ~/.bashrc file near the aliases in order to run it with a short command, for example mydrives

function mydrives { for i in $(lsblk -bdno name,size|tr -s ' ' '_'); do j=${i##*_};j=$(((j+500000000)/1000000000));printf "${i%_*}\t$j\tGB\n";done }

It works like this for me, after

source ~/.bashrc

and in all terminal windows that you open after you modified your ~/.bashrc file.

In my computer it prints like this

$ mydrives
sda     256     GB
sdb     4001    GB
sr0     1       GB
nvme0n1 250     GB

I'm used to GiB (gibibytes) so I often use the plain lsblk command

lsblk

or when I want more details and have a wide terminal window

lsblk -fm

or otherwise

lsblk -m

The command matching what I suggested for you, but with mibibytes, gibibytes, tibibytes (base 2 rather than base 10) would be

$ lsblk -dno name,size
sda     238,5G
sdb       3,7T
sr0      1024M
nvme0n1 232,9G
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.