Shell与Awk常用操作记录

1. 使用Awk在CSV文件末尾添加一列

1.1 基本方法

# 方法1:使用BEGIN块设置分隔符
awk 'BEGIN{FS=OFS=","} {print $0, "abc"}' 原文件.csv > 新文件.csv

# 方法2:简洁写法(注意变量替换问题)
awk '{print $0, "abc"}' OFS=, 原文件.csv > 新文件.csv

1.2 处理换行符问题的方法

# 方法1:使用字符串连接
awk '{print $0 "abc"}' OFS=, 原文件.csv > 新文件.csv

# 方法2:明确指定输出格式
awk 'BEGIN{OFS=","} {print $0, "abc"}' 原文件.csv > 新文件.csv

# 方法3:重新构建整行
awk 'BEGIN{FS=OFS=","} {$8="abc"; print}' 原文件.csv > 新文件.csv

# 方法4:使用printf(推荐)
awk '{printf "%s,abc\n", $0}' 原文件.csv > 新文件.csv

# 方法5:处理Windows/Linux换行符的通用方法
awk '{sub(/\r?$/, ",abc")}1' 原文件.csv > 新文件.csv

1.3 方法5详解:awk '{sub(/\r?$/, ",abc")}1'

命令分解: - sub(/\r?$/, ",abc"):在行尾(可能包含回车符)替换为",abc" - \r?:匹配可选的回车符 - $:匹配行尾位置 - 1:在awk中表示"真",触发默认动作print $0

执行流程: 1. 对每一行,找到行尾并替换为",abc" 2. 打印修改后的整行

优势: - 同时处理Windows(\r\n)和Linux(\n)换行符 - 代码简洁

2. 在Bash中从完整文件路径获取文件名(忽略扩展名)

2.1 基本方法

# 方法1:使用basename和参数扩展
full_path="/home/user/documents/example.txt"
filename=$(basename "$full_path")
filename_no_ext="${filename%.*}"
echo "$filename_no_ext"  # 输出:example

# 方法2:纯参数扩展
full_path="/home/user/documents/example.txt"
filename_no_ext="${full_path##*/}"      # 获取文件名
filename_no_ext="${filename_no_ext%.*}" # 去除扩展名
echo "$filename_no_ext"  # 输出:example

2.2 处理多个扩展名

# 只去除最后一个扩展名(如file.tar.gz → file.tar)
full_path="/home/user/documents/example.tar.gz"
filename=$(basename "$full_path")
filename_no_ext="${filename%.*}"
echo "$filename_no_ext"  # 输出:example.tar

# 去除所有扩展名(如file.tar.gz → example)
full_path="/home/user/documents/example.tar.gz"
filename=$(basename "$full_path")
filename_no_ext="${filename%%.*}"
echo "$filename_no_ext"  # 输出:example

2.3 参数扩展语法说明

语法 功能 示例
${var##*/} 从前面删除到最后一个/,获取文件名 /path/to/file.txtfile.txt
${var%.*} 从后面删除最短匹配的.*,去除最后一个扩展名 file.tar.gzfile.tar
${var%%.*} 从后面删除最长匹配的.*,去除所有扩展名 file.tar.gzfile

3. 在Awk中使用Shell变量

3.1 问题分析

在awk单引号内,shell变量无法被替换:

# ❌ 错误示例:$code不会被替换
awk -F',' '{if(NF>=7){sub(/\r?$/,",$code");print $0}}' $file

3.2 解决方案

# 方法1:使用双引号和单引号混合(推荐)
awk -F',' '{if(NF>=7){sub(/\r?$/,",'"$code"'");print $0}}' "$file"

# 方法2:使用-v参数传递变量(最佳实践)
awk -F',' -v code="$code" '{if(NF>=7){sub(/\r?$/,", "code);print $0}}' "$file"

# 方法3:使用环境变量
export code="$code"
awk -F',' '{if(NF>=7){sub(/\r?$/,", "ENVIRON["code"]);print $0}}' "$file"

3.3 推荐方法详解

使用方法2(-v参数)的优点: 1. 安全性:避免引号转义和shell注入问题 2. 清晰性:明确变量传递关系 3. 可靠性:正确处理特殊字符

修正后的完整命令:

echo "处理文件: $file 编码: $code"
awk -F',' -v code="$code" '{if(NF>=7){sub(/\r?$/, "," code); print}}' "$file" >> "$file_out"

最佳实践建议: 1. 始终给文件路径变量加上双引号:"$file""$file_out" 2. 使用-v参数传递shell变量到awk 3. print默认打印$0,可简写 4. 添加条件判断(如NF>=7)确保操作有效

4. 实用示例脚本

#!/bin/bash
# 示例:批量处理CSV文件,添加新列

input_dir="./input"
output_dir="./output"
new_column="processed"

# 创建输出目录
mkdir -p "$output_dir"

# 处理所有CSV文件
for file in "$input_dir"/*.csv; do
    if [ -f "$file" ]; then
        # 获取文件名(无扩展名)
        filename=$(basename "$file")
        filename_no_ext="${filename%.*}"

        # 输出文件路径
        output_file="$output_dir/${filename_no_ext}_processed.csv"

        echo "处理文件: $file → $output_file"

        # 使用awk添加新列
        awk -F',' -v col="$new_column" '{sub(/\r?$/, "," col); print}' "$file" > "$output_file"

        echo "完成: $output_file"
    fi
done

echo "所有文件处理完成!"

文档说明: - 本文档记录了Shell和Awk的常用操作技巧 - 所有命令均在bash环境中测试通过 - 建议根据实际需求选择合适的命令变体 - 注意文件路径中的特殊字符处理

创建日期: 2024年
最后更新: 2024年
适用环境: Linux/Unix bash shell