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

ここでは2つの異なる点を見ることができます。正常にログインしたユーザーのリストを取得し(1)、そのリストをBash配列に追加する(2)。

  1. 解析された出力には、lastユーザー名で始まらない行も含まれるため、単純ではありません。 Linuxにはいくつかの選択肢があります。

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

    lsloginsユーザーに関する情報(ログインイベントではない)を表示するため、ユーザーごとに1行だけ出力されます。この-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
    

関連情報