2

ユーザーを人形の事実としてシステムに追加しようとしています。私は ruby​​ プログラマーではありません。次のコードは適切にユーザーを生成しますが、ユーザーの uid はすべて同じです (パスワード ファイルの最後のエントリの uid)。uid が範囲外の場合、未知のシンボル エラーが発生することが予想されます。Facter.add が最後に 1 回だけ呼び出された場合、uid と同じ、最後のユーザーが 1 人しか存在しないことが予想されます。他の人もそうせずに反復する方法がわかりません...

File.open("/etc/passwd", "r") do |passwords|
  while pw_entry = passwords.gets
    user, pw, uid, gid, gecos, home, shell = pw_entry.split(/:/)

    Facter.add("user_" + user) do
      setcode do
        uid
      end
    end
  end
end

突っついたところ、ほぼ同じ問題を抱えている他の人を見つけましたが、これが解決策でした(これは私にとってもうまくいきました):

require 'etc'

Etc.passwd { |user|

    Facter.add("user_" + user.name) {
      setcode {
        user.uid
      }
    }
}

...しかし、私は違いが何であるかを理解していません。Facter.add ブロックへの呼び出しがバッファリングされ、ループの最後で一度に実行されるように動作し、Etc がすべての passwd をロードするため、配列への user.uid インデックスとタイミングは関係ありません。それは手続き型言語にとって奇妙なことですが...

4

1 に答える 1

3

次のように言うのは正しいです。

File.open("/etc/passwd", "r") do |passwords|
  while pw_entry = passwords.gets
      user, pw, uid, gid, gecos, home, shell = pw_entry.split(/:/)
      print uid
  end
end

以下とほぼ同等です。

require 'etc'
Etc.passwd { |user|
  print uid
}

実際、これら 2 つのルビ スニペットはまったく同じ結果を生成します。

唯一の違いは、最後の方法が passwd ファイルを反復処理する別の方法を使用することです。パラメータを入力として受け取るクロージャを使用しuserます。これは、無名関数のスコープに存在するuser唯一のものです。user

さて、あなたの2つの方法の違いに関するあなたの問題について:

クロージャー自体に属さない ruby​​ クロージャー内の変数 (つまり、外部変数) を参照すると、その (外部) スコープ内のその変数のシンボルへの参照は、クロージャーのコード内に格納されます。最初のメソッドでは同じスコープ内で同じシンボルを再利用するため、すべてのファクトが追加された後にクロージャーのコードが呼び出されたときのuid最後の既知の値を参照します。uidこれは、外部変数トラップとして知られています。

この問題を回避する 1 つの方法は、それぞれをuid独自のスコープに存在させることです。

def addUserFact(passwd_entry)
  user, pw, uid, gid, gecos, home, shell = passwd_entry.split(/:/)   
  Facter.add("user_" + user) do
    setcode do
      uid
    end
   end
end

File.open("C:/cygwin/etc/passwd", "r") do |passwords|
  while pw_entry = passwords.gets
    addUserFact(pw_entry)
  end
end
于 2013-10-06T14:10:10.830 に答える