将回车 return (\r) 转换为实际覆盖

Convert carriage return (\r) to actual overwrite

问题

有没有办法将回车 returns 转换为字符串中的实际覆盖,以便 000000000000\r1010 转换为 101000000000

上下文

1。初始objective:

有一个以 10 为底的数字 x(介于 0 和 255 之间),我想将这个数字转换为以 2 为底的数字,添加尾随零以获得 12 位长二进制表示,生成 12 个不同的数字(它们中的每一个都由基数 2 中的最后 n 数字组成,n 在 1 和 12 之间)并打印这 12 个数字的基数 10 表示。

2。示例:

  1. x = 10
  2. 基数 2 是 1010
  3. 尾随零 101000000000
  4. 提取12个"leading"号码:110101101010100101000 , ...
  5. 转换为 10 进制:125102040、...

3。我做了什么(它不起作用):

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

4。为什么它不起作用

因为变量x_base2_padded包含了整个序列000000000000\r1010。例如,这可以使用 hexdump 来确认。在 for 循环中,当我提取前 12 个字符时,我只得到零。

5。备选方案

我知道我可以通过向变量添加零来找到替代方案,如下所示:

x_base2=1010
x_base2_padded="$(printf '%s%0.*d' "${x_base2}" $((12-${#x_base2})) 0)"

或者使用 printfrev

用零填充
x_base2=1010
x_base2_padded="$(printf '%012s' "$(printf "${x_base2}" | rev)" | rev)"

虽然这些替代方案现在解决了我的问题并让我继续我的工作,但它并没有真正回答我的问题。

相关问题

在不同的上下文中可能会观察到相同的问题。例如,如果一个人试图连接多个包含回车 returns 的字符串。结果可能很难预测。

str=$'bar\rfoo'
echo "${str}"
echo "${str}${str}"
echo "${str}${str}${str}"
echo "${str}${str}${str}${str}"
echo "${str}${str}${str}${str}${str}"

第一个echo会输出foo。虽然你可能期望其他 echo 输出 foofoofoo...,但它们都输出 foobar.

回答具体问题,如何将000000000000\r1010转换为101000000000,参考

但是,我不会首先引入马车return,而是这样解决问题:

#!/usr/bin/env bash

x=

# Start with 12 zeroes
var='000000000000'

# Convert input to binary
binary=$(bc <<< "obase = 2; $x")

# Rightpad with zeroes: ${#binary} is the number of characters in $binary,
# and ${var:x} removes the first x characters from $var
var=$binary${var:${#binary}}

# Print 12 substrings, convert to decimal: ${var:0:i} extracts the first
# i characters from $var, and $((x#$var)) interprets $var in base x
for ((i = 1; i <= ${#var}; ++i)); do
    echo "$((2#${var:0:i}))"
done

以下函数 overwrite 转换其参数,以便在每次回车后 return \r 字符串的开头实际上被覆盖:

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

请注意,打印的字符串实际上是 xy23ef,不像 echo $'abcdef\r0123\rxy' 那样 似乎 打印相同的字符串,但仍然打印 \r 然后由您的终端解释结果 看起来 相同。您可以通过 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

函数 overwrite 还支持通过参数覆盖而不是 \r 分隔的段:

$ overwrite abcdef 0123 xy
xy23ef

要就地转换变量,请使用子 shell:myvar=$(overwrite "$myvar")

使用 awk,您可以将字段分隔符设置为 \r 并遍历字段,只打印它们的可见部分。

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 ""
}'

这确实太长了,无法放入命令替换。所以你最好将它包装在一个函数中,并将要解析的行传递给它。