0

I would like replace below input about storage disk to an output in below presented format: Below script is almost working for me. But it doesn't work for T0. It seems is there problem with proper read 0s at the end of number in 'replaceTier' function.

Could you somebody assist me to correct it? Thanks in advance.

**INPUT IN FILE:**
displayName=00:19:78
sizeInKB=26214720
dpPoolID=1
displayName=00:FE:B0
sizeInKB=2251536384
dpPoolID=110
displayName=00:FE:B1
sizeInKB=2251536384
dpPoolID=110



**EXPECTED OUTPUT:**
1978,T1
FEB0,T0
FEB1,T0



replaceTier=(
    {1,11,12,13,14,15,16,17,18,19,51,61,71,81,100}:T1
    {2,21,22,23,24}:T2
    3:T3
    {10,110}:T0
    90:SVC_T1
    91:SVC_T2
    92:SVC_T1
)
#
while read -r name serial model uid
 do
  cat "$DIR"/"$name"_disks.log | grep -v 'sizeInKB' | cut -d "=" -f2 | sed 's/\://g' | xargs -n2 | sed 's/\ /\,/g' | cut -c 3- | grep -v ',-1' > "$DIR"/"$name"_output.log
  for row in "${replaceTier[@]}"; do
    original="$(echo $row | cut -d: -f1)";
    new="$(echo $row | cut -d: -f2)";
    sed -i -e "s/,${original}.*/,${new}/g" "$DIR"/"$name"_output.log;
  done
done < /storage/logs/HDSlist.txt
3
  • Remove the .*. With the .* a number like ,110 will match ,1.* which will get translated to T1. Alternatively, order replaceTier such that longer prefixes (like 110) appear before shorter ones (like 11 and 1.)
    – filbranden
    Jun 21, 2019 at 12:44
  • Hi, Yes I tested this before but, will generate something like that: 0366,T10 0367,T10 0368,T16 0369,T1
    – Mac
    Jun 21, 2019 at 13:18
  • Try to anchor at the end of the line then, with $, that way it will only replace when it's an exact match. Reordering it so longer prefixes are substituted first would also have helped.
    – filbranden
    Jun 21, 2019 at 13:27

2 Answers 2

0

Thanks. You're right. The '.*' should be replaced by '$'. Now it's works as should. Also there I found needed information as well.

  sed -i -e "s/,${original}$/,${new}/g" "$DIR"/"$name"_output.log;

Thanks for your efforts.

0

The problem is with the wildcard at the end of your regexp:

s/,${original}.*/,${new}/g

The .* will make a 110 match 11 or 1, which come first in your replaceTier table, so 110 will translate to T1 (from the 1:T1 mapping) rather than T0.

Instead, you want to do a full match on the number to translate, so drop the .* and add an $ anchor (matching the end of the line) to make sure only the exact number will match:

s/,${original}\$/,${new}/g

By the way, I think your parsing of the log file is way too complex as it is. This is how I'd do it, assuming you're using bash for the script:

while read -r displayName; do
  # Read two more rows of data from the
  # input file.
  read -r sizeInKB   # ignored
  read -r dpPoolID
  # Process displayName, remove everything
  # up to the first ":", then drop the
  # remaining ":" in the middle.
  displayName=${displayName#*:}
  displayName=${displayName/:/}
  # Look up dpPoolID in replaceTier.
  tier=""
  for row in "${replaceTier[@]}"; do
    # Split row into id:tier.
    id=${row%%:*}
    if [[ "$dpPoolID" = "$id" ]]; then
      tier=${row#*:}
      break
    fi
  done
  if [[ -n "$tier" ]]; then
    # Only print when tier was found, so skip
    # invalid dpPoolIDs such as -1, etc.
    printf "%s,%s\n" "$displayName" "$tier"
  fi
done <"$DIR"/"$name"_disks.log >"$DIR"/"$name"_output.log

Not only this avoids multiple reads and writes of the same file, it also uses only bash built-ins (no grep, sed, cut, xargs) so a lot of the overhead in spawning external processes is gone too.

But even in this one there's room for improvement, by using an associative array for replaceTier, in which case you could do a direct lookup rather than fully traverse it on every row.

You could define such an associative array with:

replaceTier=(
    [1]=T1
    [12]=T1
    [13]=T1
    [14]=T1
    ...
    [100]=T1
    [2]=T2
    [21]=T2
    ...
    [24]=T2
    [3]=T3
    [10]=T0
    [110]=T0
    [90]=SVC_T1
    [91]=SVC_T2
    [92]=SVC_T1
)

In which case that innermost for loop could be simplified to a straight lookup:

tier=${replaceTier[$id]}

This would also be more efficient, especially if the size of your replaceTier table grows larger.

3
  • Looks cool! Many thanks for assist.
    – Mac
    Jun 21, 2019 at 14:07
  • Maybe I will have one more question. How about number range in array entry? for example [11-19]=T1 or [20-29]=T2 ???
    – Mac
    Jun 21, 2019 at 14:27
  • @Mac You can't set a range of indexes on assignment of an associative array, at least as far as I know... You could use a for loop, but unless you have a very large number of indexes on a range, I don't think that'd be a gain... (for i in {11..19}; do replaceTier[$i]=T1; done would do the job.)
    – filbranden
    Jun 21, 2019 at 15:53

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .