首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将文本拆分为列并将所有数据移动到下一列

将文本拆分为列并将所有数据移动到下一列
EN

Code Review用户
提问于 2022-08-31 12:55:29
回答 1查看 34关注 0票数 3

目标

我想确认一下下面的工作脚本是否有一种更有效的方法,它将我的tsv文件中的列数据分割开来。我怀疑这可以用awk或sed来实现,但我的经验有限,我无法在搜索中使用正确的单词,找出某人在哪里取得了类似的结果。如果有人能确认在某个地方是否有类似的帖子,或者教科书中有一章能给出答案,我会很感激你的指导。

上下文

在我的tsv数据集中,我有第6-9列,它们有一个ID值和匹配字符串的组合。我的目标是分离is和字符串,并将其余的列移到右边,以便为创建的其他列腾出空间。如果没有字符串数据,则复制ID值。如果数据为空白,则保留空白单元格。

  • ID可以包含任何字符,但总是包含在其列的最后一组括号中,除非ID没有匹配的字符串,在这种情况下,只有ID值被排除在外。
  • 字符串也可以包含任何字符,包括括号,但并不总是出现在列中。
  • 在某些情况下,列6-9可以为空。

示例数据- data.tsv

代码语言:javascript
复制
client  geo 2022-08-30  username    city    supplier1   10413   232080743   ham
client  geo 2022-08-30  username    city    supplier2 (3219dds135)  campaign (4049) 275975960   ham
client  geo 2022-08-30  username    city    Supplier2 (3211239135)  Campaign. (9591)    276220665   ham
client  geo 2022-08-30  username    city    supplier3 (3219132DSAD35)   Campaign(campaign) (72dad59)    232074226   ham
client  geo 2022-08-30  username    city    Supplier4 (3242319135)  campaign (CAMPAIGN) (7424)  232074240   ham
client  geo 2022-08-30  username    city    Supplier4 (3242319135)  Campaign - (CAMPAIGN) (7424)    232074240   ham
client  geo 2022-08-30  username    city    232074240   Campaign – (CAMPAI4324GN) (7424)    DELIVERY (DSAD32E!) (232074240) ham
client  geo 2022-08-30  username    city                ham

工作脚本-文本-列. script

代码语言:javascript
复制
#!/bin/bash

FILENAME="data"

cut -f 6 $FILENAME.tsv | sed -Ee 's/^.+ \(//g' -e 's/\)$//g' > $FILENAME-id.tsv
cut -f 6 $FILENAME.tsv | sed -Ee 's/ \(\S+\)$//g' > $FILENAME-name.tsv
cut -f 1 $FILENAME-id.tsv | paste $FILENAME-name.tsv - > $FILENAME-nameid.tsv

cut -f 7 $FILENAME.tsv | sed -Ee 's/^.+ \(//g' -e 's/\)$//g' > $FILENAME-id2.tsv
cut -f 7 $FILENAME.tsv | sed -Ee 's/ \(\S+\)$//g' > $FILENAME-name2.tsv
cut -f 1 $FILENAME-id2.tsv | paste $FILENAME-name2.tsv - > $FILENAME-nameid2.tsv

cut -f 8 $FILENAME.tsv | sed -Ee 's/^.+ \(//g' -e 's/\)$//g' > $FILENAME-id3.tsv
cut -f 8 $FILENAME.tsv | sed -Ee 's/ \(\S+\)$//g' > $FILENAME-name3.tsv
cut -f 1 $FILENAME-id3.tsv | paste $FILENAME-name3.tsv - > $FILENAME-nameid3.tsv

cut -f 1,2 $FILENAME-nameid.tsv | paste $FILENAME.tsv  - > 1-$FILENAME.tsv
cut -f 1,2 $FILENAME-nameid2.tsv | paste 1-$FILENAME.tsv  - > 2-$FILENAME.tsv
cut -f 1,2 $FILENAME-nameid3.tsv | paste 2-$FILENAME.tsv  - > 3-$FILENAME.tsv

awk -F'\t' -v OFS="\t" '{print $1,$2,$3,$4,$5,$10,$11,$12,$13,$14,$15,$9}' 3-$FILENAME.tsv > 4-$FILENAME.tsv

预期输出

代码语言:javascript
复制
client  geo 2022-08-30  username    city    supplier1   supplier1   10413   10413   232080743   232080743   ham
client  geo 2022-08-30  username    city    supplier2   3219dds135  campaign    4049    275975960   275975960   ham
client  geo 2022-08-30  username    city    Supplier2   3211239135  Campaign.   9591    276220665   276220665   ham
client  geo 2022-08-30  username    city    supplier3   3219132DSAD35   Campaign(campaign)  72dad59 232074226   232074226   ham
client  geo 2022-08-30  username    city    Supplier4   3242319135  campaign (CAMPAIGN) 7424    232074240   232074240   ham
client  geo 2022-08-30  username    city    Supplier4   3242319135  Campaign - (CAMPAIGN)   7424    232074240   232074240   ham
client  geo 2022-08-30  username    city    232074240   232074240   Campaign – (CAMPAI4324GN)   7424    DELIVERY (DSAD32E!) 232074240   ham
client  geo 2022-08-30  username    city                            ham
EN

