-2

I want to replace the text sd in the following command with a variable:

lsblk -r --output NAME,MOUNTPOINT | awk -F  \/ '/sd/ { dsk=substr($1,1,3);dsks[dsk]+=1 } END { for ( i in dsks ) { if (dsks[i]==1) print i } }'

I have tried:

lsblk -r --output NAME,MOUNTPOINT | awk -F -v dev=sd \/ '/{print $dev}/ { dsk=substr($1,1,3);dsks[dsk]+=1 } END { for ( i in dsks ) { if (dsks[i]==1) print i } }'

but that's not working. Can anyone help me? Thanks!

The lsblk output, which is piped to awk is this:

NAME MOUNTPOINT
sda 
sda1 /boot/efi
sda2 /var/lib/kubelet/pods/fae467d4-05e9-4aea-8341-0d1524a3ec9c/volume-subpaths/tigera-ca-bundle/calico-kube-controllers/1
sda3 
sdb 
sdc 
sr0 

expected output is:

sdb
sdc

So all disks, starting with sd, except the os disk, sda

  • 1
    Hi Peter and welcome. When asking questions like this, please make sure to include: i) example input (we don't know what lsblk returns on your system), ii) desired output. That way we can test our solutions. Also, when there is an error, don't tell us something is "not working" since that really doesn't tell us anything. Instead, show us the exact error message or, if none, describe how the behavior is not what you expected. Finally, it is always good to mention your operating system so we know what you are working with (we support several). – terdon Mar 06 '24 at 14:54
  • See also lsblk -nro name | grep -Eo '^sd[a-z]+' | sort | uniq -u – Stéphane Chazelas Mar 06 '24 at 15:31
  • 1
    Imagine taking your car to a mechanic and just saying "that's not working" and expecting them to be able to fix it. Exactly the same applies when asking for help to debug problems with your software or when going to a doctor if you're sick. You need to tell us the symptoms. Also, if you don't show us the text you want help writing a tool to parse (i.e. the output of lsblk -r --output NAME,MOUNTPOINT) and you don't show us the final output you want from the desired tool given that input then you're making it very much harder than necessary, maybe impossible, for us to be able to best help you – Ed Morton Mar 06 '24 at 15:59
  • @terdon, yes, you are right, didn't realize this, added this to the question. Thanks – Peter Talen Mar 06 '24 at 17:24
  • You appear to be doing substr($1,1,3) to convert sda1 into sda but if you want to use a variable dev instead of hard-coding sd then is it safe to assume the contents of that variable will also be 2 chars, always with 1 letter then digits after it in the input, and you won't have something like dev=FOOBAR with input like FOOBARabc123 /var/lib/kubelet/pods/fae467d4... and then need to convert FOOBARabc123 into FOOBARabc or something else? – Ed Morton Mar 06 '24 at 17:43
  • @StéphaneChazelas, that's working very well, thanks. – Peter Talen Mar 06 '24 at 17:58
  • I wonder what you're expecting /{print $dev}/ to do in that second AWK script – ilkkachu Mar 06 '24 at 18:05

1 Answers1

3

The -F option sets the field separator. You cannot put some other option in the middle: awk -F -v dev=sd \/. That is actually setting the field separator to the string -v:

$ awk -F -v 'BEGIN{print FS}'
-v

And not even that since the dev=sd is parsed as a script that should be run. Anyway, what you want is:

awk -F '/' -v dev=sd ' awk script here ' file

Next, the / / construct in the context you are using it expects a regular expression. It won't expand variables. Also, awk variables don't start with $, only fields do. A user-defiend variable is var not $var. In fact, $var actually means "the field whose number is the value stored in var". So if var=6, then $var is $6, the value of the 6th field.

Instead, to match against a variable, you need the ~ operator. So, putting all this together, my guess (you haven't shown input or expected output so I can only guess) is that you want something like this:

lsblk -r --output NAME,MOUNTPOINT | 
  awk -F '/' -v dev=sd  '
    $0 ~ dev { 
     dsk=substr($1,1,3);
     dsks[dsk]+=1 
    } 
    END { 
     for ( i in dsks ) { 
      if (dsks[i]==1) print i 
     }
   }'
Kusalananda
  • 333,661
terdon
  • 242,166
  • @PeterTalen Note that that script could probably be made more robust and/or efficient once we know what your input and expected output look like. – Ed Morton Mar 06 '24 at 16:16
  • @Kusalanada, thanks! – Peter Talen Mar 06 '24 at 17:26
  • @ed-morton, added it to the question – Peter Talen Mar 06 '24 at 17:26
  • @PeterTalen you added some sample input but not the expected output given that input so that's much better but still only half the example AND it's only the sunny day cases - what if sd appeared in the middle of sr0 /foo/bar/thi**sd**isaster/..., etc.? It's always much easier to write code to match the text you want than to not match similar text you don't want. So think about what the possible rainy day cases are and add those to your sample input/output otherwise you're likely to get a solution that ONLY works for sunny day cases (as is the case with all of the code posted so far). – Ed Morton Mar 06 '24 at 17:27