Filepath #
Path file adalah salah satu hal yang paling sering salah ditangani dalam kode yang perlu berjalan di berbagai sistem operasi. Di Windows, separator path adalah \, sementara di Linux dan macOS adalah /. Menggabungkan path dengan string concatenation biasa (dir + "/" + file) akan rusak di Windows. Package path/filepath menyelesaikan masalah ini dengan menyediakan fungsi-fungsi manipulasi path yang otomatis menggunakan separator yang tepat untuk sistem operasi yang sedang berjalan. Di samping portabilitas, filepath juga menyediakan WalkDir untuk traversal direktori rekursif, Glob untuk pencarian file dengan wildcard, serta fungsi-fungsi untuk memecah dan menganalisis komponen path.
Gambaran Besar Package path/filepath #
flowchart TD
FP["package path/filepath"] --> Build["Membangun Path"]
FP --> Split["Memecah Path"]
FP --> Convert["Konversi Path"]
FP --> Search["Pencarian File"]
FP --> Walk["Traversal Direktori"]
Build --> B1["filepath.Join\ngabungkan komponen path"]
Build --> B2["filepath.Abs\npath relatif → absolut"]
Build --> B3["filepath.FromSlash\n'/' → separator OS"]
Build --> B4["filepath.ToSlash\nseparator OS → '/'"]
Split --> S1["filepath.Split\n→ dir, file"]
Split --> S2["filepath.Dir\n→ direktori saja"]
Split --> S3["filepath.Base\n→ nama file saja"]
Split --> S4["filepath.Ext\n→ ekstensi saja"]
Split --> S5["filepath.SplitList\n→ pisahkan PATH env"]
Convert --> C1["filepath.Abs\npath → absolut"]
Convert --> C2["filepath.Rel\npath absolut → relatif"]
Convert --> C3["filepath.Clean\nnormalisasi path"]
Convert --> C4["filepath.EvalSymlinks\nresolve symlink"]
Search --> G1["filepath.Glob\n'*.go', '**/*.txt'"]
Search --> G2["filepath.Match\ncocokkan pola"]
Walk --> W1["filepath.WalkDir\nrekursif dengan DirEntry"]
style FP fill:#4f86c6,color:#fff
style Build fill:#e8f5e9
style Split fill:#e3f2fd
style Convert fill:#fff3e0
style Search fill:#f3e5f5
style Walk fill:#fce4ecfilepath.Join — Membangun Path dengan Benar #
filepath.Join adalah fungsi yang paling sering digunakan dari package ini. Ia menggabungkan komponen path dengan separator yang tepat untuk sistem operasi saat ini, dan secara otomatis membersihkan path yang dihasilkan:
package main
import (
"fmt"
"path/filepath"
)
func main() {
// ANTI-PATTERN: konkatenasi manual — rusak di Windows
path1 := "/home/user" + "/" + "dokumen" + "/" + "file.txt"
// Di Windows, ini menghasilkan path yang salah
// BENAR: gunakan filepath.Join
path2 := filepath.Join("/home/user", "dokumen", "file.txt")
fmt.Println(path2)
// Linux/macOS: /home/user/dokumen/file.txt
// Windows: \home\user\dokumen\file.txt
// Join membersihkan path secara otomatis
fmt.Println(filepath.Join("/home/user/", "/dokumen/../file.txt"))
// /home/user/file.txt — bukan /home/user//dokumen/../file.txt
// Join dengan nol atau satu argumen
fmt.Println(filepath.Join()) // ""
fmt.Println(filepath.Join("file")) // "file"
fmt.Println(filepath.Join("a", "")) // "a"
// Membangun path dari variabel
homeDir := "/home/budi"
appDir := filepath.Join(homeDir, ".config", "myapp")
configFile := filepath.Join(appDir, "config.yaml")
logFile := filepath.Join(appDir, "logs", "app.log")
fmt.Println(appDir) // /home/budi/.config/myapp
fmt.Println(configFile) // /home/budi/.config/myapp/config.yaml
fmt.Println(logFile) // /home/budi/.config/myapp/logs/app.log
// Join dengan path absolut di tengah — path sebelumnya diabaikan!
fmt.Println(filepath.Join("/home", "/etc", "passwd"))
// PERHATIAN: di beberapa implementasi, "/etc" bisa mengabaikan "/home"
// Selalu gunakan path relatif untuk komponen tengah
}
Memecah Path — Dir, Base, Ext, Split #
path := "/home/budi/dokumen/laporan-2024.pdf"
// Dir — direktori (tanpa file)
fmt.Println(filepath.Dir(path))
// /home/budi/dokumen
// Base — nama file (dengan ekstensi)
fmt.Println(filepath.Base(path))
// laporan-2024.pdf
// Ext — ekstensi file (termasuk titik)
fmt.Println(filepath.Ext(path))
// .pdf
// Nama file tanpa ekstensi — tidak ada fungsi langsung, kombinasikan
nama := filepath.Base(path)
namaNoExt := nama[:len(nama)-len(filepath.Ext(nama))]
fmt.Println(namaNoExt)
// laporan-2024
// Split — kembalikan (dir, file) sekaligus
dir, file := filepath.Split(path)
fmt.Println(dir) // /home/budi/dokumen/
fmt.Println(file) // laporan-2024.pdf
// CATATAN: dir menyertakan separator di akhir, file tidak
// Contoh dengan berbagai path
contoh := []string{
"/home/budi/file.txt",
"relative/path/file.go",
"file.tar.gz", // ekstensi ganda
"/path/to/directory/", // direktori dengan trailing slash
".",
"..",
".hidden",
"",
}
for _, p := range contoh {
fmt.Printf("%-30q dir=%-25q base=%-15q ext=%q\n",
p, filepath.Dir(p), filepath.Base(p), filepath.Ext(p))
}
flowchart LR
Path["'/home/budi/dokumen/laporan-2024.pdf'"] --> Dir["Dir()\n'/home/budi/dokumen'"]
Path --> Base["Base()\n'laporan-2024.pdf'"]
Path --> Ext["Ext()\n'.pdf'"]
Path --> Split["Split()\ndir='/home/budi/dokumen/'\nfile='laporan-2024.pdf'"]
Base --> NoExt["nama tanpa ext\n'laporan-2024'\n= Base - Ext"]
style Path fill:#4f86c6,color:#fff
style Dir fill:#e8f5e9
style Base fill:#e3f2fd
style Ext fill:#fff3e0
style NoExt fill:#f3e5f5filepath.Abs dan filepath.Rel — Path Absolut dan Relatif #
// Abs — konversi path relatif ke absolut berdasarkan working directory
absPath, err := filepath.Abs("config.yaml")
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println(absPath)
// /home/budi/myapp/config.yaml (jika CWD adalah /home/budi/myapp)
// Abs pada path yang sudah absolut — tidak berubah
absPath2, _ := filepath.Abs("/etc/hosts")
fmt.Println(absPath2) // /etc/hosts
// Abs juga membersihkan path
absPath3, _ := filepath.Abs("./config/../config.yaml")
fmt.Println(absPath3) // /home/budi/myapp/config.yaml
// Rel — path relatif dari basepath ke targetpath
rel, err := filepath.Rel("/home/budi", "/home/budi/dokumen/file.txt")
if err == nil {
fmt.Println(rel) // dokumen/file.txt
}
rel2, _ := filepath.Rel("/home/budi/apps", "/home/budi/dokumen/file.txt")
fmt.Println(rel2) // ../dokumen/file.txt
// Rel untuk path di drive berbeda di Windows — error
// rel3, err := filepath.Rel("C:\\Users", "D:\\data")
// error: Rel: can't make D:\data relative to C:\Users
// Pola: temukan path relatif dari executable ke resource
execPath, _ := os.Executable()
execDir := filepath.Dir(execPath)
resourcePath := filepath.Join(execDir, "assets", "template.html")
fmt.Println(resourcePath)
filepath.Clean — Normalisasi Path #
filepath.Clean menormalisasi path dengan menerapkan aturan pembersihan:
// Clean menghapus elemen yang redundan
fmt.Println(filepath.Clean("/home//budi/./dokumen/../file.txt"))
// /home/budi/file.txt
fmt.Println(filepath.Clean("./config/./app/../main.go"))
// config/main.go
fmt.Println(filepath.Clean(""))
// . (string kosong → direktori saat ini)
fmt.Println(filepath.Clean("../../../etc/passwd"))
// ../../../etc/passwd — tidak aman! tetap diizinkan oleh Clean
// ANTI-PATTERN: asumsikan Clean mencegah path traversal
func bacaFile(base, userInput string) ([]byte, error) {
path := filepath.Clean(filepath.Join(base, userInput))
return os.ReadFile(path) // masih rentan path traversal!
}
// BENAR: validasi bahwa path ada di dalam direktori base
func bacaFileAman(base, userInput string) ([]byte, error) {
// Pastikan base adalah path absolut
absBase, err := filepath.Abs(base)
if err != nil {
return nil, err
}
// Gabungkan dan bersihkan
fullPath := filepath.Clean(filepath.Join(absBase, userInput))
// Validasi bahwa fullPath dimulai dengan absBase
if !strings.HasPrefix(fullPath, absBase+string(filepath.Separator)) {
return nil, fmt.Errorf("akses ditolak: path di luar direktori yang diizinkan")
}
return os.ReadFile(fullPath)
}
filepath.Glob — Mencari File dengan Wildcard #
filepath.Glob mencari semua file yang cocok dengan pola yang diberikan. Pattern yang didukung mirip dengan Unix shell globbing:
// Cari semua file .go di direktori saat ini
matches, err := filepath.Glob("*.go")
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println("File .go:", matches)
// [main.go handler.go service.go]
// Cari semua file di subdirektori (satu level)
matches2, _ := filepath.Glob("cmd/*")
fmt.Println(matches2)
// [cmd/main.go cmd/server.go]
// Pattern yang didukung:
// * — cocok dengan apapun kecuali separator
// ? — cocok dengan satu karakter apapun kecuali separator
// [abc] — cocok dengan satu karakter dalam set
// [a-z] — cocok dengan satu karakter dalam range
// Contoh pattern
matches3, _ := filepath.Glob("data/2024-0?.csv") // Januari-September 2024
matches4, _ := filepath.Glob("config.[yd]aml") // .yaml atau .yml
matches5, _ := filepath.Glob("*.{go,mod}") // TIDAK didukung! (bukan {})
// filepath.Glob tidak mendukung ** (recursive glob)
// Untuk rekursif, gunakan filepath.WalkDir
// Alternatif: WalkDir dengan filter ekstensi
func cariSemua(root, ext string) ([]string, error) {
var hasil []string
err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if !d.IsDir() && filepath.Ext(path) == ext {
hasil = append(hasil, path)
}
return nil
})
return hasil, err
}
// Penggunaan
fileGo, _ := cariSemua(".", ".go")
fmt.Printf("Ditemukan %d file .go\n", len(fileGo))
filepath.WalkDir — Traversal Direktori Rekursif #
filepath.WalkDir menelusuri seluruh struktur direktori secara rekursif dan memanggil callback untuk setiap file dan direktori yang ditemukan:
flowchart TD
Root["/project"] --> WD["filepath.WalkDir(root, fn)"]
WD --> Visit1["fn('/project', dir, nil)"]
Visit1 --> Visit2["fn('/project/cmd', dir, nil)"]
Visit2 --> Visit3["fn('/project/cmd/main.go', file, nil)"]
Visit3 --> Visit4["fn('/project/internal', dir, nil)"]
Visit4 --> Visit5["fn('/project/internal/handler.go', file, nil)"]
Visit5 --> Visit6["fn('/project/go.mod', file, nil)"]
Visit6 --> Visit7["fn('/project/go.sum', file, nil)"]
subgraph Control["Kontrol Traversal"]
C1["return nil\nlanjutkan"]
C2["return fs.SkipDir\nskip direktori ini"]
C3["return fs.SkipAll\nberhenti total (Go 1.20+)"]
C4["return error\nberhenti dengan error"]
end
style Root fill:#4f86c6,color:#fff
style Control fill:#e8f5e9import (
"io/fs"
"path/filepath"
)
// Traversal dasar
func listSemua(root string) error {
return filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
if err != nil {
// Handle error pada path tertentu (misalnya permission denied)
fmt.Fprintf(os.Stderr, "error pada %s: %v\n", path, err)
return nil // lanjutkan traversal
}
indent := strings.Repeat(" ", strings.Count(path, string(filepath.Separator)))
tipe := "📄"
if d.IsDir() {
tipe = "📁"
}
fmt.Printf("%s%s %s\n", indent, tipe, d.Name())
return nil
})
}
// Skip direktori yang tidak perlu
func cariFileGo(root string) ([]string, error) {
var files []string
err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return nil // skip error, lanjutkan
}
// Skip direktori yang tidak perlu
if d.IsDir() {
nama := d.Name()
if nama == ".git" || nama == "vendor" || nama == "node_modules" ||
strings.HasPrefix(nama, ".") {
return fs.SkipDir // skip direktori dan isinya
}
return nil
}
// Hanya ambil file .go
if filepath.Ext(path) == ".go" {
files = append(files, path)
}
return nil
})
return files, err
}
// Hitung ukuran total direktori
func ukuranDirektori(root string) (int64, error) {
var total int64
err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
if err != nil || d.IsDir() {
return nil
}
info, err := d.Info()
if err != nil {
return nil
}
total += info.Size()
return nil
})
return total, err
}
// Temukan file terbaru di direktori
func fileTerbaru(root string) (string, time.Time, error) {
var pathTerbaru string
var waktuTerbaru time.Time
err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
if err != nil || d.IsDir() {
return nil
}
info, err := d.Info()
if err != nil {
return nil
}
if info.ModTime().After(waktuTerbaru) {
waktuTerbaru = info.ModTime()
pathTerbaru = path
}
return nil
})
return pathTerbaru, waktuTerbaru, err
}
filepath.Match — Mencocokkan Pola #
// Match mencocokkan nama dengan pola glob
fmt.Println(filepath.Match("*.go", "main.go")) // true, nil
fmt.Println(filepath.Match("*.go", "main.txt")) // false, nil
fmt.Println(filepath.Match("*.go", "cmd/main.go")) // false, nil — * tidak cocok dengan /
fmt.Println(filepath.Match("?ello", "Hello")) // true, nil
fmt.Println(filepath.Match("?ello", "hello")) // true, nil
fmt.Println(filepath.Match("?ello", "ello")) // false, nil
fmt.Println(filepath.Match("[hH]ello", "Hello")) // true, nil
fmt.Println(filepath.Match("[hH]ello", "hello")) // true, nil
fmt.Println(filepath.Match("[hH]ello", "Aello")) // false, nil
// Match untuk filter file dalam WalkDir
func filterFile(pattern, path string) bool {
// Hanya cocokkan nama file, bukan path lengkap
matched, err := filepath.Match(pattern, filepath.Base(path))
return err == nil && matched
}
filepath.SplitList — Parsing PATH Environment #
// SplitList memisahkan PATH environment variable berdasarkan separator OS
// Linux/macOS: : (titik dua)
// Windows: ; (titik koma)
pathEnv := os.Getenv("PATH")
dirs := filepath.SplitList(pathEnv)
fmt.Printf("Ada %d direktori di PATH:\n", len(dirs))
for i, dir := range dirs {
fmt.Printf(" %d. %s\n", i+1, dir)
}
// Cari executable di PATH
func cariExecutable(nama string) (string, error) {
pathEnv := os.Getenv("PATH")
dirs := filepath.SplitList(pathEnv)
for _, dir := range dirs {
path := filepath.Join(dir, nama)
info, err := os.Stat(path)
if err == nil && !info.IsDir() && info.Mode()&0111 != 0 {
return path, nil
}
}
return "", fmt.Errorf("%s: tidak ditemukan di PATH", nama)
}
namaGo, err := cariExecutable("go")
if err == nil {
fmt.Println("Go ditemukan di:", namaGo)
}
filepath.EvalSymlinks — Resolve Symlink #
// EvalSymlinks mengikuti symlink dan mengembalikan path nyata
realPath, err := filepath.EvalSymlinks("/usr/bin/python3")
if err == nil {
fmt.Println("Path nyata:", realPath)
// Mungkin: /usr/bin/python3.11
}
// Berguna untuk memastikan dua path yang berbeda menunjuk ke file yang sama
func samePath(path1, path2 string) (bool, error) {
real1, err := filepath.EvalSymlinks(path1)
if err != nil {
return false, err
}
real2, err := filepath.EvalSymlinks(path2)
if err != nil {
return false, err
}
return real1 == real2, nil
}
filepath.FromSlash dan filepath.ToSlash #
// Konversi antara format URL (slash) dan format OS
// Berguna saat menerima path dari konfigurasi atau API yang menggunakan /
// FromSlash: '/' → separator OS
// Di Linux/macOS: tidak berubah
// Di Windows: '/' → '\'
winPath := filepath.FromSlash("home/user/dokumen/file.txt")
fmt.Println(winPath)
// Linux: home/user/dokumen/file.txt
// Windows: home\user\dokumen\file.txt
// ToSlash: separator OS → '/'
// Di Linux/macOS: tidak berubah
// Di Windows: '\' → '/'
unixPath := filepath.ToSlash(`home\user\dokumen\file.txt`)
fmt.Println(unixPath)
// home/user/dokumen/file.txt (di semua OS)
// Pola: simpan path di konfigurasi selalu dengan /
// konversi ke format OS saat digunakan
type Config struct {
DataDir string `yaml:"data_dir"` // disimpan dengan /
LogDir string `yaml:"log_dir"`
}
func (c *Config) DataDirOS() string {
return filepath.FromSlash(c.DataDir) // konversi saat dipakai
}
Pola Penggunaan di Produksi #
Manajemen Direktori Aplikasi #
type AppDirs struct {
Config string
Data string
Log string
Cache string
Temp string
}
func setupDirektoriApp(namaApp string) (*AppDirs, error) {
// Dapatkan home directory user
homeDir, err := os.UserHomeDir()
if err != nil {
return nil, fmt.Errorf("gagal dapatkan home dir: %w", err)
}
// Dapatkan direktori config yang sesuai per OS
configDir, err := os.UserConfigDir()
if err != nil {
configDir = filepath.Join(homeDir, ".config")
}
// Dapatkan direktori cache
cacheDir, err := os.UserCacheDir()
if err != nil {
cacheDir = filepath.Join(homeDir, ".cache")
}
dirs := &AppDirs{
Config: filepath.Join(configDir, namaApp),
Data: filepath.Join(homeDir, ".local", "share", namaApp),
Log: filepath.Join(homeDir, ".local", "share", namaApp, "logs"),
Cache: filepath.Join(cacheDir, namaApp),
Temp: filepath.Join(os.TempDir(), namaApp),
}
// Buat semua direktori yang belum ada
for _, dir := range []string{dirs.Config, dirs.Data, dirs.Log, dirs.Cache, dirs.Temp} {
if err := os.MkdirAll(dir, 0755); err != nil {
return nil, fmt.Errorf("buat direktori %s: %w", dir, err)
}
}
return dirs, nil
}
Scanner Proyek — Analisis Struktur Kode #
type InfoProyek struct {
TotalFile int
TotalBaris int
PerEkstensi map[string]int
FileIgnored int
}
func analisisProyek(root string) (*InfoProyek, error) {
info := &InfoProyek{
PerEkstensi: make(map[string]int),
}
// Pola direktori yang diabaikan
ignoreDirs := map[string]bool{
".git": true, "vendor": true, "node_modules": true,
".idea": true, ".vscode": true, "dist": true, "build": true,
}
err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return nil
}
if d.IsDir() {
if ignoreDirs[d.Name()] {
info.FileIgnored++
return fs.SkipDir
}
return nil
}
ext := strings.ToLower(filepath.Ext(path))
if ext == "" {
ext = "(tanpa ekstensi)"
}
info.PerEkstensi[ext]++
info.TotalFile++
// Hitung baris untuk file teks
if ext == ".go" || ext == ".js" || ext == ".py" || ext == ".ts" {
n, _ := hitungBaris(path)
info.TotalBaris += n
}
return nil
})
return info, err
}
func hitungBaris(path string) (int, error) {
f, err := os.Open(path)
if err != nil {
return 0, err
}
defer f.Close()
scanner := bufio.NewScanner(f)
n := 0
for scanner.Scan() {
n++
}
return n, scanner.Err()
}
Backup dengan Preservasi Struktur #
func backupDirektori(src, dst string) error {
// Normalisasi path
src = filepath.Clean(src)
dst = filepath.Clean(dst)
return filepath.WalkDir(src, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
// Hitung path relatif dari src
relPath, err := filepath.Rel(src, path)
if err != nil {
return fmt.Errorf("rel path: %w", err)
}
// Path tujuan
dstPath := filepath.Join(dst, relPath)
if d.IsDir() {
// Buat direktori di tujuan
info, err := d.Info()
if err != nil {
return err
}
return os.MkdirAll(dstPath, info.Mode())
}
// Salin file
return salinFile(path, dstPath)
})
}
func salinFile(src, dst string) error {
// Pastikan direktori tujuan ada
if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
return err
}
sumber, err := os.Open(src)
if err != nil {
return err
}
defer sumber.Close()
tujuan, err := os.Create(dst)
if err != nil {
return err
}
defer tujuan.Close()
_, err = io.Copy(tujuan, sumber)
return err
}
filepath vs path — Perbedaan Penting #
flowchart LR
subgraph FilePath["path/filepath\n(untuk path filesystem)"]
F1["Menggunakan separator OS\nLinux/Mac: /\nWindows: \\"]
F2["Untuk path ke file\ndan direktori di disk"]
F3["filepath.Join\nfilepath.Dir\nfilepath.WalkDir"]
end
subgraph Path["path\n(untuk URL path)"]
P1["Selalu menggunakan /\ntidak peduli OS"]
P2["Untuk URL path, HTTP path\nbukan filesystem"]
P3["path.Join\npath.Dir\npath.Base"]
end
subgraph When["Gunakan yang mana?"]
W1["File di disk → filepath"]
W2["URL, HTTP route → path"]
W3["go:embed path → path"]
W4["os.Open, os.ReadFile → filepath"]
end
style FilePath fill:#e8f5e9
style Path fill:#e3f2fd
style When fill:#fff3e0import (
"path"
"path/filepath"
)
// filepath — untuk sistem file
configPath := filepath.Join(homeDir, ".config", "app.yaml")
os.ReadFile(configPath) // benar
// path — untuk URL atau HTTP route
urlPath := path.Join("/api", "v1", "users")
// selalu: /api/v1/users (bukan \api\v1\users di Windows)
// ANTI-PATTERN: gunakan filepath untuk URL
badURL := filepath.Join("/api", "v1", "users")
// Di Windows: \api\v1\users — salah untuk HTTP!
// ANTI-PATTERN: gunakan path untuk filesystem di Windows
badPath := path.Join("C:", "Users", "file.txt")
// Hasil: C:/Users/file.txt — mungkin salah di Windows
Kapan Beralih ke Alternatif #
Tetap gunakan path/filepath jika:
✓ Semua operasi path filesystem: Join, Dir, Base, Ext
✓ Traversal direktori dengan WalkDir
✓ Pencarian file dengan Glob
✓ Konversi path relatif-absolut dengan Abs dan Rel
✓ Kode yang harus berjalan lintas platform (Windows, Linux, macOS)
Gunakan package path (bukan filepath) jika:
✗ Bekerja dengan URL path atau HTTP route
✗ Path dalam go:embed directive
✗ Manipulasi path yang selalu menggunakan / tanpa peduli OS
Pertimbangkan os.DirFS / fs.FS jika:
✗ Abstraksi filesystem yang bisa di-mock di testing
✗ Bekerja dengan embedded files (go:embed)
✗ Virtual filesystem atau filesystem kustom
Pertimbangkan library eksternal jika:
✗ Watching perubahan file → github.com/fsnotify/fsnotify
✗ Glob pattern yang lebih canggih (** untuk recursive)
→ github.com/bmatcuk/doublestar
✗ Virtual filesystem yang lebih lengkap → github.com/spf13/afero
Ringkasan #
filepath.Joinselalu lebih baik dari konkatenasi string — ia menggunakan separator yang tepat untuk OS saat ini dan membersihkan path yang dihasilkan secara otomatis.filepath.Absuntuk mengkonversi path relatif ke absolut berdasarkan working directory saat ini — berguna untuk validasi dan logging path yang jelas.filepath.Dir,filepath.Base,filepath.Extuntuk memecah path — nama file tanpa ekstensi tidak ada fungsi langsungnya, kombinasikan:Base[:len(Base)-len(Ext)].filepath.WalkDirlebih efisien darifilepath.Walk— ia tidak memanggilLstatekstra untuk setiap entry, gunakanWalkDiruntuk traversal di Go 1.16+.- Kembalikan
fs.SkipDirdari callback WalkDir untuk melewati direktori beserta isinya — gunakan untuk skip.git,vendor,node_modules, dan direktori lain yang tidak perlu.filepath.Globtidak mendukung**(recursive glob) — untuk pencarian rekursif, gunakanWalkDirdengan filter ekstensi.filepath.Cleantidak mencegah path traversal — selalu validasi bahwa path yang dihasilkan dimulai dengan base directory yang diizinkan setelah Clean.- Gunakan
path(bukanfilepath) untuk URL —filepath.Joindi Windows menghasilkan backslash yang salah untuk URL dan HTTP route.filepath.Reluntuk membuat path relatif — berguna saat membuat backup, laporan, atau menampilkan path yang lebih pendek kepada pengguna.