1

So I have a loop that basically goes through all of the disks installed on the system, then it assigns a variable to a disk name. however, I cannot use those variables if it's not inside the loop, how do I make them available to be used in other functions or other parts of the script?

Here is the code

#!/bin/bash

dev=1
for disk in $(fdisk -l | grep -o '/dev/sd[a-z]'); do
        set "DISK$dev=$disk"
        dev=$((dev+1))
done

So if I do echo $DISK1 for example, it doesn't display anything. But if I do echo $DISK1 INSIDE the loop, then ir displays the fist variable assignment. Can I export them and make them available outside of the loop?

mx0
  • 6,445
  • 12
  • 49
  • 54
user3311890
  • 321
  • 1
  • 3
  • 10
  • 1
    Something else set `DISK1`, not this loop. Your `set` command would set the first positional parameter to the value `DISK1=/dev/sda` (or something similar); it would not define a variable named `DISK1`. – chepner Aug 09 '18 at 17:09
  • Oops, you're right. Corrected it. – Charles Duffy Aug 09 '18 at 17:19
  • I don't think this is a duplicate...@CharlesDuffy – user3311890 Aug 09 '18 at 17:25
  • You're trying to do indirect assignment from a loop, and trying to do it with `set`. The linked duplicate tells you the proper (non-`set`) tools to use to solve the same problem (the OP there is initially trying to use `eval`, but the end purpose is the same). I don't see how it doesn't apply. – Charles Duffy Aug 09 '18 at 17:30

2 Answers2

0

set is not at all the right command to use here. You could pull this off with eval where you have set; but the proper way to solve this is simply to assign the values to an array.

disks=($(fdisk -l | grep -o '/dev/sd[a-z]'))

You can loop over the individual entries with ${disk[0]} through ${disk[n]} or retrieve the entire array at once with "${disk[@]}". The expression ${#disk[@]} evaluates to the number of elements in the array; though because array indexing is zero-based, the last index is one less than this value.

Of course, very often, you don't really need to keep the results in a variable explicitly. If you don't need random access (do you need to recall what you know about the first disk when processing the fifth? Really?) you should probably just loop over the values directly.

for disk in $(fdisk -l | grep -o '/dev/sd[a-z]'); do
    : whatever you need to do with the current "$disk"
done
tripleee
  • 175,061
  • 34
  • 275
  • 318
  • 1
    I'd suggest `readarray -t disks < <(fdisk -l ...)` if one has bash 4.0; sure, we don't *expect* characters in IFS or glob characters, but better to showcase robust practices. See also [BashPitfalls #50](http://mywiki.wooledge.org/BashPitfalls#hosts.3D.28_.24.28aws_....29_.29). – Charles Duffy Aug 09 '18 at 17:20
  • You are right, this is the easiest approach. At some point I tried making it interactive and thoguht about the need to call each disk by their names or such. But this is more simplistic and can still be referenced if I add them to a variable, right? – user3311890 Aug 09 '18 at 17:27
  • If you want the third disk that's `${disk[2]}` – tripleee Aug 09 '18 at 17:28
-3

You cannot directly access the parent scope. Also with "export" you will be able to access the exported variable to sub-shell but not the parent one.

A work around for this can be put all these set into a file (appending the new sets) and execute it with after the loop:

dev=1 
for disk in $(fdisk -l | grep -o '/dev/sd[a-z]'); do 
  echo "export \"DISK$dev=$disk\";" >> my_script_full_of_sets.sh
  dev=$((dev+1))
done
. my_script_full_of_sets.sh
Francesco
  • 79
  • 1
  • 4
  • 1
    `set` doesn't do what the OP wants. – Benjamin W. Aug 09 '18 at 17:10
  • @BenjaminW changed set with export and added a ; – Francesco Aug 09 '18 at 17:13
  • The code-generation approach is pretty awful -- you're executing data as code, which means if your data contains a disk label or anything else that could be plaintext you need to worry about it containing `$(rm -rf ~)` as a substring. See [BashFAQ #48](http://mywiki.wooledge.org/BashFAQ/048) (about `eval`, but also applying to `source`ing any machine-generated content), and -- as more of an aside -- [DontReadLinesWithFor](http://mywiki.wooledge.org/DontReadLinesWithFor). – Charles Duffy Aug 09 '18 at 17:15
  • 1
    ...and the thing is, there's no *good reason* to do anything awful here. `dev=1; while read -r disk; do printf -v "disk$dev" %s "$disk"; dev=$((dev+1)); done – Charles Duffy Aug 09 '18 at 17:18