Bash에서 배열과 함께 변수 및 for 루프 사용

Bash에서 배열과 함께 변수 및 for 루프 사용

쉘 스크립트에서 arrUsers라는 배열을 선언했습니다. 마지막 명령을 사용하여 모든 사용자가 Linux 서버에 로그인하도록 하려고 합니다. 나는 이런 일을하고 싶다 :

declare -a arrUsers=()
for user in `last | awk '{print $1}'
do
    if $user not in arrUsers()
        do arrUsers += users

이 작업을 수행하는 데 올바른 구문이 무엇인지 잘 모르겠습니다. 어떤 도움이라도 대단히 감사하겠습니다.

답변1

여기서는 두 가지 다른 점을 볼 수 있습니다. 성공적으로 로그인한 사용자 목록을 가져오는 것(1)과 해당 목록을 Bash 배열에 추가하는 것(2)입니다.

  1. 구문 분석된 last출력은 사용자 이름으로 시작하지 않는 줄도 포함하므로 간단하지 않습니다. Linux에는 몇 가지 대안이 있습니다.

    lslogins -r | awk -F '[ ]' 'NR > 1 && $6 { print $2 }'
    

    lslogins사용자에 대한 정보(로그인 이벤트 아님)를 표시하므로 사용자당 한 줄만 출력됩니다. 이 -r옵션은 출력을 열로 구분하지 않도록 지시하여 단일 공백을 열 구분 기호로 가정하고 단일 공백이 아닌 경우 공백 문자가 압착되는 것을 awk방지하는 기능 (기본값)을 활용할 수 있도록 합니다. 그러나 출력 필드의 내용에 공백 문자가 나타나는지 FS확실하지 않습니다 .lslogins

    또는:

    utmpdump /var/log/wtmp 2>/dev/null |
      gawk -v FPAT='\\[[^][]*\\]' '
        $1 == "[7]" { gsub(/[][]/,"",$4); sub(/ +$/,"",$4); users[$4] }
        END { for (user in users) print user }'
    

    이는 비표준 GNU AWK 기능을 사용하여 FPAT필드 내용을 설명하는 정규식( 에 저장됨)을 기반으로 레코드를 분할합니다. 출력에서 utmpdump필드는 대괄호로 묶이고 공백으로 구분됩니다. 첫 번째 필드에 저장된 유형을 기준으로 레코드를 필터링합니다( man 5 wtmp자세한 내용은 참고자료 참조).

    그러한 이름을 만드는 것은 확실히 현명한 선택은 아니지만 Linux에서는 다음을 따르지 않는 사용자 이름을 허용합니다.기준에 없는 문자가 포함되어 있습니다.이식 가능한 파일 이름 문자 집합--공백을 포함합니다. 공백 문자는 문제가 되지 않습니다 lslogins -r. 공백 문자는 이스케이프 처리되지만(예 : ) 후행 공백은 출력에 명시적으로 표시 \x20되지 않으며 위의 스니펫에서 공백 문자를 제거합니다.utmpdump

  2. 다음을 사용하여 고유한 사용자 이름 목록을 배열에 넣을 수 있습니다.

    readarray -t users \
      < <(lslogins -r | awk -F '[ ]' 'NR > 1 && $6 { print $2 }')
    

    또는 재설정하지 않고 기존 배열에 요소를 추가합니다.

    readarray -t -O "${#users[@]}" users \
      < <(lslogins -r | awk -F '[ ]' 'NR > 1 && $6 { print $2 }')
    

    그러나 이것이 요소의 고유성을 보장하지는 않습니다. 이를 달성하려면 여러 루프가 필요할 수 있습니다.

    while IFS= read -r user
    do
      for el in "${users[@]}"
      do
        if [ "$el" = "$user" ]
        then
          continue 2
        fi
      done
      users+=( "$user" )
    done < <(lslogins -r | awk -F '[ ]' 'NR > 1 && $6 { print $2 }')
    

    대안으로(Bash 버전이 지원하는 경우) 연관 배열을 사용하고 사용자 이름을 키로 저장하여 각 사용자 이름이 한 번만 나타나도록 할 수 있습니다.

    declare -A users
    while IFS= read -r user
    do
      users["$user"]=''
    done < <(lslogins -r | awk -F '[ ]' 'NR > 1 && $6 { print $2 }')
    

    그런 다음 다음 구문을 사용하여 반복할 수 있습니다 "${!array[@]}".

    for username in "${!users[@]}"
    do
      printf '%s\n' "$username"
    done
    

관련 정보