Список пользователей во всех указанных unix-группах

Вопрос или проблема

Существует ли простой способ получить список пользователей, которые находятся во всех указанных группах?

Например, если у меня есть следующие пользователи:

фред - радуга, делл
джейн - хп
зиппи - радуга, хп, делл
джордж - хп, делл
бунгл - радуга, хп, делл

Я хотел бы что-то вроде этого:

[me@box ~]$ magic "делл,хп"
зиппи, джордж, бунгл

т.е. возвращая пользователей, которые находятся и в делл, и в хп.

Если это несколько шагов, это нормально, хотя если это действительно невозможно без использования множества черной магии Bash, и быстрее сделать это в текстовом редакторе с включенной поддержкой регулярных выражений, это тоже нормально.

Я работаю на RHEL4, если это имеет значение.

Я не знаю ни одного инструмента, который бы это делал, но это легко запрограммировать.

Сначала получите список пользователей на системе, затем выполните groups для каждого и в конце grep для нужных групп:

getent passwd | sed 's/:.*$//g' | \
    while read user; do groups $user; done | \
    grep group1 | grep group2

Это работает для двух групп одновременно:

getent group делл хп | cut -d: -f 4 | tr , '\n' | sort | uniq -d | sed ':a;$s/\n/, /g;N;ba'

Поместите это в функцию с некоторыми изменениями, и она будет обрабатывать любое количество групп:

grmagic () {
    getent group "$@" | 
        cut -d: -f 4 | 
        tr , '\n' | 
        sort | 
        uniq -dc | 
        grep "^[[:blank:]]*$#" |
        awk '{all = all d $3; d = ", "} END {print all}'
}

Запустите это:

$ grmagic делл хп
зиппи, джордж, бунгл
$ grmagic делл хп радуга
зиппи, бунгл

Функция, состоящая в основном из скрипта AWK:

grmagic () {
    getent group "$@" |
    awk -F: -v "c=$#" '{
            split($4, a, ","); 
            for (i in a) n[a[i]]++
        } 
        END { 
            for (i in n) 
            if (n[i] == c) {
                printf d i; d=", "
            }; 
            printf "\n"  }'
}

Небольшой скрипт на Python:

#!/usr/bin/python

import subprocess,sys

group={}

for user in sys.argv[1:]:
    group[user] = set(subprocess.Popen("id -nG %s"%user, stdout=subprocess.PIPE, shell=True).stdout.read().split())

for g in group[sys.argv[1]] & group[sys.argv[2]]:
    print g

Тест:

# id user1
uid=1001(user1) gid=1001(user1) groups=1001(user1),1004(us1),1005(del)
# id user2
uid=1002(user2) gid=1002(user2) groups=1002(user2),1004(us1),1005(del)
# ./test.py user1 user2
del
us1

Решение на bash, которое принимает более одного пользователя в строке и выводит только уникальные группы. Обычный вывод — одна группа на строку, используйте -c, чтобы получить вывод, разделенный запятыми.

#!/bin/bash
\unalias -a

