首页 > 解决方案 > 难以从 bash 中的循环创建 .txt 文件

问题描述

我有这个数据:

cat >data1.txt <<'EOF'
2020-01-27-06-00;/dev/hd1;100;/
2020-01-27-12-00;/dev/hd1;100;/
2020-01-27-18-00;/dev/hd1;100;/
2020-01-27-06-00;/dev/hd2;200;/usr
2020-01-27-12-00;/dev/hd2;200;/usr
2020-01-27-18-00;/dev/hd2;200;/usr
EOF

cat >data2.txt <<'EOF'
2020-02-27-06-00;/dev/hd1;120;/
2020-02-27-12-00;/dev/hd1;120;/
2020-02-27-18-00;/dev/hd1;120;/
2020-02-27-06-00;/dev/hd2;230;/usr
2020-02-27-12-00;/dev/hd2;230;/usr
2020-02-27-18-00;/dev/hd2;230;/usr
EOF

cat >data3.txt <<'EOF'
2020-03-27-06-00;/dev/hd1;130;/
2020-03-27-12-00;/dev/hd1;130;/
2020-03-27-18-00;/dev/hd1;130;/
2020-03-27-06-00;/dev/hd2;240;/usr
2020-03-27-12-00;/dev/hd2;240;/usr
2020-03-27-18-00;/dev/hd2;240;/usr
EOF

我想为每个文件系统创建一个 .txt 文件(所以 hd1.txt、hd2.txt、hd3.txt 和 hd4.txt )并在每个 .txt 文件中放入来自每个 dataX.txt 的每个 FS 的值的总和. 我很难用英语解释我想要什么,所以这里有一个想要的结果示例

输出文件的预期内容hd1.txt

2020-01;/dev/hd1;300;/
2020-02;/dev/hd1;360;/
2020-03;/dev/hd1;390:/

文件的预期内容hd2.txt

2020-01;/dev/hd2;600;/usr
2020-02;/dev/hd2;690;/usr
2020-03;/dev/hd2;720;/usr

我目前尝试的实现:

for i in $(cat *.txt | awk -F';' '{print $2}' | cut -d '/' -f3| uniq)
do
    cat *.txt | grep -w $i | awk -F';' -v date="$(cat *.txt | awk -F';' '{print $1}' | cut -d'-' -f-2 | uniq )" '{sum+=$3} END {print date";"$2";"sum}' >> $i

done

但它不起作用...

你能告诉我怎么做吗?

标签: linuxbashloopsfile

解决方案


因为格式似乎很固定,所以您可以使用多个分隔符分隔输入并在 awk 中轻松解析:

awk -v FS='[;-/]' '
prev != $9 {
    if (length(output)) {
        print output >> fileoutput
    }
    prev = $9
    sum = 0
}
{
    sum += $9
    output = sprintf("%s-%s;/%s/%s;%d;/%s", $1, $2, $7, $8, sum, $11)
    fileoutput = $8 ".txt"
}
END {
    print output >> fileoutput
}
' *.txt

在 repl 上测试生成:

+ cat hd1.txt
2020-01;/dev/hd1;300;/
2020-02;/dev/hd1;360;/
2020-03;/dev/hd1;390;/
+ cat hd2.txt
2020-01;/dev/hd2;600;/usr
2020-02;/dev/hd2;690;/usr
2020-03;/dev/hd2;720;/usr

或者,您可以-v FS=';'使用split拆分第一列和第二列来提取年份和月份以及hdX数字。

如果您寻求 bash 解决方案,我建议您反转循环 - 首先遍历文件,然后遍历第二列中的标识符。

for file in *.txt; do
    prev=
    output=
    while IFS=';' read -r date dev num path; do
        hd=$(basename "$dev")
        if [[ "$hd" != "${prev:-}" ]]; then
            if ((${#output})); then
                printf "%s\n" "$output" >> "$fileoutput"
            fi
            sum=0
            prev="$hd"
        fi
        sum=$((sum + num))
        output=$(
            printf "%s;%s;%d;%s" \
            "$(cut -d'-' -f1-2 <<<"$date")" \
            "$dev" "$sum" "$path"
        )
        fileoutput="${hd}.txt"
    done < "$file"
    printf "%s\n" "$output" >> "$fileoutput"
done

您也几乎可以通过IFS='-;/'在 while read 循环中将 awk 转换为 bash 1:1。


推荐阅读