среда, 18 декабря 2013 г.

Получить полное имя пользователя домена только средствами cmd

Понадобилось мне для автоустановки одной программы получать из командной строки имя пользователя.

Тонкость в том, что нужно было ПОЛНОЕ имя пользователя. То есть фамилию и имя, которые хранит домен-контроллер – данные из учётной записи.


Машины пользователей – XP, DC – 2003 r2.

Набежал я с этим вопросом на гугл, и удивился: предлагаемые решения были монструозными и выходили за рамки cmd. Городить огород ради одной завалящей маленькой программки не хотелось.

Но тут мне попалось простое и изящное:
WMIC UserAccount Where "Name = '%username%'" Get fullname
и я радостно побежал его тестировать.

Конечно же, с первого раза случился облом – WMIC не работает без администраторских прав. А получать имя нужно было из залогиненного в данный момент пользовательского аккаунта.

Я продолжил грустно смотреть в гугл. Какая-то деталь не давала мне покоя. Не может быть так всё сложно, я же где-то это видел, прямо бери руками…

Через пол-часа индеец Зоркий Глаз заметил, что у него украли вигвам, а ваш покорный вспомнил, что полный юзернейм выводится на доменных машинах в верхней части меню «Пуск». Эврика! Значит, имя где-то в реестре, и мы можем его оттуда запросто запросить, reg query то бишь.

Ищем в реестре полное имя пользователя, и обламываемся вторично:
оно есть, но лежит в HKLM, в папке с именем SID пользователя, и к тому же наше желанное имечко окружено всяким мусором и выглядит так “CN=Full User Name,DC=local…” и т.п.

Одним reg query и даже одним for-ом мы явно не отделаемся. Но, по крайней мере, можем сделать всё средствами cmd.

Теперь нам нужен SID пользователя. Гугл предлагал получать его по сети, но я даже пробовать не стал  было много жалоб, что долго. Ещё предлагали PsGetSid, но тоже не стал  решил пойти путём полного cmd. 

К счастью, SID тоже есть в локальном реестре, и можно найти его простым перебором.

For /F "Tokens=*" %%A In ('Reg Query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" /s^|Find /i "ProfileList\S-"') Do (

Тут мы откроем ветку реестра, где свалены все логинившиеся на машину учётные записи. Поскольку юзер, имя которого мы получаем, уже залогинился, его SID там есть.

For /F "Tokens=2*" %%B In ('Reg Query "%%A" /s^|Find /i "ProfileImagePath"') Do If "%USERNAME%"=="%%~nC" (

Это вложенный цикл. Тут мы сравниваем переменную с названием профиля с системной переменной %username%, которая выводит имя текущего пользователя. Когда они совпадут, делаем следующее:

For /F "skip=3 tokens=2*" %%i In ('reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\State\%%~nA" /v Distinguished-Name') do (

Заходим в ту ветку, где хранится полное имя нашего пользователя, используя найденный SID. Путь хранится в %%A, и он целиком нам не нужен. Используем системную возможность  тильда+служебный символ. ~n, например, означает имя файла в строке пути. В данном случае, это последний элемент пути, то есть, нужный нам SID.

For /F "delims=,^,=^ tokens=2" %%g In ("%%j") do (

Последним циклом мы получим, собственно, имя. Тут есть две тонкости:
1. Предыдущим запросом мы получили здоровенную строку с целой кучей мусора. Поэтому мы отсекаем ещё в предыдущем цикле три строки параметром skip=3. После этого у нас остаётся строка, тоже набитая мусором. Мы берём из неё элементы второй и дальше  это делает параметр tokens=2*. Тут хитрость в том, что второй элемент будет сохранён в уже объявленную переменную %%i, а вот «всё остальное» – звездочка  будет в «следующей» переменной, которая нигде явно не задана, но так работает cmd. Поэтому в последнем цикле мы работаем не с %%i, а со следующей по алфавиту  %%j, нужная нам информация там.
2. В %%j у нас сейчас вот такая хрень: CN=Full User Name,DC=local… Раньше в for мы скакали по элементам, разделённым пробелом и новой строкой, но тут это не прокатит. Поэтому мы делаем delims=    он позволяет задать собственные разделители, то есть будет перескакивать не по пробелам, а по чему мы скажем, хоть по букве А. Но тут нам нужно задать символы «равно» и «запятая», а «равно»  служебный символ. Его нужно экранировать – поставить рядом специальный знак. В cmd это крышечка, и ставится она после знака:  =^
(вообще-то, обычно она ставится перед знаком, но тут это почему-то работает так. Cmd полон всяких интересностей)

Скачать скрипт можно здесь
(бесплатно и без смс, конечно :) )

Вложенные циклы использовал, так как: 
1. В какой-то момент столкнулся с тем, что переменная уничтожается при выходе из корневого for. На самом деле, это решается просто: до цикла set /p Peremennaya, в цикле set $Peremennaya=%%a. %Peremennaya% можно использовать вне цикла. Только не ставь пробелы до и после =.
2. Последний цикл можно было бы объединить в один, если бы был способ задать набор разделителей delims, включающий пробел " ". Я такого способа не нашёл, пришлось делать два цикла. 




3 комментария:

  1. спасибо огромное. сам столкнулся с подобной задаче.

    ОтветитьУдалить
  2. Пишет: Непредвиденное появление: )

    ОтветитьУдалить
    Ответы
    1. Есть такое дело. Удалите комментарий рядом с goto nfend, и всё заработает.
      У cmd странные правила относительно размещения комментариев.

      Удалить