if [ $# -lt 1 ]
then
    echo "Необходим хотя бы один пользователь" 1>&2
fi

RESULT=''
COMMA=0
while [ $# -gt 0 ]
do
    if [ $1 == '-c' ]
    then
        COMMA=1
    else
        RESULT="$RESULT $(id -Gn $1)"
    fi
    shift
done
if [ $COMMA -eq 0 ]
then
    echo $RESULT |tr ' ' '\n'| sort -u
else
    echo $RESULT |tr ' ' '\n' | sort -u |tr '\n' ', ' |sed 's/.$//g'
    echo
fi

Пример вывода:

# ./magic.sh coredump mongodb
audio
cdrom
coredump
dialout
floppy
lpadmin
mongodb
netdev
nogroup
plugdev
powerdev
video

Используя -c:

# ./magic.sh -c coredump mongodb
audio,cdrom,coredump,dialout,floppy,lpadmin,mongodb,netdev,nogroup,plugdev,powerdev,video

Боюсь, что нет простого способа.
Это необычное требование.
Этот ответ предполагает, что вы используете исключительно переносимые идентификаторы групп и пользователей, как определено стандартом POSIX™.

  • Вы можете, как уже предложил jon_d, связать несколько grep, что фактически означает логическое И.
    Я хотел бы подчеркнуть необходимость правильного анкерования, чтобы избежать ложных срабатываний, например, окружая каждое имя группы пробелами.
    getent passwd | while IFS=':' read -r username remainder
    do
        printf '%s\t %s \n' "${username}" "$(id -Gn ${username})"
    done | grep -F ' group0 ' \
         | grep -F ' group1 ' \
         | grep -F ' group2 ' | cut -f1
    

    Имейте в виду, что id -Gn может создавать очень длинные строки, и это может привести к тому, что некоторые команды не выполнятся или просто выдадут неправильный результат.

  • В оболочке вы можете собрать конвейер в строковой переменной и выполнить его с помощью eval.
  • Другая реализация использует оболочечные шаблоны и при этом сохраняет количество запущенных параллельных процессов довольно низким:
    #!/bin/sh -u
    #        name: group user
    # description: show members of a group or in multiple groups simultaneously
    #  maintainer: J. Doe <mailbox@host> # fill in in multi-sysadmin environments
    : "${1:?Ошибка: Укажите хотя бы одно имя группы.}"
    # Некоторая грубая проверка ввода.
    for group
    do
        # Выход из скрипта, если была указана несуществующая группа.
        getent group "${group}" >&- || exit
    done
    # Упрощаем конвейеры с повторяющимися функциями:
    s2n() { tr ' '  '\n' ; }
    n2s() { tr '\n' ' '  ; }
    # Упорядочиваем список аргументов в алфавитном порядке.
    set -- $(printf "${*}" | s2n | sort)
    # Формируем шаблонное выражение `' * group0 * group1 * '`.
    search="$(printf " ${*} " | sed -e 's/ / * /g' )"
    # Итерируемся по списку пользователей.
    getent passwd | while IFS=':' read -r username remainder
    do
        # `id -G` получает все группы, к которым принадлежит пользователь,
        # включая основную группу, указанную в `passwd`.
        # `-n` преобразует числовые ID в текстовые представления.
        groups="  $(id -Gn ${username} | s2n | sort | n2s | sed 's/ /  /g')  "
        # Если шаблон поиска может устранить все вхождения,
        # результатом будет пустая строка. `[ "" ]` возвращает `false`.
        [ "${groups##${search}}" ] || printf '%s\n' "${username}"
    done | sort | { n2s ; printf '\n' ; } | fmt
    
  • Если вас не волнует основная группа пользователя (группа, указанная в 4м поле файла passwd), вы можете исключить вызов id.
    #!/bin/sh -u
    for group
    do
        # Выход из скрипта, если была указана несуществующая группа.
        getent group ${group} || { printf '%s?\n' "${group}" ; exit 1 ; }
    done | cut -d':' -f4 | grep -v '^$' \
         | tr ',' '\n' | sort | uniq -c \
         | while read -r count user
    do
    # Этот тест предполагает, что `group` не искажён,
        # в частности, нет строк формата
        # mygroup:*:1234:foo,bar,foo,foo,foo
        [ ${count} -eq ${#} ] && printf '%s\n' "${user}"
    done
    

    Алгоритм подсчитывает, как часто имя пользователя появляется в списках членства групп, произведённых с помощью getent group.

Ответ или решение

Для получения списка пользователей, входящих во все указанные группы в Unix-подобной системе, можно использовать различные подходы. Этот процесс может быть реализован с помощью оболочки Bash, создания скрипта на Python или с помощью комбинации системных команд. В зависимости от вашего уровня навыков и потребностей, вы можете выбрать наиболее подходящий метод.

Подход через командную оболочку (Bash)

  1. Использование getent и grep:
    Один из простых способов заключается в использовании команд getent, grep и sort. Примерный скрипт будет выглядеть так:

    #!/bin/bash
    
    if [ $# -lt 1 ]; then
       echo "Необходимы хотя бы одни группы" 1>&2
       exit 1
    fi
    
    groups=("$@")
    user_list=$(getent passwd | cut -d: -f1)  # Получаем всех пользователей
    
    for user in $user_list; do
       # Проверяем, состоит ли пользователь во всех указанных группах
       member_count=0
       for group in "${groups[@]}"; do
           if groups "$user" | grep -q "\b$group\b"; then
               ((member_count++))
           fi
       done
       if [ "$member_count" -eq "${#groups[@]}" ]; then
           echo "$user"
       fi
    done
  2. Тестирование:
    Создайте файл с приведённым выше скриптом, назовите его, например, find_users.sh, дайте ему права на выполнение:

    chmod +x find_users.sh

    Запустите скрипт с указанием необходимых групп:

    ./find_users.sh dell hp

    Это вернет список пользователей, состоящих во всех указанных группах.

Подход через Python

Если вам удобнее писать на Python, вы можете создать простой скрипт, который будет использовать библиотеку subprocess для получения групп пользователей:

#!/usr/bin/env python
import subprocess
import sys

def get_groups(user):
    groups = subprocess.check_output(f"id -Gn {user}", shell=True).decode().strip().split()
    return set(groups)

if len(sys.argv) < 3:
    print("Необходимо указать хотя бы две группы")
    sys.exit(1)

group_sets = [get_groups(user) for user in sys.argv[1:]]
common_users = set.intersection(*group_sets)

print(", ".join(common_users))

Сохраните его в файл find_users.py, сделайте его исполняемым:

chmod +x find_users.py

Запустите его с указанием имен пользователей:

./find_users.py fred zippy george bungle

Общие рекомендации

  • Оптимизация скриптов: В зависимости от количества пользователей и групп, эффективные скрипты могут быть адаптированы для повышения производительности. Например, можно использовать массивы или более сложные структуры данных для ускорения проверки.
  • Обработка ошибок: Всегда учитывайте возможность ошибок ввода, таких как несуществующие пользователи или группы. Это обеспечит более стабильную работу ваших скриптов.
  • Представление результатов: В зависимости от ваших потребностей, результаты можно выводить в различных форматах (например, через запятую или в виде таблицы).

Вышеуказанные методы позволяют вам гибко находить пользователей, соответствующих заданным критериям, и настраивать их в соответствии с конкретными требованиями вашей системы.

Оцените материал
Добавить комментарий

Капча загружается...