기본적으로 다음과 같은 file.log가 있습니다.
blah blah
blah blah
Hello world | {"foo": "bar"}
blah blah
Hello earth | {"foo1": "bar1"}
이제 내 목표는 다음과 같이 원하는 출력을 얻기 위해 몇 가지 쉘 명령을 작성하는 것입니다.
Hello earth | "bar"
Hello earth | "bar1"
현재 내가 가지고 있는 것은 다음과 같습니다.
grep Hello file.log | awk -F "|" '{print $1, system("jq " $2)}'
하지만 jq를 호출하면 다음과 같은 오류가 발생합니다.
jq: error: syntax error, unexpected ':', expecting $end (Unix shell quoting issues?) at <top-level>, line 1:
bin:application
jq: 1 compile error
나는 이것이 system() 내부에서 내 $12가 모든 따옴표 문자(")를 제거했기 때문에 JQ가 해당 json을 인식하지 못하기 때문이라고 생각합니다. 어떤 제안이 있습니까?
답변1
여기에 몇 가지 질문이 있습니다.
system
인쇄할 내용을 반환하는 대신 실행한 명령의 종료 값을 반환합니다(모든 것이 잘 되었다면 0). JSON으로 디코딩된 데이터와 그 뒤에 한 줄이 표시됩니다.Hello earth 0
- JSON 문자열의 큰따옴표는 셸에서 무시됩니다. 실행 중인 결과 명령은 다음과 같습니다
jq {foo: bar}
(2개의 인수, JSON은 더 이상 인용되지 않음). - 쉘
$2
에 특수 문자가 포함되어 있으면 이를 해석합니다.$
- 올바른 참조가 있더라도
jq
이렇게 호출되지는 않습니다. 첫 번째 인수로 필터가 필요하며(예: ".
") 파일이나 표준 입력에서 JSON 입력을 읽어야 합니다. - 로그에서 명령을 작성하고 실행하면 보안에 큰 영향을 미칩니다(
$2
그렇다면 어떨까요; rm -rf ~
?). 가능하면 피하는 것이 가장 좋습니다.
awk
보안 문제를 제외하고 대부분의 경우 작동하는 코드는 다음과 같습니다 .
awk -F "|" '{ printf "%s", $1; system("echo \x27" $2 "\x27 | jq .")}'
그것이 하는 일은 $2
작은따옴표( )를 통해 stdin \x27
으로 보내는 것입니다 jq
.
하지만 문제는 여전히 존재합니다
- 작은따옴표가 포함 되면
$2
전체 명령이 중단됩니다. - 대시로 시작 하면
$2
(가능성 없음) 옵션으로 해석됩니다 ( 대신 명령을echo
사용할 수 있습니다 ).printf
echo
- 이미 언급된 보안 문제(예: 문자열에
$2
포함된 경우)...'; rm -r ~; : ' ...
awk
이제 더 나은 코드 가 있습니다
awk -F "|" '{ printf "%s", $1; print $2 | "jq ."; close("jq ."); }'
stdin을 통해 프로세스 $2
로 전송되지만 이제 파이프를 사용하므로 쉘은 더 이상 이를 해석하지 않고 위의 모든 문제를 해결합니다. 명령은 모든 줄에서 닫혀야(종료)되어야 하므로 .jq
awk
jq
close()
답변2
awk를 사용하지 않고 다른 솔루션을 사용하면 됩니다.잭
비결은 다음과 같습니다.--원본 입력, 파일을 문자열 배열로 읽습니다.
따라서 각 라인에 대해 기호가|여기서 문자열은 잘라내어 json 문자열로 구문 분석됩니다.
jq -j --raw-input '
. as $line |
if index("|") >= 0
then
[ .[:index("|")-1] ,.[index("|")+2:] ]
else
empty
end |
[ .[0] , ( .[1] | fromjson | to_entries | .[0].value ) ] |
.[0] , " | \"" ,.[1] , "\"\n" ' /tmp/file.log
답변3
xhienne은 좋은 개요를 제공합니다기존 코드의 문제 및 달성하려는 작업에 대한 좋은 대안입니다.
또 다른 옵션은 다음과 같습니다. 호출을 전혀 시도하지 말고 jq
스크립트 가 올바른 JSON 출력을 생성하도록 awk
하세요 .awk
$ awk -F '|' 'BEGIN { print "[" } $2 != "" { if (t != "") print t ","; t = $2 } END { print t, "]" }' file | jq .
[
{
"foo": "bar"
},
{
"foo1": "bar1"
}
]
코드 awk
자체는 발견된 JSON 개체에서 다음 JSON 배열을 생성합니다(질문의 예 제공).
[
{"foo": "bar"},
{"foo1": "bar1"} ]
jq
이를 통해 스크립트를 유지 관리하고 이해하기 어렵게 만들지 않고도 보다 자유롭게 작업할 수 있습니다 .
스크립트에서 변수를 사용하면 t
마지막 JSON 개체 뒤에 후행 쉼표가 표시되지 않습니다.
답변4
간단히 말해서:
jq -r -R '
select(contains(" | ")) |
split(" | ") |
.[0] as $text |
(.[1] | fromjson | to_entries | .[0].value ) as $json_obj_value |
"\($text) | \($json_obj_value)"
' yourlogfile.log
완전한 답변
대부분의 사람들은 그것이 얼마나 강력한지 깨닫지 못합니다 jq
(그렇다고 말할 수는 있지만 awk
).
~처럼Kusaronanda는 그들의 반응에서 신중하게 언급했습니다., 당신의 가장 친한 친구는-R
깃발, json 객체 대신 json 문자열로 입력을 한 줄씩 읽습니다. 이렇게 하면 내부 문자열을 자유롭게 처리할 수 jq
있으며 전혀 필요하지 않습니다 awk
.
문서에서 해당 버전을 설명하는 방법은 다음과 같습니다.1.6:
--raw-input
/-R:
입력을 JSON으로 구문 분석하지 마세요. 대신 각 텍스트 줄이 문자열로 필터에 전달됩니다. 와 함께 사용하면
--slurp
전체 입력이 하나의 긴 문자열로 필터에 전달됩니다.
원하는 출력을 얻으려면 다음이 필요합니다.-r
깃발, 터미널에 json 문자열 대신 기본 문자열을 인쇄합니다.
다시 문서에서
--raw-output
/-r
:이 옵션을 사용하면 필터 결과가 문자열인 경우 인용된 JSON 문자열 형식이 아닌 표준 출력에 직접 기록됩니다. 이는 jq 필터가 JSON 기반이 아닌 시스템과 통신하도록 만드는 데 유용합니다.
따라서 이 문제를 해결한 후 이 문제를 해결하는 방법에는 여러 가지가 있습니다 jq
.
~처럼EchoMike444는 이미 보다 필수적인 방식으로 답변했습니다., 저는 좀 더 간소화된 다른 접근 방식을 사용해 보았습니다.
jq -r -R '
select(contains(" | ")) |
split(" | ") |
.[0] as $text |
(.[1] | fromjson | to_entries | .[0].value ) as $json_obj_value |
"\($text) | \($json_obj_value)"
' yourlogfile.log
기본적으로 우리는
- "|"를 포함하지 않는 줄은 모두 삭제하세요.
- 각 행을 두 부분으로 분할
$text
읽기 쉽도록 왼쪽 부분을 스테이플로 고정하세요.- 올바른 부분을 json으로 구문 분석하고 첫 번째 값을 가져와
$json_obj_value
더 쉽게 읽을 수 있도록 바인딩 에 넣습니다. - 문자열을 인쇄합니다
"$text | $json_obj_value"
(\(foo)
보간을 수행하는 방법입니다jq
).
가능한 한 컴팩트하게 만들고 싶다면 다음을 사용할 수 있습니다.
jq -Rr 'select(contains(" | "))|split(" | ")|"\(.[0]) | \(.[1]|fromjson|to_entries|.[0].value)"' yourlogfile.log
크기는 작지만 읽기도 더 어렵습니다. 어느 것이 가장 좋은지는 취향과 사용 사례에 따라 다릅니다.