Is there a way to convert the carriage returns to actual overwrite in a string so that 000000000000\r1010
is transformed to 101000000000
?
Having a number x
(between 0 and 255) in base 10, I want to convert this number in base 2, add trailing zeros to get a 12-digits long binary representation, generate 12 different numbers (each of them made of the last n
digits in base 2, with n
between 1 and 12) and print the base 10 representation of these 12 numbers.
x = 10
1010
101000000000
1
, 10
, 101
, 1010
, 10100
, 101000
, ...1
, 2
, 5
, 10
, 20
, 40
, ...x=10
x_base2="$(echo "obase=2;ibase=10;${x}" | bc)"
x_base2_padded="$(printf '%012d\r%s' 0 "${x_base2}")"
for i in {1..12}
do
t=$(echo ${x_base2_padded:0:${i}})
echo "obase=10;ibase=2;${t}" | bc
done
Because the variable x_base2_padded
contains the whole sequence 000000000000\r1010
. This can be confirmed using hexdump
for instance. In the for loop, when I extract the first 12 characters, I only get zeros.
I know I can find alternative by literally adding zeros to the variable as follow:
x_base2=1010
x_base2_padded="$(printf '%s%0.*d' "${x_base2}" $((12-${#x_base2})) 0)"
Or by padding with zeros using printf
and rev
x_base2=1010
x_base2_padded="$(printf '%012s' "$(printf "${x_base2}" | rev)" | rev)"
Although these alternatives solve my problem now and let me continue my work, it does not really answer my question.
The same problem may be observed in different contexts. For instance if one tries to concatenate multiple strings containing carriage returns. The result may be hard to predict.
str=$'bar\rfoo'
echo "${str}"
echo "${str}${str}"
echo "${str}${str}${str}"
echo "${str}${str}${str}${str}"
echo "${str}${str}${str}${str}${str}"
The first echo
will output foo
. Although you might expect the other echo
to output foofoofoo...
, they all output foobar
.
The following function overwrite
transforms its argument such that after each carriage return \r
the beginning of the string is actually overwritten:
overwrite() {
local segment result=
while IFS= read -rd $'\r' segment; do
result="$segment${result:${#segment}}"
done < <(printf '%s\r' "$@")
printf %s "$result"
}
$ overwrite $'abcdef\r0123\rxy'
xy23ef
Note that the printed string is actually xy23ef
, unlike echo $'abcdef\r0123\rxy'
which only seems to print the same string, but still prints \r
which is then interpreted by your terminal such that the result looks the same. You can confirm this with hexdump
:
$ echo $'abcdef\r0123\rxy' | hexdump -c
0000000 a b c d e f \r 0 1 2 3 \r x y \n
000000f
$ overwrite $'abcdef\r0123\rxy' | hexdump -c
0000000 x y 2 3 e f
0000006
The function overwrite
also supports overwriting by arguments instead of \r
-delimited segments:
$ overwrite abcdef 0123 xy
xy23ef
To convert variables in-place, use a subshell: myvar=$(overwrite "$myvar")
With awk, you'd set the field delimiter to \r
and iterate through fields printing only the visible portions of them.
awk -F'\r' '{
offset = 1
for (i=NF; i>0; i--) {
if (offset <= length($i)) {
printf "%s", substr($i, offset)
offset = length($i) + 1
}
}
print ""
}'
This is indeed too long to put into a command substitution. So you better wrap this in a function, and pipe the lines to be resolved to that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With