0

I am currently working on a small script designed for tape backups, and I need the script to search for any tape drive linked to the system. I managed to get this part working, and we can see bellow the path of the two tape drives :

lsscsi --transport | grep fc: | grep tape | awk -F" " ' { print $4 } '
/dev/st0
/dev/st1  

Edit - Here's the raw output of the lsscsi commands :

[1:0:0:0] tape fc:0x50014380032a90b8,0x000001 /dev/st0 
[2:0:0:0] tape fc:0x50014380032a90bb,0x000001 /dev/st1  

My wish is simply to find a way to redirect each line of the previous output in a new variable ($drive0=/dev/st0, $drive1=/dev/st1, etc). My first attempt looked like this, and isn't working because every lines are dumped into the same variable called $path_tape:

#!/bin/bash
/usr/bin/lsscsi --transport | grep fc: | grep tape | while read line ; do
    path_tape=$(echo $line | awk -F" " ' { print $4 } ')
done

Now the thing is, there is this thread that mention a way to increment variables names, so I tried to adapt my code to this solution, resulting in the following lines :

#!/bin/bash
i=0
/usr/bin/lsscsi --transport | grep fc: | grep tape | while read line ; do
    var="path_tape$i"
    ${!var}=$(echo $line |awk -F" " ' { print $4 } ')
    ((i++))
done

But when executed, I now get a syntax error :

./test.sh: line 7: =/dev/st0: No such file or directory
./test.sh: line 7: =/dev/st1: No such file or directory

What am I missing here ?

Yves C.
  • 3
  • 4

2 Answers2

0

Rather than creating variable names with a numerical suffix to "simulate" an array, you can just use an array in bash.

I think that the best way to do what you want is to use mapfile, which turns lines of input into an array. Another name for mapfile is readarray, you can use either one:

mapfile -t path_tape < <(lsscsi --transport | awk -F" " '/tape fc:/ { print $4 }')
readarray -t path_tape < <(lsscsi --transport | awk -F" " '/tape fc:/ { print $4 }')

The -t option strips the newlines from the array elements. Since we don't have a file, but instead the output of a command, a <(process substitution) is used as the input.

Now path_tape is an array, whose elements can be accessed like "${path_tape[i]}", where i is a number.

As a bonus I shortened your pipeline by combining the grep and awk commands. The structure of an awk script is condition { action }, where the condition can be a regular expression match, as in this example.

Testing it out, replacing your command output with a file:

$ cat file
[1:0:0:0] tape fc:0x50014380032a90b8,0x000001 /dev/st0 
[2:0:0:0] tape fc:0x50014380032a90bb,0x000001 /dev/st1  
$ mapfile -t path_tape < <(cat file | awk -F" " '/tape fc:/ { print $4 }')
$ printf '%s\n' "${path_tape[@]}"
/dev/st0
/dev/st1
Tom Fenech
  • 72,334
  • 12
  • 107
  • 141
0

How about this?

This will be available as the environment variables path_tape0 and path_tape1 in the shell session

#!/bin/bash
i=0;
/usr/bin/lsscsi --transport | grep fc: | grep tape | while read line ; 
  do
      export "path_tape$i"=$(echo $line |awk ' { print $4 } '); env | grep path_tape ;((i++));
  done
Eby Jacob
  • 1,418
  • 1
  • 10
  • 28
  • Exporting the variables to env might come in handy for the rest of the script, thanks ! – Yves C. May 23 '18 at 14:31
  • I already have, but sadly my reputation is to low to change the publicly displayed score. Have no doubt that your answer was appreciated ! – Yves C. May 23 '18 at 14:43