Вопрос или проблема
Существует ли простой способ получить список пользователей, которые находятся во всех указанных группах?
Например, если у меня есть следующие пользователи:
фред - радуга, делл
джейн - хп
зиппи - радуга, хп, делл
джордж - хп, делл
бунгл - радуга, хп, делл
Я хотел бы что-то вроде этого:
[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)
-
Использование
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
-
Тестирование:
Создайте файл с приведённым выше скриптом, назовите его, например,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
Общие рекомендации
- Оптимизация скриптов: В зависимости от количества пользователей и групп, эффективные скрипты могут быть адаптированы для повышения производительности. Например, можно использовать массивы или более сложные структуры данных для ускорения проверки.
- Обработка ошибок: Всегда учитывайте возможность ошибок ввода, таких как несуществующие пользователи или группы. Это обеспечит более стабильную работу ваших скриптов.
- Представление результатов: В зависимости от ваших потребностей, результаты можно выводить в различных форматах (например, через запятую или в виде таблицы).
Вышеуказанные методы позволяют вам гибко находить пользователей, соответствующих заданным критериям, и настраивать их в соответствии с конкретными требованиями вашей системы.