Win 修改 Powershell ls 命令

在 Windows PowerShell 中,ls 是 Get-ChildItem cmdlet 的一个别名。这意味着当你在 PowerShell 中输入 ls 时,实际上是在执行 Get-ChildItem 命令,它用于列出目录内容。

Get-ChildItem 输出是有一些问题的,比如行间距过宽等,极度不美观。
在这里插入图片描述

为了解决这个问题,我使用 GO 写了一个 ls 命令, 这个命令是仿照 linux 的 ls 命令

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"
	"sort"
	"strconv"
	"syscall"
	"strings"
	"unsafe"
)

var (
	rn         = flag.Bool("rn", false, "Ls will try to output results on one line as much as possible")
	all        = flag.Bool("a", false, "display all files including hidden files")
	allExcept  = flag.Bool("A", false, "display all files excluding '.' and '..'")
	longFormat = flag.Bool("l", false, "use a long listing format")
	reverse    = flag.Bool("r", false, "reverse order while sorting")
	recursive  = flag.Bool("R", false, "list subdirectories recursively")
	sortSize   = flag.Bool("S", false, "sort by file size")
	sortTime   = flag.Bool("t", false, "sort by modification time")
	dirOnly    = flag.Bool("d", false, "list directories themselves, not their contents")
	noSort     = flag.Bool("f", false, "do not sort")
	appendInfo = flag.Bool("F", false, "append indicator (one of */=>@|) to entries")
	noOwner    = flag.Bool("g", false, "like -l, but do not list owner")
	noGroup    = flag.Bool("G", false, "in a long listing, don't print group names")
	showAuthor = flag.Bool("author", false, "[Not implemented] print the author of each file")
	numID      = flag.Bool("n", false, "like -l, but list numeric user and group IDs")
	humanSize  = flag.Bool("h", false, "with -l, print sizes in human readable format (e.g., 1K 234M 2G)")
	ctime      = flag.Bool("c", false, "with -l, print ctime and sort by ctime")
	byColumn   = flag.Bool("C", false, "[Not implemented] list entries by columns")
	noColor    = flag.Bool("noColor", false, "never show color")
	fullTime   = flag.Bool("time", false, "like -l -time")
	depth      = 0
)

func main() {
	// Parse normal flags
	flag.Parse()

	paths := flag.Args()
	if len(paths) == 0 {
		paths = []string{"."}
	}

	for _, path := range paths {
		listFiles(path)
	}
}

func listFiles(path string) {
	var files []os.FileInfo
	var err error

	if *dirOnly {
		info, err := os.Stat(path)
		if err != nil {
			fmt.Println("Error:", err)
			return
		}
		files = []os.FileInfo{info}
	} else {
		files, err = ioutil.ReadDir(path)
		if err != nil {
			fmt.Println("Error:", err)
			return
		}
	}

	// Filter out hidden files unless -a or -A is set
	if !*all && !*allExcept {
		var filteredFiles []os.FileInfo
		for _, file := range files {
			if !isHidden(file) {
				filteredFiles = append(filteredFiles, file)
			}
		}
		files = filteredFiles
	}

	if *allExcept {
		var filteredFiles []os.FileInfo
		for _, file := range files {
			if file.Name() != "." && file.Name() != ".." {
				filteredFiles = append(filteredFiles, file)
			}
		}
		files = filteredFiles
	}

	if *noSort {
		// no sorting needed, So don't implement it here
	} else if *sortSize {
		sort.Slice(files, func(i, j int) bool {
			if *reverse {
				return files[i].Size() > files[j].Size()
			}
			return files[i].Size() < files[j].Size()
		})
	} else if *sortTime || *ctime {
		sort.Slice(files, func(i, j int) bool {
			if *reverse {
				return files[i].ModTime().Before(files[j].ModTime())
			}
			return files[i].ModTime().After(files[j].ModTime())
		})
	} else {
		sort.Slice(files, func(i, j int) bool {
			if *reverse {
				return files[i].Name() > files[j].Name()
			}
			return files[i].Name() < files[j].Name()
		})
	}
	
	var line = ""
	var width = getConsoleScreenWidth()

	// 输出文件
	for _, file := range files {
		if *longFormat {
			printLongFormat(path, file)
		} else {
			if !*rn {
				fmt.Println(space() + setStringColor(file))
			} else {
				var st = line + fmt.Sprintf("%s%s",setStringColor(file),sp(25-len(file.Name())))
				if float64(len(st)) >= (width*1.1) {
					fmt.Println(line)
					line = ""
					line +=  fmt.Sprintf("%s%s",setStringColor(file),sp(25-len(file.Name())))
				}else{
					line +=  fmt.Sprintf("%s%s",setStringColor(file),sp(25-len(file.Name())))
				}
			}
		}
		if *recursive {
			if file.IsDir() {
				depth++
				listFiles(filepath.Join(path, file.Name()))
				depth--
			}
		}
	}
	fmt.Println(line)
}

func sp(i int) string{
	var s = ""
	if i <=0 {
		return "  "
	}
	for _ = range i {
		s += " "
	}
	return s
}

func space() string {
	if !*recursive {
		return ""
	}
	var sp = "|"
	for _ = range depth {
		sp += " | "
	}
	return sp
}

