作者热门文章
- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我需要从一个文件中读取数据并插入到多个文件中(每个文件的大小小于 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/
我是一名优秀的程序员,十分优秀!