编者注:后来为了事后见识,
对标题进行了修改;存在两个明显的问题:(a)事实证明输入文件具有-only(仅CR)行尾(经典的Mac OS风格)(b)尝试使用且在正则表达式中失败,因为BSD Sed(使用在OSX上)不支持此类转义。\r
\t
\r
sed
我正在开发一个使用Python在文本文件中查找和替换某些单词的Automator程序。该程序使用字典,并且在某些情况下用作替换值的实例是''
(意思是,什么都没有)。我不认为该程序会导致此问题,但我只是通过上下文提及此问题。(我认为问题出在sed
,所以我不愿意标记Python。)
文件中的某些行具有前导空格,这些空格是在文件开头的某些单词被替换为空后无意中创建的。我想摆脱它们,sed
在这种情况下,我认为这是完成这项工作的最佳工具。
假设这是文本文件的外观:
Display
Display
BOX,
因此,我通过sed
使用以下命令运行编辑后的文件:
sed -e 's/^[ \t]*//g'
结果如下:
Display
Display
BOX,
仅第一个匹配项被编辑。为什么?
通过测试,我创建了一个全新的纯文本文件,如下所示:
hello
hello
hello
然后我在上面运行了命令。这实际上按预期工作。为什么?
Python程序是否有可能正在使用某种其他形式的空间(不可打印的字符?)?但是,为什么sed
至少要工作一次?
顺便说一句,我愿意接受与OS X兼容的另一种便携式解决方案或工具,用于修剪纯文本文件中每一行的前导空白。
编辑:这是文件的一些xxd
输出(用X替换了大多数实际内容):
0000000: 2044 6973 706c 6179 2043 616c 6962 7261 X X
0000010: 7469 6f6e 2046 6978 7475 7265 2046 4952 X X X
0000020: 4d57 4152 4520 4b49 545e 4d20 4469 7370 X X^M X
0000030: 6c61 7920 4361 6c69 6272 6174 696f 6e20 X X
0000040: 4669 7874 7572 6520 524d 6163 426f 6f6b X X
0000050: 2041 6972 2028 3131 2d69 6e63 682c 204d X X
0000060: 6964 2032 3031 3229 2050 4f52 5420 4b49 X X) X X
0000070: 545e 4d42 4f58 2c20 5245 434f 5645 5259 T^MBOX, X
tl; dr
下面更新输入文件中的解决方案中没有到位; 独立sed
命令可以适应-i ''
此要求;该awk
解决方案需要先保存到不同的文件。
\r
的文件-仅换行符谢谢@alvits。。sed
总是读取整个这样的文件,这通常是不希望的,并且会妨碍OP的行领先的空白修剪方法。awk
因此是更好的选择,因为它允许指定组成换行符的内容(通过所谓的输入记录分隔符):更新:awk
用峰值解决方案改编的更简单,更快速的替代方法替换了原始命令:
awk -v RS='\r' '{ sub(/^[ \t]+/, ""); print }'
如果也可以修剪每行的尾随空格(如果有的话),并将行中的单词之间的空格归一化为每个空格,则可以简化为:
awk -v RS='\r' '{ $1=$1; print }'
注意\n
,如通常所希望的那样,输出线将被分隔。有关说明和背景信息(包括如何保存\r
为换行符),请继续阅读。
注意:答案的第一部分通常适用,但假设输入的\n
行以-结束;OP的特殊情况(其中的行显然\r
仅是端接的)在第二部分中处理。
在OSX上使用的BSD Sed仅支持\n
作为控制字符转义序列。因此,\t
用于匹配制表符。不支持。
要仍然匹配制表符,可以拼接ANSI C引号引起来的字符串,以产生实际的制表符。到您的Sed脚本($'\t'
)中:
sed 's/^[ '$'\t'']*//'
在这种简单情况下,您可以在整个Sed脚本(sed -e $'s/^[ \t]*//'
)中使用ANSI C引号引起来的字符串,但是使用更复杂的脚本可能会比较棘手,因为此类字符串具有自己的转义规则。
g
,由于正则表达式锚定在输入(^
)的开头,因此该选项已被删除,因为它毫无意义。正如@alvits在注释中指出的那样,输入文件实际上可能具有\r
实例,而不是\n
Sed分隔行所需的实例。
即,文件可能具有OSX之前的Mac OS行终止符:a\r
by itself
终止行。
一种简单的验证方法是将输入文件传递给cat -et
:\r
实例显示为^M
,而\n
实例显示为$
(此外,\t
实例显示为^I
)。
如果输出中只有^M
实例但没有$
实例,则表示行不以\n
(也)终止,并且整个输入文件被视为单个字符串,这说明了为什么只处理第一个输入“行” :^
唯一匹配整个字符串的开头。
由于Sed解决方案(无需预处理)会使整个文件作为一个整体读取,因此awk
是更好的选择:
\n
按照类似Unix的平台上的惯例创建分隔输出:
awk -v RS='\r' '{ sub(/^[ \t]+/, ""); print }'
-v RS='\r'
告诉Awk按\r
实例将输入拆分为记录(特殊变量RS
包含输入记录分隔符)。
sub(/^[ \t]+/, "")
搜索^[ \t]+
输入行上第一次出现的regex并将其替换为""
,即,它有效地修剪了来自每个输入行的前导空格和制表符。请注意,sub()
如果没有显式的第3个参数隐式地对$0
整个输入行进行运算。
print
然后打印可能修改过的修改后的输入行。
由于\n
是Awk的默认输出记录分隔符(OFS
),所以输出记录将被\n
终止。
如果您真的想保留\r
为行分隔符:
awk 'BEGIN { RS=ORS="\r" } { sub(/^[ \t]+/, ""); print }'
RS=ORS="\r"
将输入和输出记录分隔符都设置为\r
。如果也可以从每行中修剪尾随空格(如果有的话),并将行中的单词之间的空格归一化为每个空格,则可以将\n
-terminated简化为:
awk -v RS='\r' '{ $1=$1; print }'
不使用-F
(FS
在脚本中既没有设置,也没有设置输入字段分隔符)意味着Awk通过运行空白(空格,制表符,换行符)将输入记录分为多个字段。
$1=$1
是虚拟分配,其目的是触发输入线的重建,每当将字段变量分配给该虚拟分配时就会发生。
通过使用OFS
,输出字段分隔符(默认为单个空格)将字段连接在一起来重建该行。
实际上,由此修剪了前导和尾随的空白,并且将行内空白的每个游程归一化为单个空间。
如果您确实坚持使用sed
1-即使这意味着一次读取整个文件:
sed $'s/^[ \t]*//; s/\r[ \t]*/\\\n/g' # note the $'...' to make \t, \r, \n work
\n
按照Unix的惯例,这将输出终止行。
相比之下,如果要保留\r
为行分隔符,请使用以下命令-但请注意,BSD Sed将始终\n
在末尾添加a。
sed $'s/^[ \t]*//; s/\r[ \t]*/\r/g'
[1]峰的回答最初表现出务实的多-utility替代更清楚:更换所有\r
与实例\n
使用情况tr
,并管结果到原来的BSD-SID的版本sed
命令:
tr '\r' '\n' file | sed $'s/^[ \t]*//'
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句