Есть два очевидных способа представления колоночных данных в JSON: как массив массивов и как массив объектов. В первом случае вы преобразуете каждую строку входных данных в массив, во втором - в объект.
Перечисленные ниже команды работают, по крайней мере, с выводом procps-ng в Linux для команд ps
и ps -l
.
Вы можете преобразовать вывод с помощью Perl и CPAN-модуля JSON::XS.
# ps | perl -MJSON -lane 'my @a = @F; push @data, \@a; END { print encode_json \@data }'
[["PID","TTY","TIME","CMD"],["12921","pts/2","00:00:00","ps"],["12922","pts/2","00:00:00","perl"],["28280","pts/2","00:00:01","zsh"]]
В качестве альтернативы вы можете использовать сам jq для выполнения преобразования.
# ps | jq -sR '[sub("\n$";"") | splits("\n") | sub("^ +";"") | [splits(" +")]]'
[
[
"PID",
"TTY",
"TIME",
"CMD"
],
[
"16694",
"pts/2",
"00:00:00",
"ps"
],
[
"16695",
"pts/2",
"00:00:00",
"jq"
],
[
"28280",
"pts/2",
"00:00:02",
"zsh"
]
]
Вы можете преобразовать входные данные в массив объектов JSON с осмысленно названными ключами, взяв имена ключей из строки заголовка.
Это требует немного больше усилий и немного сложнее в jq, в частности. Однако, результат, вероятно, более читабелен для человека.
# ps | perl -MJSON -lane 'if (!@keys) { @keys = @F } else { my %h = map {($keys[$_], $F[$_])} 0..$#keys; push @data, \%h } END { print encode_json \@data }'
[{"TTY":"pts/2","CMD":"ps","TIME":"00:00:00","PID":"11030"},{"CMD":"perl","TIME":"00:00:00","PID":"11031","TTY":"pts/2"},{"TTY":"pts/2","CMD":"zsh","TIME":"00:00:01","PID":"28280"}]
Обратите внимание, что ключи расположены в произвольном порядке для каждой записи. Это артефакт того, как работают хэши Perl.
# ps | jq -sR '[sub("\n$";"") | splits("\n") | sub("^ +";"") | [splits(" +")]] | .[0] as $header | .[1:] | [.[] | [. as $x | range($header | length) | {"key": $header[.], "value": $x[.]}] | from_entries]'
[
{
"PID": "19978",
"TTY": "pts/2",
"TIME": "00:00:00",
"CMD": "ps"
},
{
"PID": "19979",
"TTY": "pts/2",
"TIME": "00:00:00",
"CMD": "jq"
},
{
"PID": "28280",
"TTY": "pts/2",
"TIME": "00:00:02",
"CMD": "zsh"
}
]
Necesita un ||true
para asegurarse de que el primer comando siempre devuelva el éxito.
Puede evitar la necesidad de pgrep
desechando stderr; p.ej.
test: client server
killall myserver 2>/dev/null || true
myserver --background
myclient --server 127.0.0.1
No combine killall
con pgrep
. No usan las mismas reglas de coincidencia, por lo que lo que muestra pgrep
puede no ser lo que mata killall
. Use pkill
, que es exactamente lo mismo que pgrep
excepto que elimina los procesos coincidentes en lugar de mostrar sus PID. Tenga en cuenta que si llama tanto a pgrep
como a pkill
, hay una condición de carrera :en el momento en que se ejecuta pkill
, algunos procesos mostrados por pgrep
pueden haber terminado y algunos procesos nuevos pueden haber comenzado. A menos que le interesen los ID de proceso, no tiene sentido llamar a pgrep
; simplemente puede llamar al pkill
directamente.
pkill
devuelve el estado 1 si no encuentra ningún proceso para matar. Agregue -
al comienzo del comando, para indicarle a make que ignore este error, o cambie el comando a pkill myserver || true
, que hace exactamente lo mismo que pkill myserver
pero siempre devuelve un estado de éxito.
test: client server
pkill myserver || true
/build/bin/myserver --background
/build/bin/myclient --server 127.0.0.1
Если вам нужно игнорировать результат команды в Make , вы должны использовать оператор -
следующим образом:
test: client server
-pkill -f myserver
myserver --background
myclient --server 127.0.0.1
Таким образом, Make будет знать, что вам все равно, если pkill
потерпит неудачу, и вам не придется использовать ||
без необходимости.
Вы также можете не печатать команду и вывод с помощью @-
следующим образом:
test: client server
@-pkill -f myserver
myserver --background
myclient --server 127.0.0.1