Bash:无法将字符串附加到现有字符串 - 它似乎会覆盖该字符串的开头

thi*_*not 4 string bash line-endings

这是一个帮助我们创建 dhcpd.conf 文件的脚本。

示例输入(例如 mac-tab-;-tab-IP)

DC:D3:21:75:61:90   ;   10.25.131.17
    ;   
Run Code Online (Sandbox Code Playgroud)

预期产出

Host 27-48 { hardware ethernet DC:D3:21:75:61:90 ; fixed-address 10.25.131.17 ; }

#host 27-48 { hardware ethernet ; fixed-address ; }
Run Code Online (Sandbox Code Playgroud)

目前输出的行是这样的:

Host 27-48 { hardware ethernet 00:16:6B:C8:3D:C9 ; fixed-address 10.25.129.185
Run Code Online (Sandbox Code Playgroud)

我被困在代码中的特定行

outputLine="Host $((names[i]))-$((startingNumber+counter)) { hardware ethernet $first ; fixed-address $second"
Run Code Online (Sandbox Code Playgroud)

如果我尝试添加 ; }

outputLine="Host $((names[i]))-$((startingNumber+counter)) { hardware ethernet $first ; fixed-address $second ; }"
Run Code Online (Sandbox Code Playgroud)

我明白了:

; } 27-48 { hardware ethernet 00:16:6B:C8:3D:C9 ; fixed-address 10.25.129.185
Run Code Online (Sandbox Code Playgroud)

问题是,每当我将“;}”附加到上一行的末尾时,它就会覆盖该行的开头。我尝试了一些技巧来解决这个问题,例如将上面的行写入字符串,然后尝试附加到该字符串,但出现了同样的问题。我有一个想法将整个内容导出到一个文件,然后将文件重新加载到一个数组中,以便我可以追加,但这似乎有点矫枉过正。

for ((j=1; j<=${sizes[i]}; j++ )); do

    #split line, read split as two entries for an arrIN
    IN=(${line[counter+1]})
    arrIN=(${IN//;/ })
    first="${arrIN[0]}"
    second=${arrIN[1]}


    if [ ${lineSize[counter+1]} -gt 5 ]
    then
        #sed 's/$/ ; }/' $outputLine > newoutputLine
        outputLine="Host $((names[i]))-$((startingNumber+counter)) { hardware ethernet $first ; fixed-address $second"
        echo $outputLine
    else
        echo "#host $((names[i])) $((startingNumber+counter)) { hardware ethernet ; fixed-address ; }"
    fi
    counter=$((counter+1))
done
Run Code Online (Sandbox Code Playgroud)

mkl*_*nt0 6

正如ruakh在对该问题的评论中所解释的那样,问题是变量值末尾的CR ( 0xD,\r$second ) 字符,可以通过以下参数扩展将其删除:second="${second//$'\r'/}"

本答案的其余部分解释了症状并提供了背景信息。


问题是,每当我将“;}”附加到上一行的末尾时,它就会覆盖该行的开头。

“覆盖行的开头”几乎总是指向嵌入的CR ( 0xD, \r) 字符,当将字符串打印到终端时,该字符会显示覆盖行的开头的外观:

$ printf 'abc\rd'  # `printf '\r'` produces a CR 
dbc  # It LOOKS LIKE 'd' replaced 'a', but that's an artifact of printing to the terminal.
Run Code Online (Sandbox Code Playgroud)

只是因为终端将 CR ( \r) 解释为“将光标放在行的开头”,d之后的剩余字符串\r似乎“覆盖”了字符串已打印部分的开头。

您可以使用 可视化嵌入的 CR(和其他控制字符)cat -et ,它以脱字符号表示它们^M

$ printf 'abc\rd' | cat -et
abc^Md   # ^M represents a CR (\r)
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,d实际上并没有覆盖 a字符串的开头。


CR ( ) 实例在Unix文本处理\r领域很少使用。如果它们确实作为文本数据的一部分出现,则通常来自使用 CRLF ( ) 行结尾的Windows源,而不是 Unix 仅 LF ( ) 行结尾。\r\n\n

通常,最简单的方法是在通过 Unix 实用程序使用数据之前简单地删除CR( \r) 实例

有很多很多现有的答案涵盖了这个领域,通常建议使用第三方实用程序dos2unix,可以通过许多平台的包管理器轻松安装(例如,sudo apt install dos2unix在 Ubunutu 上,或brew install dos2unix在 macOS 上,通过Homebrew)。

或者,