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 "$@"

使用说明#

  1. 添加执行权限:

    chmod +x csv_processor.sh
  2. 基本用法:

    # 显示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
  3. 高级用法:

    # 导出为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: 用于排序

注意事项#

  1. 确保CSV文件格式正确
  2. 分隔符需要正确指定
  3. 大文件处理可能需要较长时间
  4. 聚合操作只对数值列有效
  5. JSON导出需要正确处理特殊字符