在没有定界符的固定长度编码记录中的字段之间插入空格的 sed 脚本?
A sed script that inserts spaces between field in a fixed length encoded record without delimiters?
我有这样的记录
000000000011111111112222222222333444555666777888999aaabbbcccdddeee
没有分隔符,我想将其读入 bash 脚本数组。如果有分隔符,我可以说
IFS='|' record=($line)
到此为止。但是当字段完全填充时,没有分隔符。
所以我想我应该制作一个快速的 sed 脚本
IFS='|' record=( $( echo "$line" |sed 's/\(...\)/ /g' ) )
这将 - 在这种情况下 - 在 3 个字符的等长字段之间放置一个 space 分隔符。
但我的字段宽度不同!
IFS='|' record=( $( echo "$line" |sed 's/\(.\{10\}\)\(.\{10\}\)\(.\{10\}\)\(.\{3\}\)\(.\{3\}\)\(.\{3\}\)/ /g' ) )
简单!但在我的情况下不可能,因为我有超过 9 个字段!
我想 sed 必须有某种方法来控制 s/.../.../g
行为,这样你就可以做 3 x 10 的宽度,然后 10 乘以 3 的宽度,或者任何字段长度。但我真的不记得这是如何用 sed 完成的,而且众所周知,手册页没有教育意义。
我想我可以做一个循环,然后用 read -n $width
读取每个字段并建立我自己的数组。这就是我要做的,但我更喜欢一次性的方式。如果在 shell 环境中有一个可用的 scanf(1) 命令,就像有一个 printf(1) 命令一样,或者如果 bash read -a
有一个 -n 10,那就真的很容易了,10,10,3,3,3* 格式字符串或类似的东西。
使用 GNU awk
和 FIELDWIDTHS
引入定界符的一个想法:
x='000000000011111111112222222222333444555666777888999aaabbbcccdddeee'
awk '
BEGIN { OFS="|" # define output delimiter
FIELDWIDTHS = "10 10 10 3 3 3 3 3 3 3 3 3 3 3 3" # define width of each field
}
{ = # force an evaluation so that fields are parsed
print
}
' <<< "${x}"
这会生成:
0000000000|1111111111|2222222222|333|444|555|666|777|888|999|aaa|bbb|ccc|ddd|eee
从这里您可以使用 |
分隔数据执行您想要的操作(例如,读入数组)。
备注:
FIELDWIDTHS
是 只是 一个变量,因此要处理具有可变数量字段的输入,您当然可以概括此 awk
脚本并传入一个字符串定义 FIELDWIDTHS
- 对于此示例,我们使用了单个变量 (
x
),但我们可以轻松地将文件提供给 awk
脚本以向所有输入行添加分隔符
- 如果在几个地方需要,将其包装在用户定义的函数中应该很容易
您可以使用 Perl 的 unpack
函数。
对于少量记录,您可以这样做(使用示例行):
IFS='|' record=($(perl -ple '$_=join"|",unpack"(a10)3(a3)12"' <<<"$line"))
因为它为每一行运行一个新的 perl
进程,如果你有很多,那么沿着以下行包装一个循环会更有效:
perl -ple '$_=join"|",unpack"(a10)3(a3)12"' inputfile |\
while IFS='|' read -ra record; do
: process ${record[@]}
done
(假设固定宽度的记录由换行分隔)
因为你想创建一个 bash 数组,使用像 sed
这样的外部工具是次优的。您必须解析输入两次,首先使用外部工具,然后 bash。在 bash.
中做所有事情更安全、更高效,而且可能更容易
Bash 的内置 [[
可以使用 =~
匹配正则表达式。匹配的组存储在数组 BASH_REMATCH
:
中
printf -v regex '(.{0,%s})' 10 10 10 3 3 3 3
line=000000000011111111112222222222333444555666777888999aaabbbcccdddeee
[[ "$line" =~ ^$regex ]]
fields=("${BASH_REMATCH[@]:1}")
这将忽略超出指定字段的字符,如果指定字段超出该行,则保留(部分)空数组条目。但您可以根据自己的需要进行调整。
如果您从右到左工作,此 sed
应该会产生示例数据的预期结果。
$ array=($(sed -E 's/(.{3})/ /g10;s/(.{10})/ /2;s/(.{10})/ /' input_file))
s/(.{3})/ /g10
- 这将处理最初从第 30 个字符开始的 10x3 宽度,每第 3 个字符插入一个 space。
s/(.{10})/ /2;s/(.{10})/ /
开头剩余的 30 个字符现在将再次从右到左拆分为 3x10。
回显创建的数组,结果如下
$ echo ${array[@]}
0000000000 1111111111 2222222222 333 444 555 666 777 888 999 aaa bbb ccc ddd eee
我有这样的记录
000000000011111111112222222222333444555666777888999aaabbbcccdddeee
没有分隔符,我想将其读入 bash 脚本数组。如果有分隔符,我可以说
IFS='|' record=($line)
到此为止。但是当字段完全填充时,没有分隔符。
所以我想我应该制作一个快速的 sed 脚本
IFS='|' record=( $( echo "$line" |sed 's/\(...\)/ /g' ) )
这将 - 在这种情况下 - 在 3 个字符的等长字段之间放置一个 space 分隔符。
但我的字段宽度不同!
IFS='|' record=( $( echo "$line" |sed 's/\(.\{10\}\)\(.\{10\}\)\(.\{10\}\)\(.\{3\}\)\(.\{3\}\)\(.\{3\}\)/ /g' ) )
简单!但在我的情况下不可能,因为我有超过 9 个字段!
我想 sed 必须有某种方法来控制 s/.../.../g
行为,这样你就可以做 3 x 10 的宽度,然后 10 乘以 3 的宽度,或者任何字段长度。但我真的不记得这是如何用 sed 完成的,而且众所周知,手册页没有教育意义。
我想我可以做一个循环,然后用 read -n $width
读取每个字段并建立我自己的数组。这就是我要做的,但我更喜欢一次性的方式。如果在 shell 环境中有一个可用的 scanf(1) 命令,就像有一个 printf(1) 命令一样,或者如果 bash read -a
有一个 -n 10,那就真的很容易了,10,10,3,3,3* 格式字符串或类似的东西。
使用 GNU awk
和 FIELDWIDTHS
引入定界符的一个想法:
x='000000000011111111112222222222333444555666777888999aaabbbcccdddeee'
awk '
BEGIN { OFS="|" # define output delimiter
FIELDWIDTHS = "10 10 10 3 3 3 3 3 3 3 3 3 3 3 3" # define width of each field
}
{ = # force an evaluation so that fields are parsed
print
}
' <<< "${x}"
这会生成:
0000000000|1111111111|2222222222|333|444|555|666|777|888|999|aaa|bbb|ccc|ddd|eee
从这里您可以使用 |
分隔数据执行您想要的操作(例如,读入数组)。
备注:
FIELDWIDTHS
是 只是 一个变量,因此要处理具有可变数量字段的输入,您当然可以概括此awk
脚本并传入一个字符串定义FIELDWIDTHS
- 对于此示例,我们使用了单个变量 (
x
),但我们可以轻松地将文件提供给awk
脚本以向所有输入行添加分隔符 - 如果在几个地方需要,将其包装在用户定义的函数中应该很容易
您可以使用 Perl 的 unpack
函数。
对于少量记录,您可以这样做(使用示例行):
IFS='|' record=($(perl -ple '$_=join"|",unpack"(a10)3(a3)12"' <<<"$line"))
因为它为每一行运行一个新的 perl
进程,如果你有很多,那么沿着以下行包装一个循环会更有效:
perl -ple '$_=join"|",unpack"(a10)3(a3)12"' inputfile |\
while IFS='|' read -ra record; do
: process ${record[@]}
done
(假设固定宽度的记录由换行分隔)
因为你想创建一个 bash 数组,使用像 sed
这样的外部工具是次优的。您必须解析输入两次,首先使用外部工具,然后 bash。在 bash.
Bash 的内置 [[
可以使用 =~
匹配正则表达式。匹配的组存储在数组 BASH_REMATCH
:
printf -v regex '(.{0,%s})' 10 10 10 3 3 3 3
line=000000000011111111112222222222333444555666777888999aaabbbcccdddeee
[[ "$line" =~ ^$regex ]]
fields=("${BASH_REMATCH[@]:1}")
这将忽略超出指定字段的字符,如果指定字段超出该行,则保留(部分)空数组条目。但您可以根据自己的需要进行调整。
如果您从右到左工作,此 sed
应该会产生示例数据的预期结果。
$ array=($(sed -E 's/(.{3})/ /g10;s/(.{10})/ /2;s/(.{10})/ /' input_file))
s/(.{3})/ /g10
- 这将处理最初从第 30 个字符开始的 10x3 宽度,每第 3 个字符插入一个 space。
s/(.{10})/ /2;s/(.{10})/ /
开头剩余的 30 个字符现在将再次从右到左拆分为 3x10。
回显创建的数组,结果如下
$ echo ${array[@]}
0000000000 1111111111 2222222222 333 444 555 666 777 888 999 aaa bbb ccc ddd eee