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?

4
  • 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. Commented Aug 9, 2018 at 17:09
  • Oops, you're right. Corrected it. Commented Aug 9, 2018 at 17:19
  • I don't think this is a duplicate...@CharlesDuffy Commented Aug 9, 2018 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. Commented Aug 9, 2018 at 17:30

2 Answers 2

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
Sign up to request clarification or add additional context in comments.

3 Comments

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.
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?
If you want the third disk that's ${disk[2]}
-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

4 Comments

set doesn't do what the OP wants.
@BenjaminW changed set with export and added a ;
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 (about eval, but also applying to sourceing any machine-generated content), and -- as more of an aside -- DontReadLinesWithFor.
...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 </proc/partitions (or < <(fdisk -l | grep -o /dev/sd[a-z]) if one really insists) does what the OP is asking for with a lot less pain.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.