回答 1

Code Review用户

发布于 2022-08-31 19:51:06

考虑使用Python

在Bash中实现这一相对简单的任务并不容易。它在Python中是很简单的,在现代系统中也是如此,我认为这是值得的。

,不要重复,

从列中提取id和name值的sed操作并不简单,而且是重复的。为了避免重复,您可以定义一个函数,并使用一个描述性名称来指示读者正在发生的事情,例如:

代码语言:javascript
复制
extract_id() {
    sed -Ee 's/^.+ \(//g' -e 's/\)$//g'
}

extract_name() {
    sed -Ee 's/ \(\S+\)$//g'
}

cut -f 6 "$FILENAME.tsv" | extract_id > "$FILENAME-id.tsv"
cut -f 6 "$FILENAME.tsv" | extract_name > "$FILENAME-name.tsv"

我还在包含变量的参数周围添加了推荐的双引号,因为这是一个很好的实践。

然后你可以更进一步。与其对3个不同的列重复相同的操作,不如考虑使用一个循环:

代码语言:javascript
复制
for col in 6 7 8; do
    cut -f "$col" "$FILENAME.tsv" | extract_id > "$FILENAME-$col-id.tsv"
    cut -f "$col" "$FILENAME.tsv" | extract_name > "$FILENAME-$col-name.tsv"
done

的确,上面对输出文件名的更改并不难适应脚本的其余部分,这样就更容易看出这些文件来自何处。

避免不必要的命令

实际上,许多cut命令都是不必要的,从带有单个列的文件中获取第一列,或者从具有两列的文件中获取前2列:

剪切-f $1 $FILENAME-id.tsv粘贴$FILENAME->$FILENAME-> $FILENAME-nameid.tsv剪切-f 1,2美元文件名-nameid.tsv\ cut $FILENAME.tsv -> 1-$FILENAME.tsv

如果没有cut,这些代码可以编写得更简单:

代码语言:javascript
复制
paste "$FILENAME-name.tsv" "$FILENAME-id.tsv" > "$FILENAME-nameid.tsv"
paste "$FILENAME.tsv "$FILENAME-nameid.tsv" > "1-$FILENAME.tsv"

此外,许多剪切+粘贴命令都会创建不必要的中间文件。也就是说,一旦您在单独的文件中拥有id和name列,您就不需要创建具有name-id对的文件,您可以直接将它们粘贴到一起。

在我们前面使用的循环之后,可以使用一个paste跳过大多数中间文件:

代码语言:javascript
复制
paste "$FILENAME.tsv" "$FILENAME"-{6,7,8}-{name,id}.tsv > "3-$FILENAME.tsv"

还可以通过管道直接进入awk消除最后一个中间文件:

代码语言:javascript
复制
paste "$FILENAME.tsv" "$FILENAME"-{6,7,8}-{name,id}.tsv |
    awk -F'\t' -v OFS="\t" '{print $1,$2,$3,$4,$5,$10,$11,$12,$13,$14,$15,$9}' > "4-$FILENAME.tsv"

实际上,与其键入awk中的所有列,不如为第9列引入一个中间文件,然后将其余的文件粘贴到一起,这样就更简单了:

代码语言:javascript
复制
cut -f9 "$FILENAME.tsv" > "$FILENAME-9.tsv"
cut -f1-5 "$FILENAME.tsv" |
    paste - "$FILENAME"-{6,7,8}-{name,id}.tsv "$FILENAME-9.tsv" > "4-$FILENAME.tsv"

清理临时文件

我不确定您是否需要脚本创建的所有中间文件。对于以后不需要的任何文件,您可以使用临时目录,并在退出时使脚本清理:

代码语言:javascript
复制
workdir=$(mktemp -d)

cleanup() {
    find "$workdir"
    rm -fr "$workdir"
}

trap cleanup EXIT

不要将SHOUT_CASE用于变量

SHOUT_CASE通常用于系统环境变量。看到它们被用于其他目的是令人困惑的,并可能导致问题。

把它放在一起

有了上述建议,脚本就会减少重复,使用更少的中间文件,并进行简单的清理:

代码语言:javascript
复制
#!/bin/bash

set -euo pipefail

input="data.txt"
output="4-$input.tsv"

workdir=$(mktemp -d)

cleanup() {
    rm -fr "$workdir"
}

trap cleanup EXIT

extract_id() {
    sed -Ee 's/^.+ \(//g' -e 's/\)$//g'
}

extract_name() {
    sed -Ee 's/ \(\S+\)$//g'
}

for col in 6 7 8; do
    cut -f "$col" "$input.tsv" | extract_name > "$workdir/$col-name.tsv"
    cut -f "$col" "$input.tsv" | extract_id > "$workdir/$col-id.tsv"
done

cut -f 9 "$input.tsv" > "$workdir/9.tsv"
cut -f 1-5 "$input.tsv" |
    paste - "$workdir"/{6,7,8}-{name,id}.tsv "$workdir/9.tsv" > "$output"

这仍然不是很好,因为它仍然读取输入文件8次。当然,在一次传递中使用awk就可以做到这一点,但现代语言Python也是如此。

票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/279328

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档