Обнаружьте событие клавиатуры в C

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

blockquote>

Я понимаю, что 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])
        }
      |}
    }
  }
'
    [ 1140] 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"
}
[1154 ] Вывод запроса:

{
  "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 .)}|}}}'
8
задан Archmede 1 May 2017 в 04:24
поделиться

4 ответа

Нет стандартного пути, но они должны запустить Вас.

Windows:

getch();

Unix:

Используйте этот код от W. Программирование Unix Richard Stevens заказывает, чтобы установить Ваш терминал в режиме без предварительной обработки и затем использовать чтение ().

static struct termios   save_termios;
static int              term_saved;

int tty_raw(int fd) {       /* RAW! mode */
    struct termios  buf;

    if (tcgetattr(fd, &save_termios) < 0) /* get the original state */
        return -1;

    buf = save_termios;

    buf.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
                    /* echo off, canonical mode off, extended input
                       processing off, signal chars off */

    buf.c_iflag &= ~(BRKINT | ICRNL | ISTRIP | IXON);
                    /* no SIGINT on BREAK, CR-toNL off, input parity
                       check off, don't strip the 8th bit on input,
                       ouput flow control off */

    buf.c_cflag &= ~(CSIZE | PARENB);
                    /* clear size bits, parity checking off */

    buf.c_cflag |= CS8;
                    /* set 8 bits/char */

    buf.c_oflag &= ~(OPOST);
                    /* output processing off */

    buf.c_cc[VMIN] = 1;  /* 1 byte at a time */
    buf.c_cc[VTIME] = 0; /* no timer on input */

    if (tcsetattr(fd, TCSAFLUSH, &buf) < 0)
        return -1;

    term_saved = 1;

    return 0;
}


int tty_reset(int fd) { /* set it to normal! */
    if (term_saved)
        if (tcsetattr(fd, TCSAFLUSH, &save_termios) < 0)
            return -1;

    return 0;
}
9
ответ дан 5 December 2019 в 08:55
поделиться

Что относительно старого доброго kbhit? Если я пойму вопрос правильно, то это будет работать. Вот kbhit реализация на Linux.

4
ответ дан 5 December 2019 в 08:55
поделиться

Действительно необходимо пользоваться сторонними библиотеками. Нет определенно никакого платформенно независимого способа сделать это в ANSI C. Обработка сигнала не является путем.

3
ответ дан 5 December 2019 в 08:55
поделиться

Стандарт C не имеет никаких средств для обнаружения событий клавиатуры, к сожалению. Необходимо полагаться на определенные для платформы расширения. Сигнал, обрабатывающий привычку, помогает Вам.

3
ответ дан 5 December 2019 в 08:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: