sed仅替换特定文件的第一个前导空格匹配-处理仅CR的行尾

天体之路

编者注:后来为了事后见识,
对标题进行了修改存在两个明显的问题:(a)事实证明输入文件具有-only(仅CR)行尾(经典的Mac OS风格)(b)尝试使用在正则表达式中失败,因为BSD Sed(使用在OSX上)不支持此类转义。
\r
\t\rsed

我正在开发一个使用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
mklement0

tl; dr

下面更新输入文件中的解决方案中没有到位; 独立sed命令可以适应-i ''此要求;awk解决方案需要先保存到不同的文件。

  • OP的输入似乎是具有经典Mac OS\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,由于正则表达式锚定在输入(^的开头,因此该选项已被删除,因为它毫无意义
  • 有关GNU和BSD Sed之间差异的摘要,请参见我的答案

正如@alvits在注释中指出的那样,输入文件实际上可能具有\r实例,而不是\nSed分隔行所需实例

即,文件可能具有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 }'
  • 不使用-FFS在脚本中既没有设置也没有设置输入字段分隔符)意味着Awk通过运行空白(空格,制表符,换行符)将输入记录分为多个字段。

  • $1=$1是虚拟分配,其目的是触发输入线的重建,每当将字段变量分配给该虚拟分配时就会发生。
    通过使用OFS,输出字段分隔符(默认为单个空格)将字段连接在一起来重建该行
    实际上,由此修剪了前导和尾随的空白,并且将行内空白的每个游程归一化为单个空间。


如果您确实坚持使用sed1-即使这意味着一次读取整个文件:

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] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

sed仅替换行中的第一个和最后一个空格

来自分类Dev

使用“ sed”仅替换第一个和最后一个匹配项

来自分类Dev

sed仅删除第一个模式匹配

来自分类Dev

仅删除sed的第一个匹配项

来自分类Dev

仅替换第一个

来自分类Dev

仅替换字符串中的第一个空格

来自分类Dev

sed仅打印该行的第一个模式匹配

来自分类Dev

sed:引号之间的匹配,仅第一个实例

来自分类Dev

如何仅使用.Net Regex.Replace替换第一个匹配项

来自分类Dev

Javascript正则表达式仅替换第一个匹配项

来自分类Dev

Preg_replace仅替换第一个匹配项

来自分类Dev

使用preg_replace仅替换第一个匹配项

来自分类Dev

如何仅使用.Net Regex.Replace替换第一个匹配项

来自分类Dev

仅替换匹配模式的第一个字符

来自分类Dev

jQuery .each()仅替换第一个找到的

来自分类Dev

仅替换角色的第一个实例

来自分类Dev

仅匹配第一个出现的数字

来自分类Dev

仅匹配词组的第一个出现

来自分类Dev

仅子查询第一个匹配项

来自分类Dev

NSPredicate仅查找第一个匹配项

来自分类Dev

HtmlAgilityPack仅返回第一个匹配项

来自分类Dev

仅打印第一个匹配项

来自分类Dev

仅匹配词组的第一个出现

来自分类Dev

如何使用sed仅删除文件中第一个出现的行

来自分类Dev

使用grep仅获取每个文件中的第一个匹配项

来自分类Dev

ftp'put file * .txt'的行为-仅上传第一个匹配的文件

来自分类Dev

正则表达式仅替换每个匹配项的第一个匹配项

来自分类Dev

AWK单行替换仅匹配第一个匹配的正则表达式的文本

来自分类Dev

什么是仅匹配空格后的第一个字符的Python Regex模式?