getent passwd выводится как json в правильном формате - PullRequest
0 голосов
/ 18 января 2019

Я использую jq для того, чтобы попытаться преобразовать вывод команды bash в json.Однако преобразование мне не удается.

Использование этой строки:

hostname && getent passwd | egrep -v '/s?bin/(nologin|shutdown|sync|halt)' | cut -d: -f1 | sudo xargs -I % sh -c  'echo %; chage -l %; echo' | jq --raw-input . | jq --slurp .
  • Имя хоста должно быть корневым элементом json
  • Имя пользователя должно быть первым элементом
  • вывод getent passwd должен быть вложенными элементами имени пользователя

Примерно так: JSON

Вывод вышеуказанной команды без использования jq приводит к: Вывод команды

Кто-нибудь когда-нибудь пытался правильно преобразовать вывод bash в json?Спасибо

Ответы [ 2 ]

0 голосов
/ 22 января 2019

Я использую jq ...

Я понимаю, что jq популярен здесь, но я собираюсь рекомендовать Xidel для этой задачи.
Хотя в первую очередь это анализатор HTML / XML / JSON (с использованием шаблонов CSS, XPath, XQuery, JSONiq и шаблонов), Xidel также может обрабатывать основной текст.

Вы говорите, что хотите иметь jsonкак этот:

{
  "hostname": "example",
  "users": [
    {
      "username": "example1",
      "details": {
        "LastPasswordChange": "Nov 12, 2018",
        "PasswordExpires": "never",
        "PasswordInactive": "never",
        "AccountExpires": "never",
        "MinDaysBetweenPasswordChange": "0",
        "MaxDaysBetweenPasswordChange": "99999",
        "NumOfDaysOfWarningBeforePasswordExpires": "7"
      }
    },
    {
      "username": "example2",
      "details": {
        "LastPasswordChange": "Nov 13, 2018",
        "PasswordExpires": "never",
        "PasswordInactive": "never",
        "AccountExpires": "never",
        "MinDaysBetweenPasswordChange": "0",
        "MaxDaysBetweenPasswordChange": "99999",
        "NumOfDaysOfWarningBeforePasswordExpires": "7"
      }
    }
  ]
}

Это означает, что массив users должен быть заполнен объектами (созданными из вывода chage программы) для каждого пользователя отдельно .Вот почему я бы выбрал другой подход.

Допустим, getent passwd | egrep -v '/s?bin/(nologin|shutdown|sync|halt)' | cut -d: -f1 возвращает:

root
user1
user2

Это мы собираемся направить в Xidel, чтобы сначала создать базовый json:

getent passwd | egrep -v '/s?bin/(nologin|shutdown|sync|halt)' | cut -d: -f1 | xidel -s - --xquery '
  {
    "hostname":"'$(hostname)'",
    "users":x:lines($raw) ! {
      "username":.
    }
  }
'
  • Нет необходимости в hostname && перед вашей исходной командой, потому что вы просто вызываете ее из Xidel (что также делает запрос намного проще).
  • С x:lines($raw) вы создаете последовательность каждой новой строки из ввода Xidel (в данном случае stdin).В качестве альтернативы можно использовать tokenize($raw,"\n").

Вывод запроса:

{
  "hostname": "user",
  "users": [
    {
      "username": "root"
    },
    {
      "username": "user1"
    },
    {
      "username": "user2"
    }
  ]
}

Далее мы будем вызывать chage из Xidel для создания details объект:

getent passwd | egrep -v '/s?bin/(nologin|shutdown|sync|halt)' | cut -d: -f1 | xidel -s - --xquery '
  {
    "hostname":"'$(hostname)'",
    "users":x:lines($raw) ! {
      "username":.,
      "details":{|
        for $x in x:lines(
          system(x"chage -l {.}")
        )
        let $a:=tokenize(
          $x,
          ":"
        )
        return {
          normalize-space($a[1]):normalize-space($a[2])
        }
      |}
    }
  }
'
  • system(x"chage -l {.}") возвращает выходные данные chage -l root, chage -l user1 и chage -l user2.
  • С x:lines() вокруг него вы снова создаетепоследовательность каждой новой строки.Первая строка для вывода user2, например:
Last password change                                  : Oct 23, 2018
  • Для каждого элемента в этой последовательности (или строки) создается переменная $a, которая содержит последовательностьэтого элемента, разделенного на две части, с : в качестве разделителя.$a Таким образом, для этого первого элемента:
Last password change                                  # white-space up untill this point! 
 Oct 23, 2018
  • Наконец создается пара атрибут-значение с нормализованным пробелом:
{
  "Last password change": "Oct 23, 2018"
}

Вывод запроса:

{
  "hostname": "user",
  "users": [
    {
      "username": "root",
      "details": {
        "Last password change": "never",
        "Password expires": "never",
        "Password inactive": "never",
        "Account expires": "never",
        "Minimum number of days between password change": "0",
        "Maximum number of days between password change": "99999",
        "Number of days of warning before password expires": "7"
      }
    },
    {
      "username": "user1",
      "details": {
        "Last password change": "never",
        "Password expires": "never",
        "Password inactive": "never",
        "Account expires": "never",
        "Minimum number of days between password change": "0",
        "Maximum number of days between password change": "99999",
        "Number of days of warning before password expires": "7"
      }
    },
    {
      "username": "user2",
      "details": {
        "Last password change": "Oct 23, 2018",
        "Password expires": "never",
        "Password inactive": "never",
        "Account expires": "never",
        "Minimum number of days between password change": "0",
        "Maximum number of days between password change": "99999",
        "Number of days of warning before password expires": "7"
      }
    }
  ]
}

