与 '\r' 字符的两次交锋

原创 2016年06月01日 00:51:35

背景

众所周知,不同的操作系统中,对文本中换行的表示是不同的,在 windows 中用 \r\n 表示,在 Linux 中用 \n 表示,而在 Mac OS 中用 \r 表示。对于像笔者这种以 Windows 作为开发环境,Linux 作为执行环境的程序员来说,经常会不可避免地碰到由换行符不一致而引起到奇怪问题,下面就回溯一下我曾遇到并解决的两个相关的问题,希望能给读者来一点启发。

奇怪的脚本

这是同事遇到的问题,其当时在开发一个 shell 脚本,需要一个从文件中查找关键字的功能,比如文件名叫 data.txt,要查找到关键字是 warn,很容易写出这样的命令:

grep warn data.txt

假如文件的内容是:

warn: wrong
info: good
warn: not good

那么该命令因该返回两行:

warn: wrong
warn: not good

到这里,一切都正常,但是这位同事不满足于只将结果打印到标准输出,而是要用一个变量存储,以便后续的操作,于是写下了这样两行脚本:

result=$(grep warn data.txt)
echo $result

这时奇怪的事发生了,执行这个脚本得到的结果是:

_warn: not good  # 注意 'warn' 前面有一个空格,为了醒目,用 '_' 表示

相信读者已经猜到,导致这个现象的原因,就是 \r 字符。下面解释一下原因。
这个文件的出处现已无从考证,但其中每行都是以 \r\n 结束, 推测应该是从 Windows 平台拷贝过来的。但是在 Linux 中,只有 \n 被作为换行符,其前面的 \r 字符将被视为普通的文本内容处理,所以 grep 命令返回到结果应该为:

warn: wrong\r
warn: not good\r

在向屏幕打印这段内容时,\r 字符会被转义为回车符,其作用是将光标即下一个字符的输出位置移动到本行行首(Windows 上的 \r\n 可以理解为先将输出位置移动到行首,再用 \n 移动到下一行),但由于 \r 后面再无其它字符,所以对用户来说,仅光标位置的移动是感知不到的。
但在脚本中,情况有了些许的变化—— grep 的结果不直接打印,而是先赋值给变量,再打印变量。区别在于脚本中会将 result 中的内容按行分割,再依次传给 echo ,实际执行的命令为:

echo warn: wrong\r warn: not good\r

上面已经说过,\r 字符会使光标回到行首,所以第一个 \r 字符前的内容会被后面的内容覆盖,这便是问题所在。
其实只要将输出命令写成:

echo "$result"

而不是

echo $result

就不会有这个问题,因为双引号中的内容会被当作纯粹的字符串处理,不做转义,实际的命令为:

echo "warn: wrong\r
warn: not good\r"

内容分为两行打印,和直接用 grep 输出的结果一样。

链不到的库

这次到问题出现在另一同事的脚本中,其内容只有两行编译命令,被打在一个 rar 包中发给我。两行命令的内容都查不多,但命令的内容无关紧要,假设为:

g++ -Wall source.cpp -I...-L... -o source -lxxx

其中的 xxx 是一个本项目的动态库。我直接在本地执行该脚本,但链接失败,报 ‘xxx’ 库找不到。
第一反应当然是库所在的目录没有被加到动态库的搜索路径中,但检查了一遍发现不是。又怀疑命令写的有问题、动态库文件没有读权限、文件名写错,但都验证通过了。折磨了有半个小时,突然灵光一现,用 dos2unix 命令转换了一下编译脚本,随即编译通过。
原因很简单,这个脚本中每一行的末尾都有一个 \r,恰好跟在 ‘xxx’ 后面,变成了 ‘xxx\r’,链接时实际查找的是 ‘xxx\r’ 文件,当然找不到了。但由于打印到屏幕上的内容是一样到,所以很难发现。

经验教训

  • 如果环境不能一致,起码尽量做到配置和工具一致,许多 Windows 上到编辑器都支持将默认换行符都配置为 \n,Linux 上也有 zip、gzip 等压缩工具
  • 不可轻信外来的代码和数据,要做校验
  • shell 编程中,如果变量的内容是纯粹的数据,就用引号括起来
版权声明:本文为博主原创文章,未经博主允许不得转载。

统计第一个只出现两次的字符

方法一:在一个一维数组里面查询: 最直接的方法是从头到尾遍历这个字符数组的字符,当访问这个字符时,拿这个字符和后面的每个字符匹配,如果下次又找到一个字符再次出现,就表明是出现两次的字符,如果字符与...

hdoj 3746 Cyclic Nacklace 【kmp 求至少在结尾添加多少个字符可以凑够至少两次循环】

Cyclic Nacklace Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)...

找出字符串中第一个只出现两次的字符

题目:找出字符串中第一个只出现两次的字符,要求时间复杂度是O(n),空间复杂度是O(1)       拿到这个问题,我们的第一种最容易想到的解法就是,拿每一个字符去和它之外的字符串中的每一个字符进行...

寻找一个字符串中第一个只出现两次的字符

2.【附加题】–查找一个字符串中第一个只出现两次的字符。 比如:“abcdefabcdefabc”中第一个只出现两次为‘d’,要求时间复杂度为O(N),空间复杂度为O(1) 解法: 利用哈希表;由...

HDU2548 两军交锋【水题】

题目大意:和小学时候的题目差不多。相距l米的A、B要打仗了。A以u米/秒的速度前进,B 以v米/秒的速度前进,A的侦察兵从A出发以w米/秒的速度在两军之间往返。问:两军交战的 时候,A的侦察兵走了多远...

“二马”交锋下一站:线下伙伴和大数据

游戏、电商、O2O、社交、移动支付……阿里巴巴和腾讯前所未有的产业PK丝毫没有停止的迹象,无论是业务创新还是投资并购,两家巨头互不相让。曾经“电商霸主”和“社交寡头”的定位再也无法体现两家公司日趋模糊...

阿里京东创业者交锋,赋能会是便利店竞争下一程吗?

让阿里、京东及叮大哥这些“醉翁”争相对便利店献殷勤,其想象空间除了占领便利店进行线下零售扩张的“酒”(这也是舆论的普遍关注),更有利用便利店的特殊地位谋求线下零售扩张之外的“山水之间”。...

沉痛记下这次与 mysql 的交锋,以全面溃败结束

最近在做一个博客系统,后台使用 ssh 框架,前台使用 bootstrap ,因为不是太熟悉,所以遇到不少问题,但看到成果一步步做出来,喜悦之情溢于言表! 然而今天,遇到了一个让我崩溃的问题,战斗了...

【字符串】查找一个字符串中第一个只出现两次的字符。比如:“abcdefabcdefabc”中第一个只出现两次为‘d’,要求时间复杂度为O(N),空间复杂度为O(1)

问题描述:查找一个字符串中第一个只出现两次的字符。比如:“abcdefabcdefabc”中第一个只出现两次为‘d’,要求时间复杂度为O(N),空间复杂度为O(1)。分析问题:方法1、O(N^2) ...

黑客们很喜欢骇客交锋,虽然本片不被影评人认可

黑客们很喜欢骇客交锋,虽然本片不被影评人认可。   虽然本片不被影评人认可,黑客们却很喜欢骇客交锋。可是,当谷歌 Chrome团队的主管 Parisa Tabriz组建了200 多名平静专家旁观了首...
  • bobttda
  • bobttda
  • 2016年03月18日 03:25
  • 433
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:与 '\r' 字符的两次交锋
举报原因:
原因补充:

(最多只允许输入30个字)