사용자를 순환하는 방법은 무엇입니까?

사용자를 순환하는 방법은 무엇입니까?

쉘 스크립트에서 모든 사용자를 반복하는 방법은 무엇입니까?

시스템 정리를 수행하기 위해 쉘 스크립트를 작성 중입니다. 스크립트가 루트로 실행되면 모든 사용자의 홈 디렉터리에서 요청된 작업을 수행하고 싶습니다.

나는 그것을 반복할 수 있을 것이라고 생각했습니다 /etc/passwd. 그러나 거기에는 실제 사용자보다 더 많은 항목이 있습니다. 가상 사용자를 삭제하는 특별한 방법이 있나요 /etc/passwd?

나는 (이식성을 위해) Bourne Shell용 맞춤형 솔루션을 선호합니다. 의사코드와 간단한 설명도 환영합니다.

답변1

기존 사용자 계정을 모두 열거하는 표준 명령은 없습니다. 대부분의 Unix 변종에서는/etc/passwd항상 동일한 기존 형식(콜론으로 구분된 열)으로 된 로컬 계정 목록을 포함합니다. Glibc가 있는 Linux(즉, 내장되지 않은 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
    
  • 사용자가 자신의 홈 디렉터리를 소유하고 있는지 테스트할 수 있습니다. 이는 시스템 사용자가 아닌 실제 사용자의 경우 거의 항상 해당되지만 일부 시스템 사용자는 자신의 홈 디렉토리를 가지고 있으므로 그다지 신뢰할 수 없습니다. 또한 일반적으로 해당 디렉터리는 자동으로 마운트되지만 실제 사용자의 홈 디렉터리는 현재 사용할 수 없으면 존재하지 않을 수도 있습니다. 리눅스의 경우:

    if [ -d "$home" ] && [ "$(stat -c %u "$home")" = "$uid" ]; then
      echo "Likely flesh-and-blood"
    else 
      echo "Probably a system user"
    fi
    
  • 사용자가 실제 쉘을 가지고 있는지 테스트할 수 있습니다. 그러나 많은 시스템 사용자가 동일한 작업을 수행하므로 홈 디렉터리보다 안정성이 훨씬 떨어집니다.

  • 계정에 비밀번호가 있는지 테스트할 수 있습니다. 실제 사용자가 항상 비밀번호를 갖고 있는 것은 아니기 때문에 이는 완전히 신뢰할 수는 없습니다(예를 들어 SSH 키만 갖고 있을 수도 있음). 시스템 계정에 비밀번호가 있는 경우도 있습니다. 특히 루트 계정에 비밀번호가 있는 경우가 많습니다. 이를 수행하는 방법은 Unix 변형에 따라 다르며 일반적으로 루트 액세스가 필요합니다. 섀도우 비밀번호(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다른 값은 시스템 계정에 대한 것입니다.

나는 홈 디렉토리에 대한 경로가 가장 신뢰할 수 있는 단일 지표라고 생각하지만 이를 다른 경험적 방법과 결합할 수도 있습니다.

답변2

질문에 대한 의견과 몇 가지 실험을 바탕으로 제가 생각해낸 결과는 다음과 같습니다.

#!/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그 보석에 대해 Blatchley에게 감사드립니다. :)

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

그러나 운영 체제에 따라 검사 방법이 >= 1000달라야 할 수도 있습니다 .

답변3

방금 @gilles Awesome의 답변을 템플릿으로 사용하여 다음을 작성했습니다. 나에게 잘 작동합니다. zsh를 사용하여 Ubuntu 16.04에서 테스트되었습니다.

# 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

관련 정보