Intereting Posts

Как перебрать пользователей?

Как я могу обойти всех пользователей в сценарии оболочки?

Я пишу сценарий оболочки для выполнения очистки в системе. И если скрипт запускается от имени root, я хочу выполнить запрошенные действия в домашних каталогах всех пользователей.

Я думал, что мог бы перебрать строки в /etc/passwd ; однако я вижу там гораздо больше записей, чем есть живые, дышащие пользователи. Есть ли какой-то особый способ, с помощью которого я могу избавить фиктивных пользователей от /etc/passwd ?

Я бы предпочел решения, разработанные для Bourne Shell (для переносимости). Также приветствуются псевдокод и простые объяснения.

Нет стандартной команды для перечисления всех существующих учетных записей пользователей. В большинстве вариантов Unix /etc/passwd содержит список локальных учетных записей, всегда с тем же традиционным форматом (столбцы, разделенные двоеточиями). В Linux с помощью Glibc (т.е. любой не встроенный Linux) вы можете использовать команду getent : getent passwd похож на cat /etc/passwd , но также включает удаленные учетные записи (NIS, LDAP, …).

Следующий фрагмент перечисляет учетные записи пользователей:

 getent passwd | while IFS=: read -r name password uid gid gecos home shell; do echo "$name's home directory is $home" done 

Фильтрация пользователей плоти и крови невозможна полностью надежным способом, поскольку ничто в пользовательской базе данных не говорит о том, является ли пользователь плотью и кровью. (Плюс: счетчики учетных записей счетов? Учет гостей счета? И т. Д.) Вот некоторые эвристики, которые вы можете применить.

  • Вы можете фильтровать пользователей, чей домашний каталог, похоже, не является системным каталогом.

     top=${home#/}; top=${top%%/*} case $top in |bin|dev|etc|lib*|no*|proc|sbin|usr|var) echo "Looks like a system user";; *) echo "Looks like a user with a real home directory";; esac 
  • Вы можете проверить, владеет ли пользователь своим домашним каталогом. Это почти всегда относится к потребителям плоти и крови, и обычно это не относится к системным пользователям, но это не очень надежно, потому что некоторые пользователи системы имеют собственный домашний каталог. Кроме того, домашний каталог пользователей плоти и крови может не существовать, если это удаленный каталог, который в настоящее время недоступен, хотя обычно этот каталог будет автомонтирован. В Linux:

     if [ -d "$home" ] && [ "$(stat -c %u "$home")" = "$uid" ]; then echo "Likely flesh-and-blood" else echo "Probably a system user" fi 
  • Вы можете проверить, есть ли у пользователя оболочка, которая является реальной оболочкой. Но многие пользователи системы также делают это, так что это даже менее надежно, чем домашний каталог.

  • Вы можете проверить, имеет ли учетная запись пароль. Это не полностью надежно, потому что пользователи плоти и крови не всегда имеют пароль (например, у них могут быть только ключи SSH). Иногда системные учетные записи имеют пароль – в частности, часто используется учетная запись root. Как это сделать, зависит от варианта Unix и часто требует доступа root. В Linux с теневыми паролями (что обычно для Linux) вам нужно заглянуть в /etc/shadow для пароля.

     case $(getent shadow "$name" | awk -F: '{print $2}') in ?|??) echo "No password on this account";; |\$*) echo "This account has a password";; *) echo "Weird password field";; esac 
  • Многие системы определяют диапазоны UID для системных учетных записей и пользователей плоти и крови. Диапазоны зависят от системы: по умолчанию это зависит от варианта Unix и дистрибутива, и его обычно можно настроить системным администратором. В большинстве дистрибутивов Linux диапазоны определены в login.defs : UID_MIN для UID_MAX для пользователей пользователей, другие значения для системных учетных записей.

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

Основываясь на комментариях к вопросу и некоторых экспериментах, вот что я придумал …

 #!/bin/bash #Use awk to do the heavy lifting. #For lines with UID>=1000 (field 3) grab the home directory (field 6) usrInfo=$(awk -F: '{if ($3 >= 1000) print $6}' < /etc/passwd) IFS=$'\n' #Use newline as delimiter for for-loop for usrLine in $usrInfo do #Do processing on each line echo $usrLine done 

Или, как было предложено в комментариях, getent passwd можно использовать для случаев, когда /etc/passwd не имеет полного списка. Престижность Братчли за этот драгоценный камень. 🙂

 usrInfo=$(getent passwd | awk -F: '{if ($3 >= 1000) print $6}') 

Обратите внимание, однако, что проверка >= 1000 может быть различной для разных ОС.

Я просто написал следующее, используя @gilles удивительный ответ в качестве шаблона. Хорошо работает для меня. Протестировано на Ubuntu 16.04 с использованием zsh.

 # get all users getent passwd | while IFS=: read -r name password uid gid gecos home shell; do # only users that own their home directory if [ -d "$home" ] && [ "$(stat -c %u "$home")" = "$uid" ]; then # only users that have a shell, and a shell is not equal to /bin/false or /usr/sbin/nologin if [ ! -z "$shell" ] && [ "$shell" != "/bin/false" ] && [ "$shell" != "/usr/sbin/nologin" ]; then echo "$name's home directory is $home using shell $shell" fi fi done