Вы можете пойти еще дальше:

getent passwd | egrep -v '/s?bin/(nologin|shutdown|sync|halt)' | cut -d: -f1 | xidel -s - --xquery '
  {
    "hostname":"'$(hostname)'",
    "users":x:lines($raw) ! {
      "username":.,
      "details":{|
        for $x in x:lines(
          system(x"chage -l {.}")
        )
        let $a:=tokenize(
          $x,
          ":"
        )
        return {
          string-join(
            for $x in tokenize(
              normalize-space($a[1]),
              " "
            )
            return
            upper-case(
              substring(
                $x,
                1,
                1
              )
            )||substring(
              $x,
              2
            )
          ):normalize-space($a[2]) ! (
            if (. castable as decimal) then
              number(.)
            else
              .
          )
        }
      |}
    }
  }
'
  • Вместо Last password change, с ...
string-join(for $x in tokenize(normalize-space($a[1])," ") return upper-case(substring($x,1,1))||substring($x,2))

... возвращается: LastPasswordChange.

  • Вместо "99999" (строка), с ...
normalize-space($a[2]) ! (if (. castable as decimal) then number(.) else .)

... он возвращает: 99999 как xs: десятичный атомарный тип.

Вывод запроса:

{
  "hostname": "user",
  "users": [
    {
      "username": "root",
      "details": {
        "LastPasswordChange": "never",
        "PasswordExpires": "never",
        "PasswordInactive": "never",
        "AccountExpires": "never",
        "MinimumNumberOfDaysBetweenPasswordChange": 0,
        "MaximumNumberOfDaysBetweenPasswordChange": 99999,
        "NumberOfDaysOfWarningBeforePasswordExpires": 7
      }
    },
    {
      "username": "user1",
      "details": {
        "LastPasswordChange": "never",
        "PasswordExpires": "never",
        "PasswordInactive": "never",
        "AccountExpires": "never",
        "MinimumNumberOfDaysBetweenPasswordChange": 0,
        "MaximumNumberOfDaysBetweenPasswordChange": 99999,
        "NumberOfDaysOfWarningBeforePasswordExpires": 7
      }
    },
    {
      "username": "user2",
      "details": {
        "LastPasswordChange": "Oct 23, 2018",
        "PasswordExpires": "never",
        "PasswordInactive": "never",
        "AccountExpires": "never",
        "MinimumNumberOfDaysBetweenPasswordChange": 0,
        "MaximumNumberOfDaysBetweenPasswordChange": 99999,
        "NumberOfDaysOfWarningBeforePasswordExpires": 7
      }
    }
  ]
}

"Предварительно подтвержденный" запрос гораздо легче читать, но если вы предпочитаете«минимизированная» версия:

getent passwd | egrep -v '/s?bin/(nologin|shutdown|sync|halt)' | cut -d: -f1 | xidel -s - --xquery '{"hostname":"'$(hostname)'","users":x:lines($raw) ! {"username":.,"details":{|for $x in x:lines(system(x"chage -l {.}")) let $a:=tokenize($x,":") return {string-join(for $x in tokenize(normalize-space($a[1])," ") return upper-case(substring($x,1,1))||substring($x,2)):normalize-space($a[2]) ! (if (. castable as decimal) then number(.) else .)}|}}}'
0 голосов
/ 18 января 2019

Вот скрипт на Perl, который отображает имя хоста и содержимое /etc/passwd в формате JSON (Вывод не похож на ваш пример на любой ОС, с которой я знаком, кстати).

Обратите внимание, что, конечно, «первый элемент» не имеет смысла, когда речь идет о ключах объекта JSON; они неупорядочены. Если вы зависите от того, какой конкретный ключ находится в определенном месте объекта, вы делаете это неправильно.

#!/usr/bin/env perl
use warnings;
use strict;
use Sys::Hostname;
use User::pwent;
# Might have to install through your OS's package manager or
# with your favorite CPAN client
use JSON;

my %pwdb = ( hostname => hostname(), users => [] );

while (my $pw = getpwent) {
  # Ignore users with certain shells
  next if
    defined $pw->shell && $pw->shell =~ m!s?bin/(nologin|shutdown|sync|halt)!;
  push @{$pwdb{"users"}}, {
                           username => $pw->name,
                           details => {
                                       passwd => $pw->passwd,
                                       uid => $pw->uid,
                                       gid => $pw->gid,
                                       change => $pw->change,
                                       age => $pw->age,
                                       quota => $pw->quota,
                                       comment => $pw->comment,
                                       class => $pw->class,
                                       gecos => $pw->gecos,
                                       dir => $pw->dir,
                                       shell => $pw->shell,
                                       expire => $pw->expire
                                      }
                          };
}

my $pp = JSON->new->ascii->pretty;
print $pp->encode(\%pwdb);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...