sh Shell Script Questions

jardows

2[H]4U
Joined
Jun 10, 2015
Messages
2,308
So I want to build a useful shell script, using sh, that will provide a menu to run SMART tests on a hard drive. The intent is to have this on a bootable USB drive and run it on various computers. Since the exact computer disk configuration will be unknown, I will need a way to scan for the drives, then pipe that information into the script to be called upon with the actual test. I am using FreeBSD and the smartmontools package, but my scripting questions aren't really unique to that platform. I'm fairly new to shell scripting, but this is a great opportunity for me to learn and expand my skills.

Right now, I'm stuck on how to get the specific information called into a variable to use with the menu.

On my VM, If I run
Code:
smartctl --scan
I get returned
/dev/cd0 -d atacam # /dev/cd0, ATA device
/dev/da0 -d scsi # dev/da0, SCSI device

Of course, this is far more information than I want to pass on, as the testing commands will only take the "/dev/device" portion.
I figured out that if I have in the script
Code:
smartctl --scan | awk '{print $1}'
I get returned
/dev/cd0
/dev/da0

Ok, this is useful, but how do I pass the results in the script so I can choose just a single line? Since this input will be variable depending on the computer it is used on (my laptop drive is /dev/ada0, my desktop has multiple drives, /dev/nvme0, /dev/nvme1 /dev/ada0) I really can't hardcode the varibles.

Any advice?
 
Code:
my_devices=$(smartctl --scan | awk '{print $1}')

for device in $my_devices; do
  echo "$device"
done
I've been working with this, but not making any headway. The output with the loop is the same as just running the command, and I have found no documentation that allows me to extract the individual values from the loop. So I'm still stuck.
 
I've been working with this, but not making any headway. The output with the loop is the same as just running the command, and I have found no documentation that allows me to extract the individual values from the loop. So I'm still stuck.

I was just showing how you could loop through the devices. Now with each "${device}" you can do a command on it, or check to see if a supplied argument (what device) is a valid device.... etc...

Are you asking me to write your program? (I'm ok with that, but maybe better if you write it?)
 
I've been working with this, but not making any headway. The output with the loop is the same as just running the command, and I have found no documentation that allows me to extract the individual values from the loop. So I'm still stuck.
Basically it is still the same, except the output was stored in a variable, then awk scanned each line and sent the output to echo, one at a time.

If you wanted to process it further, you could do so within the "for" loop. For instance, you could use grep to strip the comments from the string using a regexp and pass the output to another command or write it to a file.
 
I've gotten a working solution for now. I'm using 'cut' instead of 'awk' (same output result) and piping it through 'head' and 'tail'
I can set the variable like this:
Code:
drive1=$(smartctl --scan | cut -w -f 1 | head -n 1 | tail -n 1)
drive2=$(smartctl --scan | cut -w -f 1 | head -n 2 | tail -n 1)

now when I run code
Code:
 echo $drive1
I get:
Code:
/dev/cd0

An obvious problem with this is that it would run "smartctl --scan" everytime the variable is called. Practically, this doesn't seem to be a problem, but it is still inefficient. It might be noticeable on a system with a lot of drives. I imagine I can do something similar (piping through head and tail) within the loop, so I only have to run the command once and have just the values called with the variable.
 
I've gotten a working solution for now. I'm using 'cut' instead of 'awk' (same output result) and piping it through 'head' and 'tail'
I can set the variable like this:
Code:
drive1=$(smartctl --scan | cut -w -f 1 | head -n 1 | tail -n 1)
drive2=$(smartctl --scan | cut -w -f 1 | head -n 2 | tail -n 1)

now when I run code
Code:
 echo $drive1
I get:
Code:
/dev/cd0

An obvious problem with this is that it would run "smartctl --scan" everytime the variable is called. Practically, this doesn't seem to be a problem, but it is still inefficient. It might be noticeable on a system with a lot of drives. I imagine I can do something similar (piping through head and tail) within the loop, so I only have to run the command once and have just the values called with the variable.
I'm not 100% certain, but I'm pretty sure it's just the string output that is stored in the variable, not the command. So if the order changed or drives were added or removed, the output of the variables would remain the same until you change the variable yourself.

You could verify that yourself by running (for example) these commands:
Bash:
$ DIRS=$(ls -lh)
$ echo $DIRS
$ mkdir foo
$ echo $DIRS
If it runs `ls -lh` every time, then the dir "foo" will be included the second time you echo the variable. Otherwise it'll be absent.
 
I'm not 100% certain, but I'm pretty sure it's just the string output that is stored in the variable, not the command. So if the order changed or drives were added or removed, the output of the variables would remain the same until you change the variable yourself.

You could verify that yourself by running (for example) these commands:
Bash:
$ DIRS=$(ls -lh)
$ echo $DIRS
$ mkdir foo
$ echo $DIRS
If it runs `ls -lh` every time, then the dir "foo" will be included the second time you echo the variable. Otherwise it'll be absent.
Thanks. I had a chance to test this, and it does work as you described, so I can proceed with my script as is. Might come up with some better solutions as I learn more, but for now I have what I need.
 
Back
Top