sed 교체 및 삭제 명령에서 NUL 문자를 구분 기호로 사용하는 방법은 무엇입니까?

sed 교체 및 삭제 명령에서 NUL 문자를 구분 기호로 사용하는 방법은 무엇입니까?

이것은 구분 기호/구분 기호 /path/to/a/path/to/b사용하여 교체하려고 할 때 시도한 것입니다 .NUL

$ cat pathsList| sed -r -e 's\0/path/to/a\0/path/to/b\0g'
sed: -e expression #1, char 27: number option to `s' command may not be zero

내가 가고 싶은 곳 NUL: NUL/허용되지 않는 유일한 문자 ext4fs이며 경로 /이름 구분 기호로 광범위하게 사용되었습니다. 또한 단지 데이터를 사용하기 위해 데이터를 참조하거나 역참조하는 것을 피하고 싶습니다 sed.

NUL구분 기호로 사용할 수 없는 경우 데이터를 인용하고 분리하는 것보다 더 나은 해결 방법이 있습니다.

$ sed --version
sed (GNU sed) 4.4

답변1

불행히도 s///sed에서 명령의 구분 기호로 NUL을 사용하는 것은 불가능해 보입니다.

NUL 문자가 포함된 문자열을 생성하려는 경우 $'...'bash 및 기타 쉘 인식 형식을 사용할 수 있으므로 다음과 같이 작동할 것이라고 생각할 수 있습니다.

sed -r -e $'s\0o\0x\0g'

그러나 Linux(및 일반적으로 Unix)에서 인수가 전달되는 방식으로 인해 NUL이 포함된 문자열을 전달하는 것이 실제로 불가능합니다. 왜냐하면 얻을 수 있는 것은 argc(인수의 수)와 argv(배열)뿐이고 char *그런 다음 NUL- 이기 때문입니다. 종료된 문자열(C 문자열)은 매개변수를 얻을 수 있는 유일한 방법입니다. 즉, 모든 sed(또는 모든 프로그램)는 전달된 내용이 $'s\0o\0x\0g'단순한 지 "s"(그리고 NUL인 경우 이를 문자열의 끝으로 처리해야 함) 확인합니다.

외부 파일로 sed에 전달하면 작동할 것이라고 생각했습니다. 이 경우 sed는 NUL이 포함되어 있음을 알 수 있고 전체 문자열을 길이별로 추적할 수 있기 때문에 다음을 시도했습니다.

$ cat -v script.sed 
s^@o^@x^@g

s ^@는 NUL 바이트입니다. Ctrlv000ASCII 값을 통해 문자를 입력하기 위한 vim 키 입력인 (3개의 0)을 사용하여 vim에 삽입했습니다 .

하지만 이것도 작동하지 않는 것 같습니다.

$ echo "/path/to/a/folder" | sed -r -f script.sed 
sed: file script.sed line 1: delimiter character is not a single-byte character

s흥미롭게도 이는 스크립트 파일에 하나만 있는 경우와 다릅니다. 이 경우 sed는 불평합니다 unterminated 's' command... 그래서 문자열의 길이를 추적하는 것처럼 보이지만 여전히 NUL을 구분 기호로 사용하는 것에 대해 불만스러워 보입니다. .

소스코드를 보면 sed이것이 의도적인 것인지 버그인지는 확실하지 않습니다. is_mb_char()바이트가 멀티바이트 문자의 일부인지 감지하는 함수 에서 NUL 처리이와 같이:

case 0: /* Special case of mbrtowc(3): the NUL character */
  /* TODO: test this */
  return 1;

이 경우에는 return 1"예, 멀티바이트 문자입니다"를 의미하지만 그렇지 않습니다.

위의 몇 줄에 있는 주석은 다음과 같습니다.:

/*
 * Return zero in all other cases:
 *   CH is a valid single-byte character (e.g. 0x01-0x7F in UTF-8 locales);
 *   CH is an invalid byte in a multibyte sequence for the currentl locale,
 *   CH is the NUL byte.
 */

그렇다면 return 0의도적인 것이 아닐까?

이것범죄이 코드가 도입된 컨텍스트에는 더 이상 컨텍스트가 없습니다...

이것매뉴얼 페이지mbrtowc(3)L'\0'나는 그것이 일종의 멀티바이트 NUL이라고 생각했다고 언급했는데 , 그래서 그들이 이런 식으로 처리하기로 결정한 것일까요?

이 정보가 여전히 도움이 되기를 바랍니다!

답변2

NUL은 파일 이름에서 찾을 수 없지만(유사한 이유로 명령 인수에서는 찾을 수 없음), .(매우 일반적임) , ^, *, , 이들 모두는 명령이 표현식을 이해하는 [정규식 에 의해 있는 그대로 이스케이프될 수 있고 있어야 합니다. 운영자 .$\seds

넌 언제나 이렇게 할 수 있어도망가다자동화된 방식으로.

NUL을 제외하고 개행 문자와 모든 멀티바이트 문자는 GNU에서 사용할 수 없습니다 sed. 다른 구현에는 다른 제한사항이 있을 수 있습니다. POSIX는 백슬래시도 금지합니다(GNU에서는 작동하지만 sed). 따라서 백슬래시가 아닌 이식 가능한 문자 집합의 그래픽 문자를 사용하는 것이 좋습니다.

답변3

단일 문자(바이트)를 단일 문자(바이트)로 바꾸려면 다음을 사용하십시오 tr.

$ echo "/path/to/a/folder" | tr ao xy
/pxth/ty/x/fylder

임의 문자열의 경우 Perl을 사용할 수 있습니다.

$ echo "/path/to/a/folder" | patt=o repl=xx perl -pe 's/$ENV{patt}/$ENV{repl}/g'
/path/txx/a/fxxlder

( 명령줄 인수가 처리할 파일 이름을 의미했기 때문에 patt환경을 전달했습니다 .)replperl -p

물론 이는 patt정규식으로 처리되며 모든 항목을 포함합니다.

$ echo "/path/to/a/folder" | patt='a.' repl=x perl -pe 's/$ENV{patt}/$ENV{repl}/g'
/pxh/to/xfolder

따라서 점( \.) 및 기타 특수 문자를 이스케이프하거나 다음을 사용해야 합니다 \Q$ENV{patt}.

$ echo "/path/to/a/folder.txt" | patt=. repl=, perl -pe 's/\Q$ENV{patt}/$ENV{repl}/g'
/path/to/a/folder,txt

위의 두 경우(명령줄 인수 및 환경 변수)에서 운영 체제와 유틸리티 간의 인터페이스는 문자열을 C 표준 라이브러리에서 사용하는 NUL 종료 문자열로 전달합니다. 이 인터페이스는 인수에 리터럴 NUL 바이트를 삽입하는 것을 불가능하게 하며, sed -e 's\a\x\g'sed는 리터럴 백슬래시를 s명령의 구분 기호로 사용합니다.

답변4

@cerving의 답변은 가깝지만 tr을 사용할 필요는 없습니다.

cat pathsList| sed -z 's/\n/\x0/g'

-z구분자 로 사용됩니다 \x0. 이는 본질적으로 파일을 긴 문자열로 변환합니다(pathsList에 아직 파일이 포함되어 있지 않은 경우 \x0). 따라서 파일이 사용 가능한 메모리에 비해 너무 커서는 안 됩니다.

관련 정보