Go语言获取Unix用户及关联组:数据结构与Shell命令问询
解决方案:Shell命令与Go语言实现
一、Shell命令方案
1. 简化循环命令
你当前的循环命令可以优化,也可以用id -Gn替代groups(输出格式更简洁),两种写法如下:
# 原命令简化:一次性传入所有用户,减少进程调用 groups $(awk -F: '{print $1}' /etc/passwd) # 用id -Gn输出纯组名列表,自定义格式 for user in $(awk -F: '{print $1}' /etc/passwd); do echo "$user: $(id -Gn $user)"; done
2. 无外部命令的高效解析
如果想避免频繁调用外部命令,直接解析/etc/passwd和/etc/group文件关联用户与组,性能更好:
awk -F: ' NR==FNR { users[$1] = $4; next } # 读取passwd,存用户名和主组GID { gname = $1; gid = $3 # 匹配主组 for (u in users) { if (users[u] == gid) { user_groups[u] = user_groups[u] ? user_groups[u] " " gname : gname } } # 匹配附加组 split($4, usrs, ",") for (i in usrs) { u = usrs[i] if (u in users) { user_groups[u] = user_groups[u] ? user_groups[u] " " gname : gname } } } END { for (u in user_groups) { print u ": " user_groups[u] } } ' /etc/passwd /etc/group
3. 无效命令说明
你尝试的tail -n +1 -F /etc/passwd | head -n +2500 -f":" /etc/passwd是错误的:head没有-f":"参数,且tail -F是实时跟踪文件变化,和head组合无实际意义,建议直接丢弃。
二、Go语言数据结构与实现
1. 推荐数据结构
用**map[string][]string**最适配需求:
- Key:用户名(
string类型) - Value:该用户所属的所有组名(
[]string切片,支持一个用户对应多个组)
2. 两种实现方式
方式一:调用系统命令id
package main import ( "bufio" "fmt" "os" "os/exec" "strings" ) func main() { file, err := os.Open("/etc/passwd") if err != nil { fmt.Println("读取passwd文件失败:", err) return } defer file.Close() userGroups := make(map[string][]string) scanner := bufio.NewScanner(file) for scanner.Scan() { parts := strings.Split(scanner.Text(), ":") if len(parts) < 1 { continue } user := parts[0] // 执行id -Gn获取组名列表 output, err := exec.Command("id", "-Gn", user).Output() if err != nil { fmt.Printf("获取%s组信息失败: %v\n", user, err) continue } groups := strings.Fields(strings.TrimSpace(string(output))) userGroups[user] = groups } // 打印结果 for user, groups := range userGroups { fmt.Printf("%s: %s\n", user, strings.Join(groups, ", ")) } }
方式二:直接解析系统文件(更高效)
避免调用外部命令,直接读取/etc/passwd和/etc/group解析:
package main import ( "bufio" "fmt" "os" "strings" ) func main() { gidToName := make(map[string]string) userToGroups := make(map[string][]string) // 读取/etc/group,构建GID到组名、用户到附加组的映射 groupFile, err := os.Open("/etc/group") if err != nil { fmt.Println("读取group文件失败:", err) return } defer groupFile.Close() scanner := bufio.NewScanner(groupFile) for scanner.Scan() { parts := strings.Split(scanner.Text(), ":") if len(parts) < 4 { continue } gname := parts[0] gid := parts[2] users := strings.Split(parts[3], ",") gidToName[gid] = gname for _, u := range users { if u != "" { userToGroups[u] = append(userToGroups[u], gname) } } } // 读取/etc/passwd,补充主组信息 passwdFile, err := os.Open("/etc/passwd") if err != nil { fmt.Println("读取passwd文件失败:", err) return } defer passwdFile.Close() scanner = bufio.NewScanner(passwdFile) for scanner.Scan() { parts := strings.Split(scanner.Text(), ":") if len(parts) < 4 { continue } user := parts[0] primaryGid := parts[3] primaryGroup, ok := gidToName[primaryGid] if !ok { primaryGroup = primaryGid // 找不到组名时用GID代替 } // 合并主组与附加组 groups := []string{primaryGroup} if addGroups, ok := userToGroups[user]; ok { groups = append(groups, addGroups...) } userToGroups[user] = groups } // 打印结果 for user, groups := range userToGroups { fmt.Printf("%s: %s\n", user, strings.Join(groups, ", ")) } }
内容的提问来源于stack exchange,提问作者user3454622
相关产品推荐
相关产品推荐

