Sed 没有写入文件

Sed is not writing to file

我只想更改 CSV 文件中的分隔符。 该文件来自外部服务器,因此分隔符是这样的:^A.

name^Atype^Avalue^A
john^Ab^A500
mary^Ac^A400
jack^Ad^A200

我想要这个:

name,type,value
john,b,500
mary,c,400
jack,d,200

我需要将其更改为逗号 (,) 或制表符 (,),但我的 sed 命令尽管输出正确,但并未写入文件。

cat -v CSVFILE | sed -i "s/\^A/,/g"

当我使用上面的行时,它正确地输出了用逗号而不是 ^A 分隔的文件,但它没有写入文件。

我也这样试过:

sed -i "s/\^A/,/g" CSVFILE

也不行... 我做错了什么?

如果您的 sed 支持 -i 选项,您可以像这样使用它:

sed -i.bak -e "s/\^A/,/g" CSVFILE

(这里假定源文件中的分隔符由两个字符 ^ 和 A 组成;如果 ^A 应该指的是 Control-A,那么您将不得不进行相应的调整,例如使用 's/\x01/,/g'.)

否则,假设您想保留原始文件的副本(例如,以防结果不是您所期望的——见下文),可以使用如下咒语:

mv CSVFILE CSVFILE.bak  &&  sed "s/\^A/,/g" CSVFILE.bak > CSVFILE

正如别处指出的那样,如果源文件分隔符是 Control-A,您也可以使用 tr '[=14=]1' ,(或 tr '[=15=]1' '\t' 作为制表符)。

需要注意的是,源文件中的定界符可能恰好被使用,因为逗号可能出现在分隔符分隔的“值”中。如果这是可能的,那么将需要一种不同的方法。 (参见 https://www.rfc-editor.org/rfc/rfc4180

如果 运行 在 OS X 下:

  • 添加扩展名到-i以写入新文件:

    sed -i.bak "s/^A/,/g" CSVFILE
    
  • 或写到位:

    sed -i '' "s/^A/,/g" CSVFILE
    
  • 您也可以使用 cat 输出到文件,但在您的 sed 上没有 -i 命令:

    cat -v CSVFILE | sed "s/^A/,/g" > ouput
    

确保你这样写 ^A :

Ctrl+V+Ctrl+A

这是 tr 创建的目的:

tr '<control-A>' ',' < file > tmp && mv tmp file

显然用文字控件-A替换<control-A>

  • 文字 ^A(两个字符,^A)是如何cat -v 可视化控制字符0x1(ASCII码1,命名为SOH(标题开始))。 ^A是一个例子caret notation表示unprintable ASCII字符:

    • ^A 代表键盘组合 Control-A,当其前面有通用转义序列 Control-V,是您可以在终端中创建 actual 控制字符的方法;换句话说,
      Control-VControl-A 将插入一个实际的 0x1 字符。

    • 顺带一提,caret notation(^<letter>)的逻辑是:字母对应所表示的控制字符的ASCII值;例如,A对应0x1D对应0x4^DEOT)。
      换句话说:您将 0x40 添加到控制字符的 ASCII 值,以获取其以脱字符号表示的字母表示的 ASCII 值。
      ^@表示NUL0x0个字符)和^?表示DEL0x7f)与这种写法一致,因为@ 具有 ASCII 值 0x40(即,它刚好在 ASCII table 中的 A (0x41) 之前)并且 0x40 + 0x7f 被限制为 7 位(bit-ANDed 与最大 ASCII 值 0x7f) 产生 0x3f,这是 ?.

    • 的 ASCII 值
    • 检查给定文件的 ASCII 值 外来控制字符,您可以将其通过管道传输到 od -c,将 0x1 表示为(八进制)001.

  • 这意味着,将文件直接sed传递时,您不能使用脱字符号,而必须使用您的 s 调用中的 实际控制字符

    • 请注意,当您使用 Control-VControl-A 创建一个 actual 0x1 字符,它也会 出现 插入符号 - 如 ^A - 但在那种情况下它只是 终端的真控角色观想;虽然它可能看起来像两个主要table字符^A,但它不是。纯粹从视觉上看不出区别 - 这就是为什么 使用转义序列或 ANSI C-quoted 字符串来表示控制字符是更好的选择 - 见下文。
  • 假设您的 shell 是 bashkshzsh,使用 [=181 的更好选择=]Control-A使用ANSI C-quoted string生成0x1字符:$''

    • 然而,正如 Lars Fischer 在对该问题的评论中指出的那样,GNU sed 也识别 \x01 的转义序列 0x1.

因此,您的命令应该是:

sed -i 's/\x01/,/g' CSVFILE    # \x01 only recognized by GNU sed

或者,使用 ANSI C-quoted 字符串:

sed -i $'s//,/g' CSVFILE  

注:虽然这种形式原则上可以与BSD/OSX一起使用sed-i 语法略有不同:您必须使用 sed -i '' $'s//,/g' CSVFILE


为您的任务使用 sed 的唯一原因是利用 in-place 更新 (-i);否则,tr 是更好的选择 - 请参阅 Ed Morton's answer