我有一个 1 行文件,包含非图形字符,我想根据模式拆分它。我的模式是 \[0-9][0-9][0-9]
;我该怎么做?例如,我想分割这一行:
\001abd \002pqr \003xyz
到:
\001abd
\002pqr
\003xyz
我使用 /bin/sh 作为默认 shell。
另一个输入示例:
CHANGE^\039^OE@ MORE^\040^L^[[00m^OAC DEPOSIT TO WHICH ACCOUNT^N020^^\055^L^[(1^[[00m^OAA PAYMENT FROM WHICH ACCOUNT^N020
期望的输出:
CHANGE^
\039^OE@ MORE^
\040^L^[[00m^OAC DEPOSIT TO WHICH ACCOUNT^N020^^
\055^L^[(1^[[00m^OAA PAYMENT FROM WHICH ACCOUNT^N020
一行文件的大小为 80KB,我使用的是 GNU sed 版本 4.2.1,操作系统是 Red Hat Enterprise Linux Server 版本 6.5(圣地亚哥)
答案1
有一个初步的问题陈述,并提出了各种解决方案。事实证明这些不起作用,因为实际数据与描述不符。
可行的答案
我的模式是:
FS[0-9][0-9][0-9]
, (FS 是“字段分隔符”);如何在脚本中使用它sed
?
对于 Bash 和 BSD sed
,您可以使用它(这与约翰1024 建议的并使用 BSD 和 Mac OS Xsed
-E
启用扩展正则表达式的符号):
sed -E $'s/(.)(\x1C[[:digit:]]{3})/\\1\\\n\\2/g' file1
符号$'…'
是 Bash 的ANSI C 引用机制。 FS 的字节值是 28,十六进制 0x1C 或八进制 038。双反斜杠表示sed
;看到的反斜杠。\n
前面的内容\\
满足sed
手册中的规定(在本s///
节中):
可以通过将换行符替换到其中来分割行。要在替换字符串中指定换行符,请在其前面添加反斜杠。
检查什么适用于 GNU sed
。
我还观察到 FS 有时被编码为Control-Backslash(因为Control-A有代码 1,但A有代码 65 = 64 + 1;反斜杠\有代码 92 = 64 + 28);这可能解释了永远是学生问题中的混乱。
请注意,GNUsed
用于-r
执行 BSD 所做的操作-E
; POSIXsed
不识别任何一种符号。
研究背景
我已通过电子邮件发送了该文件,如果我收到的内容是准确的,那么我们需要对所需内容进行不同的描述。
字数统计输出:
$ wc file1
1 8804 80106 file1
$
这是十六进制转储的输出:
$ odx file1 | sed 20q
0x0000: 33 1C 1C 1C 31 31 1C 30 30 31 0E 32 30 31 1C 30 3...11.001.201.0
0x0010: 30 32 0E 32 30 31 1C 30 30 33 0E 32 30 33 1C 30 02.201.003.203.0
0x0020: 30 34 24 20 1C 30 30 35 0E 30 30 32 1C 30 30 36 04$ .005.002.006
0x0030: 0E 30 30 32 1C 30 31 31 0C 1B 28 32 0F 45 40 20 .002.011..(2.E@
0x0040: 20 20 59 4F 55 52 20 43 41 52 44 20 49 53 20 4E YOUR CARD IS N
0x0050: 4F 54 20 20 53 45 52 56 49 43 45 44 0F 46 40 20 OT SERVICED.F@
0x0060: 20 20 20 20 20 20 20 20 20 42 59 20 20 54 48 49 BY THI
0x0070: 53 20 41 54 4D 20 0F 47 40 20 20 20 20 20 50 4C S ATM .G@ PL
0x0080: 45 41 53 45 20 54 41 4B 45 20 20 59 4F 55 52 20 EASE TAKE YOUR
0x0090: 43 41 52 44 1B 28 37 0F 49 40 20 20 20 20 20 20 CARD.(7.I@
0x00A0: 20 5C 26 20 2D 28 23 58 3E 3D 20 5C 25 22 40 22 \& -(#X>= \%"@"
0x00B0: 20 41 22 20 0F 4A 40 20 20 20 20 20 20 20 30 57 A" .J@ 0W
0x00C0: 5F 40 5B 3F 4A 58 20 2D 28 40 23 51 20 59 5F 22 _@[?JX -(@#Q Y_"
0x00D0: 20 0F 4B 40 20 20 30 3E 5F 40 22 3E 40 26 20 22 .K@ 0>_@">@& "
0x00E0: 40 20 3E 5B 3D 20 20 2D 28 40 23 51 20 23 4D 47 @ >[= -(@#Q #MG
0x00F0: 55 1B 28 32 1C 30 31 34 0C 1B 28 3E 0F 43 40 20 U.(2.014..(>.C@
0x0100: 20 20 45 53 50 2D 4C 49 4E 4B 2F 46 54 53 0F 45 ESP-LINK/FTS.E
0x0110: 40 20 20 20 20 20 20 20 41 54 4D 0F 47 40 20 4D @ ATM.G@ M
0x0120: 41 52 4B 45 54 49 4E 47 20 4D 45 53 53 41 47 45 ARKETING MESSAGE
0x0130: 20 45 32 1C 30 31 35 0C 1C 30 31 38 0C 1C 30 32 E2.015..018..02
$
以下是来自 的相同数据od -c
:
$ od -c file1 | sed 20q
0000000 3 034 034 034 1 1 034 0 0 1 016 2 0 1 034 0
0000020 0 2 016 2 0 1 034 0 0 3 016 2 0 3 034 0
0000040 0 4 $ 034 0 0 5 016 0 0 2 034 0 0 6
0000060 016 0 0 2 034 0 1 1 \f 033 ( 2 017 E @
0000100 Y O U R C A R D I S N
0000120 O T S E R V I C E D 017 F @
0000140 B Y T H I
0000160 S A T M 017 G @ P L
0000200 E A S E T A K E Y O U R
0000220 C A R D 033 ( 7 017 I @
0000240 \ & - ( # X > = \ % " @ "
0000260 A " 017 J @ 0 W
0000300 _ @ [ ? J X - ( @ # Q Y _ "
0000320 017 K @ 0 > _ @ " > @ & "
0000340 @ > [ = - ( @ # Q # M G
0000360 U 033 ( 2 034 0 1 4 \f 033 ( > 017 C @
0000400 E S P - L I N K / F T S 017 E
0000420 @ A T M 017 G @ M
0000440 A R K E T I N G M E S S A G E
0000460 E 2 034 0 1 5 \f 034 0 1 8 \f 034 0 2
$
这是数据的字符频率分析:
= 3: 1
= 10: 1
= 12: 648
= 14: 883
= 15: 3461
= 27: 1384
= 28: 722
= 32: 15248
! = 33: 178
" = 34: 1236
# = 35: 1847
$ = 36: 2
% = 37: 44
& = 38: 945
' = 39: 197
( = 40: 2096
) = 41: 1434
* = 42: 695
+ = 43: 25
, = 44: 446
- = 45: 1431
. = 46: 92
/ = 47: 29
0 = 48: 2453
1 = 49: 1279
2 = 50: 1052
3 = 51: 758
4 = 52: 427
5 = 53: 565
6 = 54: 299
7 = 55: 862
8 = 56: 414
9 = 57: 423
: = 58: 78
; = 59: 330
< = 60: 3
= = 61: 313
> = 62: 1683
? = 63: 60
@ = 64: 3472
A = 65: 2268
B = 66: 791
C = 67: 2034
D = 68: 1480
E = 69: 2862
F = 70: 732
G = 71: 692
H = 72: 799
I = 73: 1202
J = 74: 360
K = 75: 358
L = 76: 963
M = 77: 823
N = 78: 1483
O = 79: 1726
P = 80: 588
Q = 81: 507
R = 82: 1411
S = 83: 1624
T = 84: 1905
U = 85: 1172
V = 86: 151
W = 87: 372
X = 88: 1063
Y = 89: 647
Z = 90: 758
[ = 91: 1026
\ = 92: 665
] = 93: 275
^ = 94: 397
_ = 95: 1179
a = 97: 1
c = 99: 1
d = 100: 1
m = 109: 240
o = 111: 2
p = 112: 2
q = 113: 4
r = 114: 2
s = 115: 2
t = 116: 4
u = 117: 1
w = 119: 1
y = 121: 1
z = 122: 15
最后一列中的数字之和为 80106,与 一致wc
。
正如您所看到的,只有一个换行符(代码 10),并且它出现在文件的最后。小写字母很少,大写字母很多,反斜杠数量适中,但是(从目前显示的数据中看不到的是)反斜杠后面没有一个数字。请注意,不存在 ASCII 范围之外的字符代码(没有设置高位的字符代码),并且 ASCII 范围的覆盖也不完整。
我编写了一个快速分析程序来查看反斜杠后面有哪些字符:
#include <stdio.h>
int main(void)
{
int c;
int count[256] = { 0 };
while ((c = getchar()) != EOF)
{
if (c == '\\')
{
c = getchar();
count[c]++;
}
}
for (int i = 0; i < 255; i++)
{
if (count[i] != 0)
printf("%3d = %5d\n", i, count[i]);
}
return 0;
}
当运行该文件时,它产生:
12 = 3
14 = 58
15 = 3
27 = 25
34 = 10
35 = 51
37 = 14
38 = 126
40 = 9
44 = 51
45 = 26
47 = 2
59 = 17
62 = 118
64 = 46
65 = 2
66 = 2
67 = 17
69 = 1
71 = 4
72 = 5
74 = 15
79 = 1
81 = 9
83 = 1
85 = 5
88 = 18
90 = 12
91 = 6
95 = 8
计数总和为 665,与原始字符分析中的反斜杠数量一致。
数字的代码为 48..57;反斜杠后面的字符都不是数字。
这就是为什么所展示的各种解决方案都失败了——它们从来没有机会,因为数据甚至开始与描述的内容不相似。
答案2
使用 sed:
$ cat file
\001abd \002pqr \003xyz
$ sed -E 's/(.)(\\[[:digit:]]{3})/\1\n\2/g' file
\001abd
\002pqr
\003xyz
使用相同的 sed 命令但使用其他数据示例:
$ cat file2
CHANGE^\039^OE@ MORE^\040^L^[[00m^OAC DEPOSIT TO WHICH ACCOUNT^N020^^\055^L^[(1^[[00m^OAA PAYMENT FROM WHICH ACCOUNT^N020
$ sed -E 's/(.)(\\[[:digit:]]{3})/\1\n\2/g' file2
CHANGE^
\039^OE@ MORE^
\040^L^[[00m^OAC DEPOSIT TO WHICH ACCOUNT^N020^^
\055^L^[(1^[[00m^OAA PAYMENT FROM WHICH ACCOUNT^N020
更新:FS-数字-数字-数字的拆分
ASCII“文件分隔符”(FS) 字符是十六进制的1C
。使用 GNU sed:
sed -E 's/(.)(\x1c[[:digit:]]{3})/\1\n\2/g'
为了演示这一点,让我们创建一个测试文件:
$ echo $'One\x1c123Two\x1c456Three\x1c7none' >newfile
现在,让我们运行sed
:
$ sed -E 's/(.)(\x1c[[:digit:]]{3})/\1\n\2/g' newfile
One
123Two
456Three7none
线路已成功分割。
讨论
在我的终端上,如上所示,FS 字符是不可见的。当跑得少时,它们就会变得可见。例如,运行less newfile
结果显示:
One^\123Two^\456Three^\7none
这里我们可以看到FS字符显示为^\
。这与问题中显示的第二个示例输入一致:
CHANGE^\039^OE@ MORE^\040^L^[[00m^OAC DEPOSIT TO WHICH ACCOUNT^N020^^\055^L^[(1^[[00m^OAA PAYMENT FROM WHICH ACCOUNT^N020
答案3
如果你想从:
\001abc \002jkl \003xyz
...到...
\001abc
\002jkl
\003xyz
...那么最简单、最高效的解决方案是:
tr \ \\n <in >out
\n
然而,这与根据您的模式在 ewlines 上拆分您的输入不同\[0-9][0-9][0-9]
,这可能会受到如下影响:
sed 's/\\[0-9]\{3\}/&\n/g' <in >out
...并且会导致...
\001
abc \002
jkl \003
xyz
...虽然我想你的意思可能是你想插入一条\n
ewline前每次出现\[0-9][0-9][0-9]
,在这种情况下你可以这样做:
sed 's/\\[0-9]\{3\}/\n&/g' <in >out
...要得到...
\001abc \002jkl \003xyz
...每行末尾有一个尾随空格。
但是这两种方法在处理很长的输入行时都可能存在问题。如果您的整个输入文件只有一行,那么我们可以可靠地执行以下操作:
{ tr '\\' \\n |
sed -e:t \
-e'$!N;/\n[0-9]\{3\}/!s/\n/\\/;tt' \
-e's/\n/&\\/;P;D'
} <infile >outfile
上面的命令链将被tr
解析全部输入中的反斜杠为\n
ewline 字符,然后将结果通过管道传输到该结果,sed
该结果将递归地t
估计每个输入行的头部三个数字。tr
处理长输入行根本不会有任何问题,并且当它完成时,它的输出应该至少包含\n
您需要的许多 ewline 字符。如果\n
ewline 字符是不是紧随其后的是三位数字,然后简单地用反斜杠替换,否则如果是\n
然后在ewline 字符和三个数字之间插入一个反斜杠。
第二个示例的结果是:
CHANGE^
\039^OE@ MORE^
\040^L^[[00m^OAC DEPOSIT TO WHICH ACCOUNT^N020^^
\055^L^[(1^[[00m^OAA PAYMENT FROM WHICH ACCOUNT^N020