CSV处理脚本#
脚本说明#
CSV处理脚本用于处理CSV文件,包括数据读取、转换、过滤、统计、合并等操作。
脚本代码#
#!/bin/bash
# CSV处理脚本
# 功能:处理CSV文件的各种操作
# 作者:System Admin
# 日期:2026-01-01
set -euo pipefail
# 配置变量
INPUT_FILE=""
OUTPUT_FILE=""
DELIMITER=","
HAS_HEADER=true
FILTER_COLUMN=""
FILTER_VALUE=""
SORT_COLUMN=""
AGGREGATE_COLUMN=""
AGGREGATE_TYPE="sum"
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# 日志函数
log() {
local level=$1
shift
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$timestamp] [$level] $@"
}
log_info() {
log "INFO" "$@"
}
log_error() {
log "ERROR" "$@"
}
# 检查CSV文件
check_csv_file() {
if [ ! -f "$INPUT_FILE" ]; then
log_error "CSV文件不存在: $INPUT_FILE"
return 1
fi
if [ ! -r "$INPUT_FILE" ]; then
log_error "CSV文件不可读: $INPUT_FILE"
return 1
fi
return 0
}
# 读取CSV文件
read_csv() {
local file=$1
log_info "读取CSV文件: $file"
if [ "$HAS_HEADER" = true ]; then
# 显示表头
head -1 "$file"
# 显示数据
tail -n +2 "$file"
else
cat "$file"
fi
}
# 显示CSV信息
show_csv_info() {
log_info "显示CSV文件信息"
local total_lines=$(wc -l < "$INPUT_FILE")
local data_lines=$((total_lines - 1))
local columns=$(head -1 "$INPUT_FILE" | awk -F"$DELIMITER" '{print NF}')
local file_size=$(du -h "$INPUT_FILE" | cut -f1)
echo "CSV文件信息"
echo "==========="
echo "文件名: $INPUT_FILE"
echo "文件大小: $file_size"
echo "总行数: $total_lines"
echo "数据行数: $data_lines"
echo "列数: $columns"
echo "分隔符: $DELIMITER"
echo ""
echo "列名:"
head -1 "$INPUT_FILE" | awk -F"$DELIMITER" '{for(i=1;i<=NF;i++) print " "i": "$i}'
}
# 过滤CSV数据
filter_csv() {
local file=$1
local column=$2
local value=$3
log_info "过滤CSV数据: 列$column=$value"
# 获取列号
local col_num=$(head -1 "$file" | awk -F"$DELIMITER" -v col="$column" '{for(i=1;i<=NF;i++) if($i==col) print i}')
if [ -z "$col_num" ]; then
log_error "列不存在: $column"
return 1
fi
# 过滤数据
if [ "$HAS_HEADER" = true ]; then
# 显示表头
head -1 "$file"
# 过滤数据行
tail -n +2 "$file" | awk -F"$DELIMITER" -v col="$col_num" -v val="$value" '$col == val'
else
awk -F"$DELIMITER" -v col="$col_num" -v val="$value" '$col == val' "$file"
fi
}
# 排序CSV数据
sort_csv() {
local file=$1
local column=$2
log_info "排序CSV数据: 列$column"
# 获取列号
local col_num=$(head -1 "$file" | awk -F"$DELIMITER" -v col="$column" '{for(i=1;i<=NF;i++) if($i==col) print i}')
if [ -z "$col_num" ]; then
log_error "列不存在: $column"
return 1
fi
# 排序数据
if [ "$HAS_HEADER" = true ]; then
# 显示表头
head -1 "$file"
# 排序数据行
tail -n +2 "$file" | sort -t"$DELIMITER" -k"$col_num"
else
sort -t"$DELIMITER" -k"$col_num" "$file"
fi
}
# 聚合CSV数据
aggregate_csv() {
local file=$1
local column=$2
local type=$3
log_info "聚合CSV数据: 列$column, 类型=$type"
# 获取列号
local col_num=$(head -1 "$file" | awk -F"$DELIMITER" -v col="$column" '{for(i=1;i<=NF;i++) if($i==col) print i}')
if [ -z "$col_num" ]; then
log_error "列不存在: $column"
return 1
fi
# 聚合数据
if [ "$HAS_HEADER" = true ]; then
# 跳过表头
tail -n +2 "$file"
else
cat "$file"
fi | awk -F"$DELIMITER" -v col="$col_num" -v type="$type" '
{
if ($col ~ /^[0-9.]+$/) {
values[NR] = $col
sum += $col
count++
if (NR == 1 || $col < min) min = $col
if (NR == 1 || $col > max) max = $col
}
}
END {
if (type == "sum") print sum
else if (type == "avg") print sum / count
else if (type == "min") print min
else if (type == "max") print max
else if (type == "count") print count
else print "Unknown type: " type
}'
}
# 统计CSV数据
stats_csv() {
local file=$1
log_info "统计CSV数据"
echo "数据统计"
echo "========"
# 获取列名
local columns=($(head -1 "$file" | awk -F"$DELIMITER" '{for(i=1;i<=NF;i++) print $i}'))
# 统计每列
for i in "${!columns[@]}"; do
local col_num=$((i + 1))
local col_name="${columns[$i]}"
echo ""
echo "列: $col_name"
# 检查是否为数值列
local is_numeric=$(tail -n +2 "$file" | awk -F"$DELIMITER" -v col="$col_num" '
{
if ($col !~ /^[0-9.]+$/) {
print "false"
exit
}
}
END {
print "true"
}')
if [ "$is_numeric" = "true" ]; then
# 数值统计
tail -n +2 "$file" | awk -F"$DELIMITER" -v col="$col_num" '
{
if ($col ~ /^[0-9.]+$/) {
sum += $col
count++
if (NR == 1 || $col < min) min = $col
if (NR == 1 || $col > max) max = $col
}
}
END {
if (count > 0) {
print " 总和: " sum
print " 平均: " sum / count
print " 最小: " min
print " 最大: " max
print " 数量: " count
} else {
print " 无有效数据"
}
}'
else
# 文本统计
tail -n +2 "$file" | awk -F"$DELIMITER" -v col="$col_num" '
{
values[$col]++
count++
}
END {
print " 总数: " count
print " 唯一值: " length(values)
print " 前5个值:"
for (val in values) {
items[val] = values[val]
}
n = asorti(items, sorted, "@val_num_desc")
for (i = 1; i <= n && i <= 5; i++) {
print " " sorted[i] ": " items[sorted[i]]
}
}'
fi
done
}
# 合并CSV文件
merge_csv() {
local files=($@)
log_info "合并CSV文件"
# 显示第一个文件的表头
head -1 "${files[0]}"
# 合并所有文件的数据(跳过表头)
for file in "${files[@]}"; do
tail -n +2 "$file"
done
}
# 转换CSV格式
convert_csv() {
local file=$1
local new_delimiter=$2
log_info "转换CSV分隔符: $DELIMITER -> $new_delimiter"
sed "s/$DELIMITER/$new_delimiter/g" "$file"
}
# 导出为JSON
export_json() {
local file=$1
log_info "导出为JSON格式"
# 获取列名
local columns=($(head -1 "$file" | awk -F"$DELIMITER" '{for(i=1;i<=NF;i++) print $i}'))
echo "["
# 处理数据行
local first=true
tail -n +2 "$file" | while IFS="$DELIMITER" read -ra row; do
if [ "$first" = false ]; then
echo ","
fi
first=false
echo -n " {"
# 处理每列
for i in "${!row[@]}"; do
if [ $i -gt 0 ]; then
echo -n ", "
fi
local col_name="${columns[$i]}"
local col_value="${row[$i]}"
# 转义特殊字符
col_value=$(echo "$col_value" | sed 's/\\/\\\\/g' | sed 's/"/\\"/g')
echo -n "\"$col_name\": \"$col_value\""
done
echo "}"
done
echo "]"
}
# 显示帮助
show_help() {
echo "用法: $0 [选项] <操作> [参数]"
echo ""
echo "选项:"
echo " -i <文件> 输入CSV文件"
echo " -o <文件> 输出文件"
echo " -d <分隔符> CSV分隔符(默认: ,)"
echo " -H 无表头"
echo " -h 显示帮助信息"
echo ""
echo "操作:"
echo " info 显示CSV信息"
echo " read 读取CSV数据"
echo " filter <列> <值> 过滤数据"
echo " sort <列> 排序数据"
echo " aggregate <列> <类型> 聚合数据(sum/avg/min/max/count)"
echo " stats 统计数据"
echo " merge <文件>... 合并CSV文件"
echo " convert <新分隔符> 转换分隔符"
echo " json 导出为JSON"
echo ""
echo "示例:"
echo " $0 -i data.csv info"
echo " $0 -i data.csv filter \"name\" \"John\""
echo " $0 -i data.csv sort \"age\""
echo " $0 -i data.csv aggregate \"salary\" \"sum\""
echo " $0 -i data.csv stats"
echo " $0 -i data.csv json"
}
# 主函数
main() {
# 解析选项
while getopts "i:o:d:Hh" opt; do
case $opt in
i)
INPUT_FILE="$OPTARG"
log_info "输入文件: $INPUT_FILE"
;;
o)
OUTPUT_FILE="$OPTARG"
log_info "输出文件: $OUTPUT_FILE"
;;
d)
DELIMITER="$OPTARG"
log_info "分隔符: $DELIMITER"
;;
H)
HAS_HEADER=false
log_info "无表头模式"
;;
h)
show_help
exit 0
;;
*)
log_error "无效选项: $opt"
show_help
exit 1
;;
esac
done
shift $((OPTIND - 1))
# 检查输入文件
if [ -z "$INPUT_FILE" ]; then
log_error "缺少输入文件"
show_help
exit 1
fi
if ! check_csv_file; then
exit 1
fi
# 检查操作
if [ $# -eq 0 ]; then
log_error "缺少操作"
show_help
exit 1
fi
OPERATION=$1
shift
# 执行操作
case $OPERATION in
info)
show_csv_info
;;
read)
read_csv "$INPUT_FILE"
;;
filter)
if [ $# -lt 2 ]; then
log_error "缺少过滤参数"
exit 1
fi
filter_csv "$INPUT_FILE" "$1" "$2"
;;
sort)
if [ $# -eq 0 ]; then
log_error "缺少排序列"
exit 1
fi
sort_csv "$INPUT_FILE" "$1"
;;
aggregate)
if [ $# -lt 2 ]; then
log_error "缺少聚合参数"
exit 1
fi
aggregate_csv "$INPUT_FILE" "$1" "$2"
;;
stats)
stats_csv "$INPUT_FILE"
;;
merge)
if [ $# -eq 0 ]; then
log_error "缺少合并文件"
exit 1
fi
merge_csv "$@"
;;
convert)
if [ $# -eq 0 ]; then
log_error "缺少新分隔符"
exit 1
fi
convert_csv "$INPUT_FILE" "$1"
;;
json)
export_json "$INPUT_FILE"
;;
*)
log_error "无效的操作: $OPERATION"
show_help
exit 1
;;
esac
}
# 执行主函数
main "$@"使用说明#
添加执行权限:
chmod +x csv_processor.sh基本用法:
# 显示CSV信息 ./csv_processor.sh -i data.csv info # 读取CSV数据 ./csv_processor.sh -i data.csv read # 过滤数据 ./csv_processor.sh -i data.csv filter "name" "John" # 排序数据 ./csv_processor.sh -i data.csv sort "age" # 聚合数据 ./csv_processor.sh -i data.csv aggregate "salary" "sum" # 统计数据 ./csv_processor.sh -i data.csv stats高级用法:
# 导出为JSON ./csv_processor.sh -i data.csv json > output.json # 转换分隔符 ./csv_processor.sh -i data.csv convert ";" > output.csv # 合并CSV文件 ./csv_processor.sh -i data1.csv merge data2.csv data3.csv > merged.csv # 输出到文件 ./csv_processor.sh -i data.csv -o output.csv filter "status" "active"
功能特点#
- 读取和显示CSV数据
- 显示文件信息
- 数据过滤
- 数据排序
- 数据聚合(求和、平均、最小、最大、计数)
- 数据统计
- 文件合并
- 分隔符转换
- JSON导出
依赖项#
- awk: 用于文本处理
- sed: 用于文本替换
- sort: 用于排序
注意事项#
- 确保CSV文件格式正确
- 分隔符需要正确指定
- 大文件处理可能需要较长时间
- 聚合操作只对数值列有效
- JSON导出需要正确处理特殊字符