模組 Etc
Etc
模組提供存取通常儲存在 Unix 系統上 /etc
目錄中檔案的資訊。
可存取的資訊包含在 /etc/passwd
和 /etc/group
檔案中找到的資訊,以及有關系統暫存目錄 (/tmp
) 和組態目錄 (/etc
) 的資訊。
Etc
模組提供比環境變數(例如 +$USER+)更可靠的方式來存取已登入使用者的資訊。
範例:¶ ↑
require 'etc' login = Etc.getlogin info = Etc.getpwnam(login) username = info.gecos.split(/,/).first puts "Hello #{username}, I see your login name is #{login}"
請注意,此模組提供的方法並不總是安全的。它應該用於提供資訊,而不是用於安全性。
在此模組中定義的所有操作都是類別方法,因此您可以將 Etc
模組包含到您的類別中。
常數
- VERSION
公開類別方法
使用 confstr() 傳回系統組態變數。
name 應為 Etc
下的常數,其開頭為 CS_
。
傳回值為字串或 nil。nil 表示沒有組態定義的值。(confstr() 傳回 0 但 errno 未設定。)
Etc.confstr(Etc::CS_PATH) #=> "/bin:/usr/bin" # GNU/Linux Etc.confstr(Etc::CS_GNU_LIBC_VERSION) #=> "glibc 2.18" Etc.confstr(Etc::CS_GNU_LIBPTHREAD_VERSION) #=> "NPTL 2.18"
static VALUE etc_confstr(VALUE obj, VALUE arg) { int name; char localbuf[128], *buf = localbuf; size_t bufsize = sizeof(localbuf), ret; VALUE tmp; name = NUM2INT(arg); errno = 0; ret = confstr(name, buf, bufsize); if (bufsize < ret) { bufsize = ret; buf = ALLOCV_N(char, tmp, bufsize); errno = 0; ret = confstr(name, buf, bufsize); } if (bufsize < ret) rb_bug("required buffer size for confstr() changed dynamically."); if (ret == 0) { if (errno == 0) /* no configuration-defined value */ return Qnil; rb_sys_fail("confstr"); } return rb_str_new_cstr(buf); }
結束由 ::getgrent
開始的掃描 /etc/group
檔案的程序,並關閉檔案。
static VALUE etc_endgrent(VALUE obj) { #ifdef HAVE_GETGRENT endgrent(); #endif return Qnil; }
結束由 ::getpwent
開始的掃描 /etc/passwd
檔案的程序,並關閉檔案。
static VALUE etc_endpwent(VALUE obj) { #ifdef HAVE_GETPWENT endpwent(); #endif return Qnil; }
傳回 /etc/group
檔案中的項目。
第一次呼叫時,它會開啟檔案並傳回第一個項目;每次後續呼叫都會傳回下一個項目,或在到達檔案結尾時傳回 nil
。
若要在處理完成時關閉檔案,請呼叫 ::endgrent
。
每個項目都傳回為 Group
結構
static VALUE etc_getgrent(VALUE obj) { #ifdef HAVE_GETGRENT struct group *gr; if ((gr = getgrent()) != 0) { return setup_group(gr); } #endif return Qnil; }
傳回在 /etc/group
中找到的具有指定整數 group_id
的群組的資訊。
資訊以 Group
結構傳回。
有關更多詳細資訊,請參閱 getgrgid(3)
的 unix 手冊頁。
範例:¶ ↑
Etc.getgrgid(100) #=> #<struct Etc::Group name="users", passwd="x", gid=100, mem=["meta", "root"]>
static VALUE etc_getgrgid(int argc, VALUE *argv, VALUE obj) { #ifdef HAVE_GETGRENT VALUE id; gid_t gid; struct group *grp; if (rb_scan_args(argc, argv, "01", &id) == 1) { gid = NUM2GIDT(id); } else { gid = getgid(); } grp = getgrgid(gid); if (grp == 0) rb_raise(rb_eArgError, "can't find group for %d", (int)gid); return setup_group(grp); #else return Qnil; #endif }
傳回在 /etc/group
中找到的具有指定 name
的群組資訊。
資訊以 Group
結構傳回。
有關更多詳細資訊,請參閱 getgrnam(3)
的 unix 手冊頁。
範例:¶ ↑
Etc.getgrnam('users') #=> #<struct Etc::Group name="users", passwd="x", gid=100, mem=["meta", "root"]>
static VALUE etc_getgrnam(VALUE obj, VALUE nam) { #ifdef HAVE_GETGRENT struct group *grp; const char *p = StringValueCStr(nam); grp = getgrnam(p); if (grp == 0) rb_raise(rb_eArgError, "can't find group for %"PRIsVALUE, nam); return setup_group(grp); #else return Qnil; #endif }
傳回目前已登入使用者的簡短使用者名稱。很不幸地,通常很容易欺騙 ::getlogin
。
請勿將 ::getlogin
用於與安全性相關的目的。
如果 ::getlogin
失敗,請嘗試 ::getpwuid
。
有關更多詳細資訊,請參閱 getpwuid(3)
的 unix 手冊頁。
例如
Etc.getlogin -> 'guest'
static VALUE etc_getlogin(VALUE obj) { char *login; #ifdef HAVE_GETLOGIN login = getlogin(); if (!login) login = getenv("USER"); #else login = getenv("USER"); #endif if (login) { #ifdef _WIN32 rb_encoding *extenc = rb_utf8_encoding(); #else rb_encoding *extenc = rb_locale_encoding(); #endif return rb_external_str_new_with_enc(login, strlen(login), extenc); } return Qnil; }
從 /etc/passwd
檔案傳回一個項目。
第一次呼叫時,它會開啟檔案並傳回第一個項目;每次後續呼叫都會傳回下一個項目,或在到達檔案結尾時傳回 nil
。
若要關閉檔案(處理完成後),請呼叫 ::endpwent
。
每個項目都以 Passwd
結構傳回。
static VALUE etc_getpwent(VALUE obj) { #ifdef HAVE_GETPWENT struct passwd *pw; if ((pw = getpwent()) != 0) { return setup_passwd(pw); } #endif return Qnil; }
傳回具有指定登入 name
的使用者的 /etc/passwd
資訊。
資訊以 Passwd
結構傳回。
有關更多詳細資訊,請參閱 getpwnam(3)
的 unix 手冊頁。
範例:¶ ↑
Etc.getpwnam('root') #=> #<struct Etc::Passwd name="root", passwd="x", uid=0, gid=0, gecos="root",dir="/root", shell="/bin/bash">
static VALUE etc_getpwnam(VALUE obj, VALUE nam) { #ifdef HAVE_GETPWENT struct passwd *pwd; const char *p = StringValueCStr(nam); pwd = getpwnam(p); if (pwd == 0) rb_raise(rb_eArgError, "can't find user for %"PRIsVALUE, nam); return setup_passwd(pwd); #else return Qnil; #endif }
傳回具有指定整數 uid
的使用者的 /etc/passwd
資訊。
資訊以 Passwd
結構傳回。
如果省略 uid
,則會傳回 Passwd[:uid]
的值。
有關更多詳細資訊,請參閱 getpwuid(3)
的 unix 手冊頁。
範例:¶ ↑
Etc.getpwuid(0) #=> #<struct Etc::Passwd name="root", passwd="x", uid=0, gid=0, gecos="root",dir="/root", shell="/bin/bash">
static VALUE etc_getpwuid(int argc, VALUE *argv, VALUE obj) { #if defined(HAVE_GETPWENT) VALUE id; rb_uid_t uid; struct passwd *pwd; if (rb_scan_args(argc, argv, "01", &id) == 1) { uid = NUM2UIDT(id); } else { uid = getuid(); } pwd = getpwuid(uid); if (pwd == 0) rb_raise(rb_eArgError, "can't find user for %d", (int)uid); return setup_passwd(pwd); #else return Qnil; #endif }
提供一個便利的 Ruby 迭代器,它會為 /etc/group
檔案中的每個項目執行一個區塊。
程式區塊傳遞 Group
結構。
有關詳細資訊,請參閱上方的 ::getgrent
。
範例
require 'etc' Etc.group {|g| puts g.name + ": " + g.mem.join(', ') }
static VALUE etc_group(VALUE obj) { #ifdef HAVE_GETGRENT struct group *grp; if (rb_block_given_p()) { each_group(); } else if ((grp = getgrent()) != 0) { return setup_group(grp); } #endif return Qnil; }
傳回線上處理器的數量。
結果預計為使用所有可用處理器的程序數量。
此方法使用下列方式實作
-
sched_getaffinity(): Linux
-
sysconf(_SC_NPROCESSORS_ONLN): GNU/Linux、NetBSD、FreeBSD、OpenBSD、DragonFly BSD、OpenIndiana、Mac OS X、AIX
範例
require 'etc' p Etc.nprocessors #=> 4
結果可能小於實際 CPU 數量,特別是當 Ruby 程序繫結到特定 CPU 時。這預計可獲得更佳的平行處理。
範例:(Linux)
linux$ taskset 0x3 ./ruby -retc -e "p Etc.nprocessors" #=> 2
static VALUE etc_nprocessors(VALUE obj) { long ret; #if !defined(_WIN32) #if defined(HAVE_SCHED_GETAFFINITY) && defined(CPU_ALLOC) int ncpus; ncpus = etc_nprocessors_affin(); if (ncpus != -1) { return INT2NUM(ncpus); } /* fallback to _SC_NPROCESSORS_ONLN */ #endif errno = 0; ret = sysconf(_SC_NPROCESSORS_ONLN); if (ret == -1) { rb_sys_fail("sysconf(_SC_NPROCESSORS_ONLN)"); } #else SYSTEM_INFO si; GetSystemInfo(&si); ret = (long)si.dwNumberOfProcessors; #endif return LONG2NUM(ret); }
提供一個方便的 Ruby 迭代器,可針對 /etc/passwd
檔案中的每個項目執行區塊。
程式區塊傳遞 Passwd
結構。
有關詳細資訊,請參閱上方的 ::getpwent
。
範例
require 'etc' Etc.passwd {|u| puts u.name + " = " + u.gecos }
static VALUE etc_passwd(VALUE obj) { #ifdef HAVE_GETPWENT struct passwd *pw; if (rb_block_given_p()) { each_passwd(); } else if ((pw = getpwent()) != 0) { return setup_passwd(pw); } #endif return Qnil; }
重設讀取 /etc/group
檔案的程序,以便下一次呼叫 ::getgrent
會再次傳回第一個項目。
static VALUE etc_setgrent(VALUE obj) { #ifdef HAVE_GETGRENT setgrent(); #endif return Qnil; }
重設讀取 /etc/passwd
檔案的程序,以便下一次呼叫 ::getpwent
會再次傳回第一個項目。
static VALUE etc_setpwent(VALUE obj) { #ifdef HAVE_GETPWENT setpwent(); #endif return Qnil; }
使用 sysconf() 傳回系統組態變數。
name 應為 Etc
下的常數,且以 SC_
開頭。
傳回值為整數或 nil。nil 表示不確定的限制。(sysconf() 傳回 -1,但 errno 未設定。)
Etc.sysconf(Etc::SC_ARG_MAX) #=> 2097152 Etc.sysconf(Etc::SC_LOGIN_NAME_MAX) #=> 256
static VALUE etc_sysconf(VALUE obj, VALUE arg) { int name; long ret; name = NUM2INT(arg); errno = 0; ret = sysconf(name); if (ret == -1) { if (errno == 0) /* no limit */ return Qnil; rb_sys_fail("sysconf"); } return LONG2NUM(ret); }
傳回系統組態目錄。
這通常是 "/etc"
,但會由編譯 Ruby 時使用的前綴修改。例如,如果 Ruby 建置並安裝在 /usr/local
,則會在 Windows 以外的平台傳回 "/usr/local/etc"
。
在 Windows 上,這總是傳回系統提供的目錄。
static VALUE etc_sysconfdir(VALUE obj) { #ifdef _WIN32 return rb_w32_special_folder(CSIDL_COMMON_APPDATA); #else return rb_filesystem_str_new_cstr(SYSCONFDIR); #endif }
傳回系統暫存目錄;通常是 “/tmp”。
static VALUE etc_systmpdir(VALUE _) { VALUE tmpdir; #ifdef _WIN32 WCHAR path[_MAX_PATH]; UINT len = rb_w32_system_tmpdir(path, numberof(path)); if (!len) return Qnil; tmpdir = rb_w32_conv_from_wchar(path, rb_filesystem_encoding()); #else const char default_tmp[] = "/tmp"; const char *tmpstr = default_tmp; size_t tmplen = strlen(default_tmp); # if defined _CS_DARWIN_USER_TEMP_DIR #ifndef MAXPATHLEN #define MAXPATHLEN 1024 #endif char path[MAXPATHLEN]; size_t len; len = confstr(_CS_DARWIN_USER_TEMP_DIR, path, sizeof(path)); if (len > 0) { tmpstr = path; tmplen = len - 1; if (len > sizeof(path)) tmpstr = 0; } # endif tmpdir = rb_filesystem_str_new(tmpstr, tmplen); # if defined _CS_DARWIN_USER_TEMP_DIR if (!tmpstr) { confstr(_CS_DARWIN_USER_TEMP_DIR, RSTRING_PTR(tmpdir), len); } # endif #endif #ifndef RB_PASS_KEYWORDS /* untaint on Ruby < 2.7 */ FL_UNSET(tmpdir, FL_TAINT); #endif return tmpdir; }
傳回 uname 系統呼叫取得的系統資訊。
傳回值至少有 5 個鍵值的雜湊。
:sysname, :nodename, :release, :version, :machine
範例
require 'etc' require 'pp' pp Etc.uname #=> {:sysname=>"Linux", # :nodename=>"boron", # :release=>"2.6.18-6-xen-686", # :version=>"#1 SMP Thu Nov 5 19:54:42 UTC 2009", # :machine=>"i686"}
static VALUE etc_uname(VALUE obj) { #ifdef _WIN32 OSVERSIONINFOW v; SYSTEM_INFO s; const char *sysname, *mach; VALUE result, release, version; VALUE vbuf, nodename = Qnil; DWORD len = 0; WCHAR *buf; v.dwOSVersionInfoSize = sizeof(v); if (!GetVersionExW(&v)) rb_sys_fail("GetVersionEx"); result = rb_hash_new(); switch (v.dwPlatformId) { case VER_PLATFORM_WIN32s: sysname = "Win32s"; break; case VER_PLATFORM_WIN32_NT: sysname = "Windows_NT"; break; case VER_PLATFORM_WIN32_WINDOWS: default: sysname = "Windows"; break; } rb_hash_aset(result, ID2SYM(rb_intern("sysname")), rb_str_new_cstr(sysname)); release = rb_sprintf("%lu.%lu.%lu", v.dwMajorVersion, v.dwMinorVersion, v.dwBuildNumber); rb_hash_aset(result, ID2SYM(rb_intern("release")), release); version = rb_sprintf("%s Version %"PRIsVALUE": %"PRIsVALUE, sysname, release, rb_w32_conv_from_wchar(v.szCSDVersion, rb_utf8_encoding())); rb_hash_aset(result, ID2SYM(rb_intern("version")), version); # if defined _MSC_VER && _MSC_VER < 1300 # define GET_COMPUTER_NAME(ptr, plen) GetComputerNameW(ptr, plen) # else # define GET_COMPUTER_NAME(ptr, plen) GetComputerNameExW(ComputerNameDnsFullyQualified, ptr, plen) # endif GET_COMPUTER_NAME(NULL, &len); buf = ALLOCV_N(WCHAR, vbuf, len); if (GET_COMPUTER_NAME(buf, &len)) { nodename = rb_w32_conv_from_wchar(buf, rb_utf8_encoding()); } ALLOCV_END(vbuf); if (NIL_P(nodename)) nodename = rb_str_new(0, 0); rb_hash_aset(result, ID2SYM(rb_intern("nodename")), nodename); # ifndef PROCESSOR_ARCHITECTURE_AMD64 # define PROCESSOR_ARCHITECTURE_AMD64 9 # endif # ifndef PROCESSOR_ARCHITECTURE_INTEL # define PROCESSOR_ARCHITECTURE_INTEL 0 # endif GetSystemInfo(&s); switch (s.wProcessorArchitecture) { case PROCESSOR_ARCHITECTURE_AMD64: mach = "x64"; break; case PROCESSOR_ARCHITECTURE_ARM: mach = "ARM"; break; case PROCESSOR_ARCHITECTURE_INTEL: mach = "x86"; break; default: mach = "unknown"; break; } rb_hash_aset(result, ID2SYM(rb_intern("machine")), rb_str_new_cstr(mach)); #else struct utsname u; int ret; VALUE result; ret = uname(&u); if (ret == -1) rb_sys_fail("uname"); result = rb_hash_new(); rb_hash_aset(result, ID2SYM(rb_intern("sysname")), rb_str_new_cstr(u.sysname)); rb_hash_aset(result, ID2SYM(rb_intern("nodename")), rb_str_new_cstr(u.nodename)); rb_hash_aset(result, ID2SYM(rb_intern("release")), rb_str_new_cstr(u.release)); rb_hash_aset(result, ID2SYM(rb_intern("version")), rb_str_new_cstr(u.version)); rb_hash_aset(result, ID2SYM(rb_intern("machine")), rb_str_new_cstr(u.machine)); #endif return result; }