func printLongFormat(path string, file os.FileInfo) {
	modTime := file.ModTime()
	var owner, group string

	if *numID || *noOwner {
		owner = strconv.Itoa(os.Getuid())
	} else {
		usr, err := user.LookupId(strconv.Itoa(os.Getuid()))
		if err != nil {
			// owner = strconv.Itoa(os.Getuid())
			owner = "-"
		} else {
			owner = usr.Username
		}
	}

	if *numID || *noGroup {
		group = strconv.Itoa(os.Getgid())
	} else {
		grp, err := user.LookupGroupId(strconv.Itoa(os.Getgid()))
		if err != nil {
			// group = strconv.Itoa(os.Getgid())
			group = "-"
		} else {
			group = grp.Name
		}
	}

	if *fullTime {
		fmt.Printf("%s%s %-4d %-3s %-4s %-11s %13s  %s\n",
			space(),
			file.Mode().String(),
			getLinkCount(path, file),
			owner,
			group,
			formatSize(file.Size()),
			modTime.Format("2006-01-02 15:04:05.000000000"),
			setStringColor(file))
	} else {
		fmt.Printf("%s%s %-4d %-3s %-4s %-11s %13s  %s\n",
			space(),
			file.Mode().String(),
			getLinkCount(path, file),
			owner,
			group,
			formatSize(file.Size()),
			modTime.Format("Jan 02 2006"),
			setStringColor(file))
	}
}

func setStringColor(file os.FileInfo) string {
	str := file.Name()
	if *noColor {
		return str
	}
	// 可执行文件后缀名数组
	exts := []string{".exe", ".bat", ".cmd", ".com", ".sh",".app"}
	hidden := isHidden(file)
	// 文件夹:蓝色
	if file.IsDir() {
		// 隐藏文件: 斜体
		if hidden {
			return "\033[3;34m"+str+"\033[0m"
		}
		return "\033[34m"+str+"\033[0m"
	}

	// 获取后缀名
	is := false
	ext := filepath.Ext(file.Name())
	for _, e := range exts {
		if ext == e {
			is = true
		}
	}
	// 可执行文件: 绿色
	if is {
		// 隐藏文件: 斜体
		if hidden {
			return "\033[3;32m"+str+"\033[0m"
		}
		return "\033[32m"+str+"\033[0m"
	}

	// 隐藏文件: 斜体
	if hidden {
		return "\033[3m"+str+"\033[0m"
	}

	return "\033[0m"+str
}

func getLinkCount(path string, file os.FileInfo) int {
	fullPath := filepath.Join(path, file.Name())
	if file.IsDir() {
		entries, err := ioutil.ReadDir(fullPath)
		if err != nil {
			return 1
		}
		return len(entries)
	}
	return 1
}

func formatSize(size int64) string {
	if !*humanSize {
		return strconv.FormatInt(size, 10)
	}
	const unit = 1024 // B
	if size < unit {
		return fmt.Sprintf("%-4d %-1s", size,"B")
	}
	div, exp := int64(unit), 0
	for n := size / unit; n >= unit; n /= unit {
		div *= unit
		exp++
	}
	unitStr := strings.TrimSpace(fmt.Sprintf("%c","KMGTPE"[exp]))
	return fmt.Sprintf("%-4.1f %-2s", float64(size)/float64(div), unitStr+"B")
}

func isHidden(file os.FileInfo) bool {
	if file.Name()[0] == '.' {
		return true
	}

	// Check for hidden file attribute on Windows
	if stat, ok := file.Sys().(*syscall.Win32FileAttributeData); ok {
		return stat.FileAttributes&syscall.FILE_ATTRIBUTE_HIDDEN != 0
	}

	return false
}


/// 窗口大小
type (
    short int16
    word  uint16
    dword uint32
    coord struct {
        X short
        Y short
    }
    smallRect struct {
        Left   short
        Top    short
        Right  short
        Bottom short
    }
    consoleScreenBufferInfo struct {
        Size              coord
        CursorPosition    coord
        Attributes        word
        Window            smallRect
        MaximumWindowSize coord
    }
)

var kernel32 = syscall.NewLazyDLL("kernel32.dll")
var procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")

func getConsoleScreenBufferInfo(handle syscall.Handle) (consoleScreenBufferInfo, error) {
    var csbi consoleScreenBufferInfo
    ret, _, err := procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
    if ret == 0 {
        return csbi, err
    }
    return csbi, nil
}

func getConsoleScreenWidth() float64 {
	handle := syscall.Handle(os.Stdout.Fd())
    csbi, err := getConsoleScreenBufferInfo(handle)
    if err != nil {
        fmt.Fprintln(os.Stderr, "Error getting console screen buffer info:", err)
		return float64(-1)
    }
	return float64(csbi.Size.X)
}

为了能正常将自己写的Ls 程序能在 win 中正常使用。需要进行以下操作

  1. 删除 Powershell 的 Ls 别名
# 该命令运行后只在当前终端有效
Remove-Item Alias:ls

我们需要修改配置文件来达到永远的删除别名。PowerShell 的配置文件是一个脚本文件,在每次启动时自动执行。你可以通过以下命令查看配置文件的路径:

$PROFILE

修改该配置文件,如果不存在则运行New-Item -ItemType File -Path $PROFILE -Force 进行创建
在配置文件中添加 Remove-Item Alias:ls

  1. 编译GO程序编译出 exe
  2. 将exe 放到 win 的环境变量下

运行图片

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值