В моем случае ошибка была:
java.net.SocketException: Software caused connection abort: recv failed
Она была получена в eclipse при отладке приложения java для доступа к базе данных H2. Источником ошибки было то, что я сначала открыл базу данных с помощью SQuirreL, чтобы проверить целостность вручную. Я использовал флаг для включения нескольких подключений к одному и тому же БД (т. Е. AUTO_SERVER=TRUE
), поэтому нет проблем с подключением к БД из java.
Ошибка появилась, когда через некоторое время --it это длинный Java-процесс. Я решил закрыть SQuirreL для освобождения ресурсов. Похоже, что SQuirreL был «владельцем» экземпляра сервера БД и что он был отключен с помощью соединения SQuirreL.
Перезапуск приложения Java снова не выдало ошибку.
config
Вот скрипт 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);
Я использую 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
- Наконец создается пара атрибут-значение с нормализованным пробелом:
[1154 ] Вывод запроса:{ "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 .)}|}}}'