บล็อกนี้ทำด้วย Hugo เก็บ content ที่เป็นโพสต์ต่างๆ ไว้ในโฟลเดอร์ content/post/<POST_NAME>/
แต่ยังมีโพสต์เก่าๆ ที่ยังไม่ได้อยู่ในโฟลเดอร์ของตัวเอง หลงเหลืออยู่ เป็นโพสต์ที่ย้ายมาจากบล็อก Jekyll เมื่อก่อนนู้น
ชื่อไฟล์จะอยู่ในรูปแบบ YYYY-MM-DD-post-slug.markdown
อยากจัดระเบียบให้มันเหมือนๆ กัน เป็นงานที่ดองไว้นานแล้วแต่ไม่ได้ทำ
ภารกิจ
ภารกิจคือ ย้ายไฟล์
- จาก
content/post/2011-03-29-hello-world.markdown
- ไป
content/post/hello-world/index.md
ซึ่งมีไฟล์ต้องย้ายอยู่ 140 กว่าไฟล์ ถ้าต้องมาสร้างโฟลเดอร์ใหม่ และย้ายไฟล์เองด้วยมือทุกไฟล์ คงเป็นงานที่น่าเบื่อน่าดู
ผมมีความคิดจะเขียน shell script ขึ้นมาเพื่อ automate process นี้
ถึงแม้ว่าผมจะเขียน shell script ไม่เป็น แต่เพราะไม่อยากทำงานซ้ำๆ ก็เลยใช้โอกาสนี้ในการเรียนรู้ใหม่อีกครั้ง
(ผมเคยเรียนเขียน shell script ในวิชา basic linux ตอนเรียนมหาลัยปี 2 แต่ก็ลืมไปหมดแล้ว) 😅
วางแผน
- ใช้ shell script
- loop ไฟล์
.markdown
ทั้งหมดในโฟลเดอร์content/post/
- ตัดส่วนวันที่ออกจากชื่อไฟล์
20xx-xx-xx-
(เอาออกได้เพราะข้อมูลวันที่อยู่ใน front-matter ของแต่ละไฟล์อยู่แล้ว) - ตัดส่วน extension
.markdown
ออกจากชื่อไฟล์ - ก็จะเหลือ post slug ตรงกลาง สร้างโฟลเดอร์ใหม่จากชื่อนี้
- ย้ายไฟล์
.markdown
ไปเป็นindex.md
ในโฟลเดอร์ที่สร้างใหม่
1. สร้างไฟล์ script
ผมเริ่มจากการสร้างไฟล์ shell script ไว้ข้างในโฟลเดอร์เดียวกับไฟล์ที่จะทำงาน
พร้อมกับทำให้มันรันได้ ด้วยการเปลี่ยน permission เป็น +x
(executable)
$ cd content/post/
$ touch rename.sh
$ chmod +x rename.sh
คุ้นๆ ว่าในบรรทัดแรกของ shell script มันต้องมีอะไรพิเศษสักอย่าง
เลยเสิชกูเกิลด้วย keyword bash first line
ก็พบว่า บรรทัดนั้นคือ shebang หรือ sha-bang หรือ hashbang
มีไว้เลือก shell ว่าจะรัน script ใน shell ไหน
ผมเลือก bash
ไว้ก่อนเพราะคิดว่าเซฟสุด
#!/bin/bash
2. loop ไฟล์ .markdown
จากนั้นก็เสิชหาวิธีการ loop ไฟล์ในโฟลเดอร์นั้นทำยังไงด้วย keyword: bash list files in directory
เจอคำตอบจาก StackOverflow
ก็คือใช้ command ls
นั่นเอง
#!/bin/bash
markdownfiles=`ls *.markdown`
for file in $markdownfiles
do
# something ...
echo $file
done
- list file markdown ด้วย
ls
- ใช้
`
(backtick) เพื่อเก็บ output จาก command ไว้ใน variable - จากนั้น loop ด้วย
for ... in
- เปิด block ด้วย
do
จบด้วยdone
- เวลาสร้าง variable ไม่ต้องใช้
$
แต่เวลาเรียกใช้ ต้องมี$
ข้างหน้า - เว้นวรรคตรงเครื่องหมาย
=
ไม่ได้ ต้องเขียนติดกัน (เว้นวรรคถือว่าเป็น argument ของ command)
3. ตัดวันที่ชื่อออกจากชื่อไฟล์
วันที่ในชื่อไฟล์อยู่ใน pattern เดียวกันหมด คือ นำหน้าด้วย YYYY-MM-DD-
เป็นตัวอักษร 11 ตัวแรกของชื่อไฟล์ ก็ใช้วิธีบ้านๆ เลย คือ การตัด string
ผมเสิชหาด้วย keyword bash cut string
เจอกระทู้ใน StackOverflow อีกแล้ว
#!/bin/bash
markdownfiles=`ls *.markdown`
for file in $markdownfiles
do
filename=${file:11}
done
${file:11}
ตัดตัวอักษร 11 ตัวแรก แล้ว return ส่วนที่เหลือ- แต่ถ้าเป็น
${file::11}
(มี:
สองอัน) คือตัดเอาเฉพาะ 11 ตัวแรก
4. ตัด .markdown
ออกจากชื่อไฟล์
ใช้วิธีตัด string เหมือนเดิม แต่เนื่องจากส่วน .markdown
อยู่ท้าย string
จะใช้วิธีนับ index แบบเดิมไม่ได้แล้ว เพราะชื่อไฟล์แต่ละไฟล์ยาวไม่เท่ากัน
ก็เลยให้มันตัด string หลังจากตัวจุด .
ออกไปแทน
#!/bin/bash
markdownfiles=`ls *.markdown`
for file in $markdownfiles
do
filename=${file:11}
foldername=${filename%.*}
done
%.
ตัดตัวอักษรตั้งแต่.
เป็นต้นไป แล้ว return ส่วนที่เหลือ
5. เก็บส่วนที่เหลือไว้ สร้างโฟลเดอร์ใหม่
จาก variable ที่ได้จากขั้นตอนที่แล้ว เอาไปสร้างโฟลเดอร์ใหม่ด้วย mkdir
.
#!/bin/bash
markdownfiles=`ls *.markdown`
for file in $markdownfiles
do
filename=${file:11}
foldername=${filename%.*}
mkdir $foldername
done
6. ย้ายไฟล์เดิมไปที่ใหม่
ใช้ mv
#!/bin/bash
markdownfiles=`ls *.markdown`
for file in $markdownfiles
do
filename=${file:11}
foldername=${filename%.*}
mkdir $foldername
mv $file $foldername/index.md
done
สุดท้ายก็ run script
$ ./rename.sh
content ทุกโพสต์ก็ถูกจัดระเบียบให้เหมือนกันแล้ว
ผมเชื่อว่าคนที่เก่ง unix command line / shell script สามารถทำให้ code 10 บรรทัดของผม อยู่ใน 1 บรรทัด (one-liner) ได้อย่างไม่ยากนัก หรือไม่ก็ใช้แอพ Automator ที่ติดมากับเครื่องอยู่แล้ว ก็น่าจะใช้ทำงานนี้ได้เหมือนกัน
เพราะการขี้เกียจทำงานซ้ำๆ เลยยอมเสียเวลาเสิช google นิดหน่อย หรือลองใช้เครื่องไม้เครื่องมือ เพื่อหาวิธีลดการทำงานที่ซ้ำๆ นั้นลง
แต่ก็ไม่ได้หมายความว่า ต้องเกิดจากความขี้เกียจเท่านั้นนะครับ
ตัวอย่างเช่น video เรื่อง Problem-Solving for Developers - A Beginner’s Guide ของช่อง Fireship.io เขียน script มาเพื่อจะได้ไม่ต้องกด merge pull requests กว่า 600 อันบน GitHub แบบ manual เพราะโจทย์มันใหญ่เกินกว่าจะทำแบบ manual ได้จริงๆ
แนะนำให้ไปชมวิธีการแก้โจทย์ของเขาครับ