Unix - cut命令(添加自定义分隔符)

4
给定一个包含如下数据的文件(即stores.dat文件):
id               storeNo     type
2ttfgdhdfgh      1gfdkl-28   kgdl
9dhfdhfdfh       2t-33gdm    dgjkfndkgf

期望输出:

id               |storeNo     |type
2ttfgdhdfgh      |1gfdkl-28   |kgdl
9dhfdhfdfh       |2t-33gdm    |dgjkfndkgf

希望在这三个切割范围之间添加“|”分隔符:

cut -c1-18,19-30,31-40 stores.dat

如何在每个切割值之间插入分隔符的语法是什么?

如果您能提供修剪值的选项,将获得奖励分数(例如):

id|storeNo|type
2ttfgdhdfgh|1gfdkl-28|kgdl
9dhfdhfdfh|2t-33gdm|dgjkfndkgf\

更新(感谢Mat的答案)我最终成功实现了这个解决方案——(它有点混乱,但我的bash版本似乎不支持更优雅的算术运算)

#!/bin/bash
unpack=""
filename="$1"
while [ $# -gt 0 ] ; do
    arg="$1"
    if [ "$arg" != "$filename" ]
    then
        firstcharpos=`echo $arg | awk -F"-" '{print $1}'`
        secondcharpos=`echo $arg | awk -F"-" '{print $2}'`
        compute=`(expr $firstcharpos - $secondcharpos)`
        compute=`(expr $compute \* -1 + 1)`
        unpack=$unpack"A"$compute
    fi
    shift
done
perl -ne 'print join("|",unpack("'$unpack'", $_)), "\n";' $filename 

Usage: sh test.sh input_file 1-17 18-29 30-39


你需要使用cut吗?这可以很容易地通过sed命令完成。 - Shraddha
但是如果我不使用cut命令,假设我的输入文件在位置17、18、19处有值(2个字段连在一起没有空格),那么sed解决方案会如何处理? - toop
可能是这样的:cat stores.dat | sed 's/ \b/|/g' - Yaniro
只需使用简单的 tr 命令。 - jaypal singh
8个回答

6

因为你在示例中使用了cut命令,所以假设每个字段都是用制表符分隔开的:

$ cut  --output-delimiter='|' -f1-3 input
id|store|No
2ttfgdhdfgh|1gfdkl-28|kgdl
9dhfdhfdfh|2t-33gdm|dgjkfndkgf

如果不是这种情况,请添加输入分隔符开关-d

假设是使用制表符分隔字段而不是固定长度。 - roblogic

4

我会使用awk:

awk '{print $1 "|" $2 "|" $3}'

和其他一些建议类似,它假设列是由空格分隔的,并且不关心列的编号。如果您在其中一个字段中有空格,则它将无法使用。


为什么要打印管道符,当你可以将OFS设置为|呢? awk -v OFS="|" '{print $1,$2,$3}' stores.dat - jaypal singh
这个答案并没有解决提问者的问题;列是固定长度的,而不是以空格分隔的。 - Mat
鹰眼!但是需要遵守@ugoren所放置的免责声明。 :) - jaypal singh
1
@ropata,这看起来像是一个答案,而不是一个评论。 - ugoren
我的完整答案在页面下方[https://dev59.com/H17Va4cB1Zd3GeqPLqIh#25297938],不幸的是。 - roblogic

2
基于字符位置而非空格的更好的 awk 解决方案
$ awk -v FIELDWIDTHS='17 12 10' -v OFS='|' '{ $1=$1 ""; print }' stores.dat | tr -d ' '

id|storeNo|type
2ttfgdhdfgh|1gfdkl-28|kgdl
9dhfdhfdfh|2t-33gdm|dgjkfndkgf

1

如果你不怕使用Perl,这里有一个一行代码解决的方法:

$ perl -ne 'print join("|",unpack("A17A12A10", $_)), "\n";' input 

unpack 调用将从输入行中提取一个 17 个字符的字符串,然后是一个 12 个字符的字符串,最后是一个 10 个字符的字符串,并将它们作为数组返回(去除空格)。join 添加了 |

如果您想要输入列以 x-y 格式显示,而不编写“真正”的脚本,您可以像这样进行黑客攻击(但很丑陋):

#!/bin/bash
unpack=""

while [ $# -gt 1 ] ; do
    arg=$(($1))
    shift
    unpack=$unpack"A"$((-1*$arg+1))
done

perl -ne 'print join("|",unpack("'$unpack'", $_)), "\n";' $1 

用法:t.sh 1-17 18-29 30-39 input_file。与编程有关的内容。

这很好,有没有另一种方法可以输入1-18、19-30等而不是17、12? - toop
已经为此提出了建议,但是现在你应该真正考虑编写一个真正的Perl脚本。顺便说一句,要么我弄错了输入文件,要么你的列规范有误——第一列是1-17,而不是1-18。 - Mat

0

据我所知,你不能使用cut来做到这一点,但只要每列中的值没有内部空格,你可以轻松地使用sed实现:

sed -e 's/  */|/g'

编辑:如果文件格式是真正的固定列格式,并且您不想像Mat所示那样使用perl,那么这可以使用sed完成,但这并不美观,因为sed不支持数字重复量词(.{17}),因此您必须输入正确数量的点:

sed -e 's/^\(.................\)\(............\)\(..........\)$/\1|\2|\3/; s/  *|/|/g'

如果我的输入文件更改为在位置17、18、19处具有值(2个字段连在一起没有空格),那么就会出现问题。同时,处理内部空格也是很好的。 - toop

0

使用 tr 命令怎么样?

tr -s " " "|" < stores.dat

来自man页面:

-s      Squeeze multiple occurrences of the characters listed in the last
        operand (either string1 or string2) in the input into a single
        instance of the character.  This occurs after all deletion and
        translation is completed.

测试:

[jaypal:~/Temp] cat stores.dat 
id               storeNo     type
2ttfgdhdfgh      1gfdkl-28   kgdl
9dhfdhfdfh       2t-33gdm    dgjkfndkgf

[jaypal:~/Temp] tr -s " " "|" < stores.dat 
id|storeNo|type
2ttfgdhdfgh|1gfdkl-28|kgdl
9dhfdhfdfh|2t-33gdm|dgjkfndkgf

你可以轻松地将它重定向到一个新文件,就像这样 -

[jaypal:~/Temp] tr -s " " "|" < stores.dat > new.stores.dat

注意:正如Mat在评论中指出的那样,此解决方案假定每个列由一个或多个空格分隔,而不是以固定长度分隔。

0

你可以简单地使用

cat stores.dat | tr -s ' ' '|'

-1

使用 'sed' 基于正则表达式搜索和替换文件的部分内容

从 infile1 中将空格替换为 '|'

sed -e 's/[ \t\r]/|/g' infile1 > outfile3

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接