0

I am reading a file that basically contains a list of binary data (fixed at 8 bits wide per line):

01011100
11110001
...

For each line that is read, I need to "remap" the bits in chunks of 4 bits to specific positions. So for example in the 1st line above, bits 1100 and 0101 will each be remapped that will follow this formula: bit 0 goes to bit 3 position, bit 1 to 2, bit 3 to 1, and lastly bit 2 to 0.

To do this, I coded as follows:

function remap {
    echo "Remapper";
    IFS=
    read -ra din <<< $1;
    echo ${#din};
    echo ${din[1]};
    ## above line is just displaying blank as seen in below result
    echo ${din[*]};
    ## do actual remapping here
};

for line in `cat $infile`;
do 
    data0=${line:0:4};
    data1=${line:4:4};
    echo "Read line";
    echo $data0;
    echo $data1;
    remap $data0;
    remap $data1;
done

I don't know why I'm not seeing the echoed array element. This is the output from the 1st read line:

Read line
0101
1100
Remapper
4

0101
Remapper
4

1100

I haven't gotten to coding the actual remapping itself because I couldn't even verify that I'm able to properly split the $1 variable of remap() function into the din array.

Thank you in advance for the help!

renvill
  • 143
  • 1
  • 10

2 Answers2

2

Unlike other languages, setting IFS to empty string does not split a string into each character array. Instead you can use fold -w1 to add a newline after each character:

remap() {
    echo "Remapper"
    mapfile -t din < <(fold -w1 <<< "$1")
    echo "${din[3]}${din[2]}${din[0]}${din[1]}"
}

As it will be inefficient to invoke fold command every time, it may be better to say:

remap() {
    echo "Remapper"
    echo "${1:3:1}${1:2:1}${1:0:1}${1:1:1}"
}

As a side note, you don't need to append semicolon after each command.

tshiono
  • 21,248
  • 2
  • 14
  • 22
1

There are a number of confusions here. The biggest is that read -ra din is not splitting the string into characters. read will split its input into words delimited by the characters in IFS; normally that's whitespace, but since you set IFS to the empty string, there are no delimiters and the string won't be split at all. Anyway, you don't want to split it based on delimiters, you want to split it into characters, so read is the wrong tool.

Another source of confusion is that ${#din} isn't the length of the array, it's the length (in characters) of the first element of the array. ${#din[@]} would get the number of elements in the array, and in this case it'd be 1. More generally, declare -p din would be a better way to see what din is and what it contains; here, it'd print something like declare -a din='([0]="0101")', showing that it's an array with a single four-character element, numbered 0.

What I'd do here is skip trying to split the characters into array elements entirely, and just index them as characters in $1 -- that is, ${1:0:1} will get the first character (character #0) from $1, ${1:1:1} will get the second (#1), etc. So to print the bits in the order third, first, second, fourth, you'd use:

echo "${1:2:1}${1:0:1}${1:1:1}${1:3:1}"

Other recommendations: It's best to double-quote variable expansions (like I did above) to prevent weird parsing problems. for var in $(cat somefile) is a fragile way to read lines from a file; while read var; do ... done <somefile is generally better. I'd recommend remap() { instead of the nonstandard function remap { syntax, and semicolons are redundant at the end of lines (well... with a few weird exceptions). shellcheck.net will point most of these out, and is a good tool to sanity-check your scripts for common mistakes.

Gordon Davisson
  • 118,432
  • 16
  • 123
  • 151