September 2, 2017

แยกไฟล์ใหญ่ๆ ให้เป็นบรรทัดย่อยๆด้วย Command line

แยกไฟล์ใหญ่ๆ ให้เป็นบรรทัดย่อยๆด้วย Command line

พอดีว่าต้อง export ไฟล์ csv ราวๆ 160,000 บรรทัด ได้ไฟล์มาขนาด 200MB ส่งไฟล์ต่อไปให้คนที่ขอ ...เปิดไม่ได้ ไฟล์ใหญ่เกินไป แต่คอมพับผม MacBook Air ใช้ LibreOffice เปิดได้น่ะ


Photo by Ryan Grewell / Unsplash

ก็เลยได้ลองใช้คำสั่ง split ที่อยู่ใน *nix ผมใช้บน Mac OS X (ยังไม่อัปเกรดเป็น macOS เพราะอัปแล้วพังไป 3 รอบ) ขนาดไฟล์ก็เท่านี้

ls -lh export.csv
[email protected] 1 narate staff 237M Sep 2 12:25 export.csv

ไฟล์ขนาด 237MB ก็ใหญ่อยู่น่ะ แต่ไม่ถึงกับว่าใหญ่สัสสส เคย export csv มาไฟล์ 1.x GB ด้วยน่ะ แต่อันนั้นไม่กล้าเอา LibreOffice เปิด 555555 แค่สั่ง cat ยังไม่กล้าเลย มาดูจำนวนบรรทัด

wc -l export.csv
160587 export.csv

มี 160,587 บรรทัด (ตัวแปร A) ผมเลยจะแยกไฟล์ ให้มีสัก 8,000 บรรทัดต่อไฟล์ (ตัวแปร B) ก่อนอื่นเลย ทำการแยก csv บรรทัดที่ 1 ออกไปก่อน โดยใช้คำสั่ง head

head -n1 export.csv > headers.txt

จากนั้นลบบรรทัดที่ 1 ในไฟล์ export.csv ออกด้วยคำสั่ง sed

sed -i '1d' export.csv

ตอนนี้ไฟล์ export.csv จะไม่มีหัวแล้ว คือ csv พิการน่ะ มีแต่ส่วนของข้อมูลล้วนๆ เดี๋ยวค่อยมาประกอบร่างทีหลัง มาแยกร่างมันก่อน ด้วยคำสั่ง split

split -l 8000 export.csv split_

-l 8000 อันนี้คือ จำนวนบรรทัดแต่ละไฟล์
export.csv คือ ชื่อไฟล์
split_ คือ prefix ของไฟล์ที่แตกออกมา ปิ๊ดๆๆ หลังจากรันคำสั่งไป จะได้ไฟล์ชื่อ split_aa, split_ab, ... เรื่อยๆ เช็คดูว่าแต่ละไฟล์ มีกี่บรรทัดด้วยคำสั่ง wc -l

wc -l split_a*

จะได้

    8000 split_aa
    8000 split_ab
    8000 split_ac
    8000 split_ad
    8000 split_ae
    8000 split_af
    8000 split_ag
    8000 split_ah
    8000 split_ai
    8000 split_aj
    8000 split_ak
    8000 split_al
    8000 split_am
    8000 split_an
    8000 split_ao
    8000 split_ap
    8000 split_aq
    8000 split_ar
    8000 split_as
    8000 split_at
     587 split_au
  160587 total

มี 8,000 บรรทัดเกือบทุกไฟล์ ไฟล์สุดท้าย คือเศษที่เหลือจาก A ÷ B ตอนนี้มันก็ยังพิการอยู่ เพราะไม่มีหัวของ csv ทำการรวมมันกลับมาคืน อันนี้คำสั่งยาวหน่อย เขียนมันใส่ไฟล์เป็น shell script แล้วกัน ตั้งชื่อ merge.sh

#!/bin/bash
COUNT=0
for f in split_*
do
  ((COUNT++))
  FILENAME=$(printf "%02d" $COUNT)
  echo "Merging $f..."
  cat headers.txt $f > "export-$FILENAME.csv"
done
echo "done."

คำสั่งก็คือ cat เอา headers.txt ออกมา และ cat ไฟล์ที่ split มาหลังจาก headers.txt แล้ว redirect มันไปใส่ไฟล์ใหม่ ขั้นตอนสุดท้าย คือ รันคำสั่ง เอาง่ายๆเลย

sh merger.sh

จากนั้นจะได้ไฟล์ export-* แล้วเช็คจำนวนบรรทัดของไฟล์ที่ได้

wc -l export-*

จะได้

    8001 export-01.csv
    8001 export-02.csv
    8001 export-03.csv
    8001 export-04.csv
    8001 export-05.csv
    8001 export-06.csv
    8001 export-07.csv
    8001 export-08.csv
    8001 export-09.csv
    8001 export-10.csv
    8001 export-11.csv
    8001 export-12.csv
    8001 export-13.csv
    8001 export-14.csv
    8001 export-15.csv
    8001 export-16.csv
    8001 export-17.csv
    8001 export-18.csv
    8001 export-19.csv
    8001 export-20.csv
     588 export-21.csv
  160608 total

จบ

echo $?