검색했는데 내가 뭘 잘못하고 있는지 모르겠지만 이 질문에 대한 답을 찾을 수 없습니다.
모든 텍스트가 한 줄로 저장되는 파일이 있습니다. 패턴을 찾아서 구분 기호까지 해당 텍스트 앞뒤의 모든 텍스트를 삭제해야 합니다.
전임자. 문서
[{"something":false,"more":"123","moresamerecord":"otherstuff"},{"something":false,"more":"abc","moresamerecord":"otherstuff"},{"something2":false,"more":"def","moresamerecord":"otherstuff"},{"something2":false,"more":"456","moresamerecord":"otherstuff"}]
이는 여러 레코드가 포함된 단일 행이라는 점을 기억하세요. "abc"를 찾아 이전 레코드와 다음 레코드 사이의 모든 항목을 삭제하려고 합니다.
예상되는 결과는 다음과 같아야 합니다.
[{"something":false,"more":"123","moresamerecord":"otherstuff"},{"something2":false,"more":"def","moresamerecord":"otherstuff"},{"something2":false,"more":"456","moresamerecord":"otherstuff"}]
나는 노력했지만 이것을 알아낼 수 없습니다. 어떤 도움이라도 대단히 감사하겠습니다.
답변1
이미 지적했듯이 jq
이러한 유형의 데이터를 위한 도구가 있습니다. 그러나 jq는 "객체 목록은 대괄호로 표시되는 배열에 있어야 합니다"와 같은 특정 구문 제약 조건을 적용합니다.
파일이 이미 유효한 json인지 확인할 수 없는 경우 sed를 사용하여 사전 처리할 수 있습니다(결과를 더 쉽게 볼 수 있고 정확성도 확인하므로 jq를 통해 초기 실행을 수행합니다).
$ sed 's/^/[/; s/,$/]/' data.txt | jq -r '.[]'
{
"something": false,
"more": "123",
"moresamerecord": "otherstuff"
}
{
"something": false,
"more": "abc",
"moresamerecord": "otherstuff"
}
{
"something2": false,
"more": "def",
"moresamerecord": "otherstuff"
}
{
"something2": false,
"more": "456",
"moresamerecord": "otherstuff"
}
이제 일치하는 객체를 삭제하도록 jq 명령을 수정해 보겠습니다 "more": "abc"
.
$ sed 's/^/[/; s/,$/]/' data.txt | jq -r '.[] | select(.more != "abc")'
{
"something": false,
"more": "123",
"moresamerecord": "otherstuff"
}
{
"something2": false,
"more": "def",
"moresamerecord": "otherstuff"
}
{
"something2": false,
"more": "456",
"moresamerecord": "otherstuff"
}
마지막으로 공백 없이 쉼표 구분 기호를 사용하여 한 줄로 다시 압축하려면 후처리 단계도 필요한 것 같습니다.
$ sed 's/^/[/; s/,$/]/' data.txt | jq -r '.[] | select(.more != "abc")' | sed 's/}$/},/' | tr -d ' \n'
{"something":false,"more":"123","moresamerecord":"otherstuff"},{"something2":false,"more":"def","moresamerecord":"otherstuff"},{"something2":false,"more":"456","moresamerecord":"otherstuff"},
답변2
기본 아이디어는 패턴을 더 이상 구분자가 아닌 구분 기호까지 확장하는 것입니다.
따라서 가장 가까운 일치 항목부터 시작하려면 {
가 아닌 문자가 뒤에 오는 "abc"
것을 찾습니다 . 마찬가지로, "}" 뒤에 오지 않는 문자를 찾아 가장 가까운 후속 문자로 확장할 수 있습니다.{
{
"abc"
}
}
그런 다음 쉼표를 처리하는 몇 가지 극단적인 경우가 있습니다.
sed 's/{[^{]*"abc"[^}]*}//;s/,,/,;s/,$//;s/^,//'
데이터가 표시된 것보다 더 복잡한 경우, 특히 {
및가 }
중첩될 수 있는 경우 구문 분석으로 전환할 수 있습니다. 정규 표현식은 "셀 수 없습니다". 따라서 특정 유한 깊이(예: 3)를 처리하는 패턴을 작성할 수 있지만 임의의 깊이는 처리할 수 없습니다.
jq
sed를 사용하는 대신 주석의 제안을 사용해 보는 것이 확실히 가치가 있습니다.
답변3
jq
이것이 해결책이 아니라면 다음을 권장합니다 .
# Instead of a single line pattern matching,
# make the "records" one per line
# then delete the line with the pattern
# finally get everything again to a single line
sed -e 's:,{:\n{:g;s:,$::' file | sed '/abc/d' | tr '\n' ','
단계별:
$ sed -e 's:,{:\n{:g;s:,$::' file
{"something":false,"more":"123","moresamerecord":"otherstuff"}
{"something":false,"more":"abc","moresamerecord":"otherstuff"}
{"something2":false,"more":"def","moresamerecord":"otherstuff"}
{"something2":false,"more":"456","moresamerecord":"otherstuff"}
$ sed -e 's:,{:\n{:g;s:,$::' foo.txt | sed '/abc/d'
{"something":false,"more":"123","moresamerecord":"otherstuff"}
{"something2":false,"more":"def","moresamerecord":"otherstuff"}
{"something2":false,"more":"456","moresamerecord":"otherstuff"}
$ sed -e 's:,{:\n{:g;s:,$::' foo.txt | sed '/abc/d' | tr '\n' ','
{"something":false,"more":"123","moresamerecord":"otherstuff"},{"something2":false,"more":"def","moresamerecord":"otherstuff"},{"something2":false,"more":"456","moresamerecord":"otherstuff"},
답변4
awk '
BEGIN { FS = "},{" }
{ k=0
for (i=1; i<=NF; i++)
if ($i !~ /"abc"/)
printf "%s%s", (k++?FS:""), $i
$0=""
}1
' file
$ cat file \
| sed -e 's/},{/}\n{/g' \
| sed -E '/([{:,])"abc"([,:}])/d' \
| paste -sd, - \
;
- 레코드를 한 줄로 구분합니다.
- 이제 다음이 포함된 모든 기록을 삭제하세요.
"abc"
- 쉼표로 레코드를 꿰매세요
,
산출:
{"something":false,"more":"123","moresamerecord":"otherstuff"},{"something2":false,"more":"def","moresamerecord":"otherstuff"},{"something2":false,"more":"456","moresamerecord":"otherstuff"}