큰 JSON 파일이 있는데 한 문자열을 다른 문자열로 바꾸고 싶습니다. 그렇게 해서는 안 되지만, 문자열을 바꾸고 싶지 않은 상황에서 문자열을 사용하는 것이 가능할 수도 있습니다.
올바른 컨텍스트에서 파일에서 해당 문자열이 몇 번 발생하는지 알고 있으므로 해당 문자열이 로 대체되는 횟수도 인쇄하고 싶습니다 sed
. 어떻게 해야 합니까?
문자열을 찾아 바꾸려면 다음을 사용합니다.
sed -i "" "s/my_string/new_string/g" my_file.json
저는 Mac을 사용하고 있지만 Linux도 필요합니다. 예를 들면 다음과 같습니다.
sed -i "s/my_string/new_string/g" my_file.json
나는 a를 실행하여 grep
파일에서 문자열을 찾고 다음과 같이 개수를 반환할 수 있다는 것을 알고 있습니다.
grep -o my_string my_file.json | wc -l
그러나 그것은 내가 묻는 것이 아닙니다. 텍스트 편집기(단어, 메모장, geany 등)처럼 수행할 수 있는 방법이 있는지 묻고 있습니다. 문자열을 주면 이 문자열을 보고 대체한 횟수를 알려줍니다. 하나.
추가 정보 - Bash 스크립트에서 실행되므로 다른 더 좋은 방법이 있다면 열려 있습니다.
답변1
그냥 perl
대신 사용하세요. 이는 and 대체 연산자만큼 이식 가능하며 sed
(이 경우 구문은 perl
설치된 모든 시스템에서 동일하므로 이식성이 더 높습니다) -i
대체 수를 인쇄하도록 지시할 수 있습니다.
perl -i -lpe '$k+= s/my_string/new_string/g; END{print "$k"}' my_file.json
이렇게 하면 교체 작업이 수행되고 교체 번호가 표준 출력으로 인쇄됩니다.
답변2
sed를 사용해야 하는 경우 한 가지 접근 방식은 다음과 같습니다.
@Phillipos가 제안한대로 다음과 같이 변경하십시오.
sed -i "" -e '
s/my_string/new_string\
/g;s/\n//w /dev/stdout
s///g
' my_file.json | wc -l
- my_string이 나타날 때마다 개행 문자 +를 넣으면 변경됩니다.
- 그런 다음 sed가 인쇄할 때 암시적으로 개행 문자를 추가하므로 개행 문자를 제거합니다.
- 대체가 성공한 경우에만 조건부로 stdout에 기록됩니다. 즉, 행에 my_string mng가 포함된 경우
- 그런 다음 개행 마커를 제거합니다.
답변3
강제로 할 수 있다정력편집기는 스트리밍 모드에서 다음을 보고합니다.
ex -nsc 'redir! >/dev/stderr' -c '%s/pattern/PATTERN/g' -c 'redir END' -c 'wq' my_file
3 substitutions on 2 lines
ex
-vim 모드(또는 vim -e
)
-n
--스왑 파일을 생성하지 마십시오
-s
-스크립트
-c
명령줄 모드(또는 +'command'
)
'redir! >/dev/stderr'
--쉘 표준 오류로 리디렉션
'redir END'
-리디렉션이 종료됩니다. 생략 가능
'wq'
-변경 사항을 저장하고 편집기를 종료합니다. 이를 로 바꾸면 'q!'
출력을 얻고 예상한 것과 비교하기 위해 파일을 변경할 필요가 없습니다.
답변4
다음은 변경 사항과 영향을 받는 줄을 계산하는 간단한 awk 버전입니다.
#! /bin/bash
Awk='
BEGIN { fmtEnd = "Made %d substitutions on %d lines.\n"; }
{
n = gsub (/exit/, "return");
if (n) { Lines++; Count += n; }
print;
}
END { printf (fmtEnd, Count, Lines) > "/dev/stderr"; }
'
awk "${Awk}" doFifo > doFifo.fix
출력(stderr)은 다음과 같습니다. 카운트를 더 쉽게 복구할 수 있도록 재정렬할 수 있습니다.
Made 8 substitutions on 6 lines.
GNU/awk에는 -i inplace 확장자가 있지만 저는 내부 업데이트에 대해 매우 보수적입니다. 내 고객은 종종 자신의 데이터가 항상 100% 정확하다고 불평하고 주장하므로 감사 추적과 데이터의 모든 버전을 유지합니다.
다음은 변경된 각 줄을 기록하는 awk 변형입니다. 이것은 아직 프로덕션 수준이 아닙니다. 패턴과 대체를 인수로 받아들이고, 한 번의 실행으로 여러 파일을 처리하고, 입력을 기반으로 출력 파일의 이름을 지정하고, 파일 및 전체 합계별로 요약하기를 원합니다. 어쩌면 패턴 배열->교체도 허용할 수도 있습니다.
#! /bin/bash
AwkFull='
BEGIN {
reFix = "exit"; txFix = "return";
fmtEnd = "Made %d substitutions on %d lines.\n";
fmtSub = "\n.... %d Changes on file %s line %d:\n";
fmtSub = fmtSub "Was: %s\nNow: %s\n";
}
{
New = $0;
n = gsub (reFix, txFix, New);
if (n == 0) { print $0; next; }
Lines++; Count += n;
printf (fmtSub, n, FILENAME, FNR, $0, New) > "/dev/stderr";
print New;
}
END { printf (fmtEnd, Count, Lines) > "/dev/stderr"; }
'
awk "${AwkFull}" doFifo > doFifo.fix
이는 변경된 각 줄을 보여줍니다. 예를 들면 다음과 같습니다.
.... 2 Changes on file doFifo line 64:
Was: (exit) printf 1>&7 '%(%T)T Received exit command\n' -1
Now: (return) printf 1>&7 '%(%T)T Received return command\n' -1
편집: 매개변수를 명령 매개변수로 전환했습니다.
위의 첫 번째 버전은 패턴과 대체 텍스트를 gsub 명령 자체에 포함합니다. 두 번째 버전에서는 (a) 이름을 지정하고 (b) 코드 헤드에서 선언하여 변경하기가 더 쉽습니다.
코드 일반화의 다음 단계는 셸에서 코드를 전달하는 것입니다. 이것은 awk에서는 쉽습니다. 먼저 reFix 및 txFix를 정의하는 줄을 삭제합니다. 규칙은 re
정규식과 tx
텍스트에 대한 것이지만 일관성이 있는 한 변수를 자유롭게 호출할 수 있습니다.
쉘 문자열을 awk 변수에 넣으려면 옵션이 있습니다 -v
. 따라서 awk 명령은 다음과 같습니다.
awk -v reFix="exit" -v txFix="return" "${AwkFull}" doFifo > doFifo.fix
쉘 변수 사용의 마지막 단계는 모든 형태의 쉘 대체를 사용하는 것입니다. 예를 들면 다음과 같습니다.
awk -v reFix="${1}" -v txFix="${myNew}" "${AwkFull}" doFifo > doFifo.fix
두 가지(아마도 그 이상) 단점이 있습니다.
(1) awk는 이것이 /exit/
패턴이라는 것을 알고 있습니다. 어떤 경우에는 구문을 명확히 해야 할 수도 있습니다. 예를 들어 간단한 줄 일치는 /exit/
로 다시 작성해야 $0 ~ reFix
하지만 awk는 첫 번째 인수가 gsub()
패턴이라는 것을 알고 있으므로 구문이 변경되지 않습니다. (바라보다https://www.gnu.org/software/gawk/manual/gawk.html#Strong-Regexp-Constants더 알아보기. )
(2) 변수의 패턴은 awk 프로그램을 처음 읽을 때 구문 검사를 받지 않으며, 사용할 때만 구문 검사를 받습니다. 따라서 사용자가 입력한 패턴은 모호한 오류 메시지로 인해 런타임 중에 쉽게 중단될 수 있습니다.