gpt4 book ai didi

linux - 在 bash 脚本中使用 While 循环的问题(将文件拆分为多个文件)

转载 作者:太空狗 更新时间:2023-10-29 11:17:41 24 4
gpt4 key购买 nike

我需要从一个文件中读取数据并插入到多个文件中(每个文件的大小小于 3mb,文件大小可以不同)。重要的是 - 代理的记录不应拆分为多个文件。我在 UNIX bash 脚本的 While 循环中完成所有这些工作。

Input.csv
Src,AgentNum,PhoneNum
DWH,Agent_1234,phone1
NULL,NULL,phone2
NULL,NULL,phone3
DWH,Agent_5678,phone1
NULL,NULL,phone2
NULL,NULL,phone3
DWH,Agent_9999,phone1
NULL,NULL,phone2
NULL,NULL,phone3

Desired Output -

Output1.csv (less than 3MB)
Src,AgentNum,PhoneNum
DWH,Agent_1234,phone1
NULL,NULL,phone2
NULL,NULL,phone3

Output2.csv (less than 3MB)
Src,AgentNum,PhoneNum
DWH,Agent_5678,phone1
NULL,NULL,phone2
NULL,NULL,phone3
DWH,Agent_9999,phone1
NULL,NULL,phone2
NULL,NULL,phone3

Bash Shell 脚本

#!/bin/bash
BaseFileName=$(basename $FileName | cut -d. -f1)
Header=`head -1 $FileName`
MaxFileSize=$(( 3 * 1024 * 1024 ))

sed 1d $FileName |
while read -r line
do
echo $line >> ${BaseFileName}_${FileSeq}.csv

MatchCount=`echo $line | grep -c -E '^.DWH'`

if [[ $MatchCount -eq 1 ]]
then
FileSizeBytes=`du -b ${BaseFileName}_${FileSeq}.csv | cut -f1`
if [[ $FileSizeBytes -gt $MaxFileSize ]]
then
#Add a header record to each file
sed -i "1i ${Header}" ${BaseFileName}_${FileSeq}.csv
FileSeq=$((FileSeq + 1))
fi
fi
done

除了1) 它没有按预期拆分记录(代理的一些记录被拆分到多个文件中)2) 只为第一个输出文件插入头记录。3)太慢了,一个10MB的文件用了3分钟。实际上我有一个 3GB 的文件。

有人可以告诉我哪里做错了吗?有没有更好的方法来处理这个问题?

最佳答案

这是一个粗略的尝试——它不像纯 awk 解决方案那样快,但它比您已有的解决方案快得多,:

#!/bin/bash

# two external parameters: input file name, and max size in bytes (default to 3MB)
InputFile=$1
MaxFileSize=${2:-$(( 3 * 1024 * 1024 ))}

BaseName=${InputFile%.*} # strip extension
Ext=${InputFile##*.} # store extension
FileSeq=0 # start output file at sequence 0

# redirect stdin from the input file, stdout to the first output file
exec <"$InputFile" || exit
exec >"${BaseName}.${FileSeq}.${Ext}" || exit

# read the header; copy it to the first output file, and initialize CurFileSize
IFS= read -r Header || exit
printf '%s\n' "$Header" || exit
CurFileSize=$(( ${#Header} + 1 ))

# ...then loop over our inputs, and copy appropriately
while IFS= read -r line; do
if [[ $line = DWH,* ]] && (( CurFileSize > MaxFileSize )); then
(( FileSeq++ ))
exec >"${BaseName}.${FileSeq}.${Ext}" || exit
printf '%s\n' "$Header" || exit
CurFileSize=$(( ${#Header} + 1 ))
fi
printf '%s\n' "$line" || exit
(( CurFileSize += ${#line} + 1 ))
done

值得注意的变化:

  • 根本没有调用任何外部工具。没有 sed,没有 basename,没有 du,没有 grep。任何时候你编写 $()``,都会有非常重要的性能成本;除非无法避免,否则不应在紧密循环内使用这些构造——并且在使用对 POSIX sh 标准的 ksh 或 bash 扩展时,它们实际上无法避免的情况很少见。
  • 只有在需要打开一个新的输出文件时才会调用重定向。我们不会每次想写一行时都使用>>"$filename",而是每次需要开始一个代码时都使用exec>"$filename"新的输出文件。
  • 在参数扩展期间始终使用引号,除非在其他语法明确禁止字符串拆分或通配的情况下。如果不这样做可能会损坏您的文件(例如,将 * 替换为当前目录中的文件列表;将制表符替换为空格;等等)。如有疑问,请引用更多内容。
  • 使用 printf '%s\n'echo 更符合 POSIX 标准的定义 -- 参见 the standard definition for echo ,尤其是“应用程序使用”部分。
  • 我们正在明确地进行错误处理。也可以使用 set -e,但有 substantial caveats对其使用。

测试过程和输出如下:

$ cat >input.csv <<'EOF'
Src,AgentNum,PhoneNum
DWH,Agent_1234,phone1
NULL,NULL,phone2
NULL,NULL,phone3
DWH,Agent_5678,phone1
NULL,NULL,phone2
NULL,NULL,phone3
DWH,Agent_9999,phone1
NULL,NULL,phone2
NULL,NULL,phone3
EOF

$ ./splitCSV input.csv 100 ## split at first boundary after 100 bytes

$ cat input.0.csv
Src,AgentNum,PhoneNum
DWH,Agent_1234,phone1
NULL,NULL,phone2
NULL,NULL,phone3
DWH,Agent_5678,phone1
NULL,NULL,phone2
NULL,NULL,phone3

$ cat input.1.csv
Src,AgentNum,PhoneNum
DWH,Agent_9999,phone1
NULL,NULL,phone2
NULL,NULL,phone3

关于linux - 在 bash 脚本中使用 While 循环的问题(将文件拆分为多个文件),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40029848/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com