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興味深いことに、これはスクリプトファイルに1つしかない場合とは異なります。この場合、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

上記の2つの場合(コマンドライン引数と環境変数)では、オペレーティングシステムとユーティリティ間のインタフェースは、文字列を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)。したがって、ファイルが使用可能なメモリよりも大きすぎてはいけません。

関連情報