【问题标题】:Should LC_ALL=C Always be used for Non-Locale-Specific sed Operations?LC_ALL=C 是否应该始终用于非特定于语言环境的 sed 操作?
【发布时间】:2017-07-01 09:01:17
【问题描述】:

我有带有 cmets 注释的 JSON 文件,我在使用 jq 进行操作之前将其删除。我刚刚遇到了一个有趣的问题,我收到了一个带有注释注释的 JSON 文件,其中包含一些富文本引号字符(十六进制 93 和十六进制 94)。我现有的 sed 点 . 字符与这些字符不匹配。这是一个演示:

首先,输入:

% echo -e '# \x93text\x94\n{"a":1}' | od -c
0000000   #     223   t   e   x   t 224  \n   {   "   a   "   :   1   }
0000020  \n
0000021
%

这是转换:

% echo -e '# \x93text\x94\n{"a":1}' | sed 's/^\s*#.*//' | od -c
0000000 223   t   e   x   t 224  \n   {   "   a   "   :   1   }  \n
0000017
%

请注意,sed 表达式中的点字符与十六进制 93 字符不匹配。但是,如果我包含 LC_ALL=C:

% echo -e '# \x93text\x94\n{"a":1}' | LC_ALL=C sed 's/^\s*#.*//' | od -c
0000000  \n   {   "   a   "   :   1   }  \n
0000011
%

那么 sed 表达式中的点字符匹配十六进制 93 和十六进制 94 字符。 sed 文档部分Locale Considerations 谈到了括号表达式,但上面的行为似乎证明了这个问题发生在其他地方。

有趣的是,删除而不是替换没有显示这个问题:

% echo -e '# \x93text\x94\n{"a":1}' | sed '/^\s*#.*/d' | od -c         
0000000   {   "   a   "   :   1   }  \n
0000010

鉴于我操作的是带注释的 JSON 文件,我认为在 sed 语句中添加LC_ALL=C 的解决方案是合理的。

所以,我的问题是:在进行非语言环境特定的 sed 转换时,我是否一直想使用 LC_ALL=C (适用于带注释的 JSON 文件)?如果没有,有哪些替代方法可以避免我上面显示的问题?

我的环境:

  • CentOS 7.3 [kernel-3.10.0-514.6.1.el7.x86_64]
  • sed (GNU sed) 4.2.2 [sed-4.2.2-5.el7.x86_64]
  • Bash 4.2.46(1) [bash-4.2.46-21.el7_3.x86_64]

【问题讨论】:

  • 不是 KSH 下的问题(环境肯定不同),但尝试你的线路我得到这个可以帮助 echo -e '# \x93text\x94\n{"a":1}' | sed '/^[[:space:]]*#.*/ s//[HERE]/' | od -c0000000 [ H E R E ] 223 t e x t 224 \n { " a 0000020 " : 1 } \n 所以 sed 估计替换到行尾,而不是选择
  • @NeronLeVelu 是的,无论如何这都是非常奇怪的行为。由于 sed 网页建议包含 LC_ALL=C,这让我想知道这是否是 sed 中错误的解决方法,或者它是否是一个难以理解的功能。

标签: json bash sed centos7


【解决方案1】:

C 语言环境是一种特殊的语言环境,旨在成为最简单的语言环境。您也可以说,虽然其他语言环境适用于人类,但 C 语言环境适用于计算机。 在 C 语言环境中,字符是单字节,字符集是 ASCII

在某些系统上,POSIX 语言环境有所不同,例如未定义非 ASCII 字符的排序顺序。

所以 LC_ALL=C 是考虑非 8 位字符的安全方式。

查看对比

使用 LC,sed 算作字符的一部分

echo -e '# \x93text\x94\n{"a":1}' | LC_ALL=C sed 's/[^[:alnum:]]/[HERE:&] /g' | od -c
0000000   [   H   E   R   E   :   #   ]       [   H   E   R   E   :
0000020   ]       [   H   E   R   E   : 223   ]       t   e   x   t   [
0000040   H   E   R   E   : 224   ]      \n   [   H   E   R   E   :   {
0000060   ]       [   H   E   R   E   :   "   ]       a   [   H   E   R
0000100   E   :   "   ]       [   H   E   R   E   :   :   ]       1   [
0000120   H   E   R   E   :   }   ]      \n

没有 LC,sed 不计入要考虑的字符的一部分([[:alnum:]][^[:alnum:]] 不要看到第 8 位字符)

 echo -e '# \x93text\x94\n{"a":1}' | sed 's/[[:alnum:]]/[HERE:&] /g' | od -c
0000000   #     223   [   H   E   R   E   :   t   ]       [   H   E   R
0000020   E   :   e   ]       [   H   E   R   E   :   x   ]       [   H
0000040   E   R   E   :   t   ]     224  \n   {   "   [   H   E   R   E
0000060   :   a   ]       "   :   [   H   E   R   E   :   1   ]       }
0000100  \n

echo -e '# \x93text\x94\n{"a":1}' | sed 's/[^[:alnum:]]/[HERE:&] /g' | od -c
0000000   [   H   E   R   E   :   #   ]       [   H   E   R   E   :
0000020   ]     223   t   e   x   t 224  \n   [   H   E   R   E   :   {
0000040   ]       [   H   E   R   E   :   "   ]       a   [   H   E   R
0000060   E   :   "   ]       [   H   E   R   E   :   :   ]       1   [
0000100   H   E   R   E   :   }   ]      \n

【讨论】:

  • 那么,您认为我什至可以在我的脚本中使用它作为全局选项 (export LC_ALL=C)?即使用于其他目的的字符串操作涉及特定于语言环境的数据,那么我猜替换仍然会做正确的事情。例如,s/$LOCALE_WORD/$REPLACEMENT/,其中两个词是特定于语言环境的,仍然可以与LC_ALL=C 一起正常工作,对吧?这将激励我将其设置为环境变量,而不是每次使用 sed 时的 in from。你怎么看?
  • 这是一种 sed 行为,因此仅将其添加到 sed(您的批次中的每个 sed)以避免其他应用程序上出现其他意外行为(这只是我的观点和我在批次中使用它的方式)可能会出现问题)
  • 非常好。我会去做。感谢您花时间回答。将此标记为已接受的答案。
猜你喜欢
  • 1970-01-01
  • 2012-07-18
  • 2012-06-08
  • 1970-01-01
  • 1970-01-01
  • 2020-08-23
  • 1970-01-01
  • 2018-12-06
  • 2014-01-18
相关资源
最近更新 更多