Linux 在bash脚本中使用While循环时出现问题(将文件拆分为多个文件)

Linux 在bash脚本中使用While循环时出现问题(将文件拆分为多个文件),linux,bash,shell,unix,sed,Linux,Bash,Shell,Unix,Sed,我需要从一个文件中读取数据并插入多个文件(每个文件的大小小于3mb,文件大小可以不同)。重要的是,代理的记录不应该被分割成多个文件。我在UNIX bash脚本中的While循环中完成了所有这些 Input.csv Src,AgentNum,PhoneNum DWH,Agent_1234,phone1 NULL,NULL,phone2 NULL,NULL,phone3 DWH,Agent_5678,phone

我需要从一个文件中读取数据并插入多个文件(每个文件的大小小于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
bashshell脚本

#!/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

好得多的处理方法,是的。在这里使用
du
效率极低,因为您只能在内部维护一个字节计数器。另请参见#14,修复发现的错误——通常,在这里提问之前作为一种练习。(将
>
放在
echo
上效率很低——这意味着每次想写一行时都要重新打开输出文件)。顺便问一句,每行前面的制表符都是文字吗?(如果没有,为什么在正则表达式中的
DWH
前面有一个通配符?)!!对于剧本和有价值的评论。快速提问-事实上,在我的输入文件中,evey agent record以“DWH”开头,我如何在if条件下使用它?是否尚未对该条件进行充分测试?如果我按原样运行脚本,它只是复制文件(而不是执行拆分)。我猜这可能与第一列中DWH的双引号有关。请告诉我。哦——不清楚双引号是否是字面意义上的;我以为你是在用它们把你要问的字符串从英语文本的其余部分划出,因此字母
DWH
就是现在/曾经出现的。将来,请使用反勾号,即。
“DWH”
,以指示StackOverflow文本格式中的文字字符串。无论如何,在这种情况下,它将是
[[$line=''DWH','*]
它现在就像一个符咒。你刚刚救了我一天。谢谢!!!此外,您关于使用BashPittles和shellcheck.net的建议也非常有用。