0/null 区切り文字により列コマンドが中断される

0/null 区切り文字により列コマンドが中断される

質問

フィールドが文字で区切られた\n行(区切られた)で構成されるデータを解析したいと思います。NUL\0

多くのLinuxコマンドは、--zerofor find、for、またはforなどのオプションを使用して区切り文字をforとして定義することによって、-0この区切り文字を処理します。xargs\0gawk

column区切り文字として解釈する方法を理解できませんNUL

はい

次のデータセットが生成される場合(2行と3行、区切り\0):

echo -e "line1\nline2" | awk 'BEGIN {OFS="\0"} {print $1"columnA",$1"columnB",$1"columnC"}'

予想どおり、次の出力が表示されます(\0区切り記号は表示されませんが、各フィールドは区切ります)。

line1columnAline1columnBline1columnC
line2columnAline2columnBline2columnC

しかし、私の列を表示するために列を使用しようとすると、何らかの理由で渡されたにもかかわらず、\0出力に最初の列だけが表示されます。

echo -e "line1\nline2" \ | awk 'BEGIN {FS="\0"; OFS="\0"} {print $1"columnA",$1"columnB",$1"columnC"}' | column -s '\0'
line1columnA    line2columnA

実際に区切り文字が指定されていなくても、列はNULL文字で中断されるように見えます。

echo -e "line1\nline2" \ | awk 'BEGIN {FS="\0"; OFS="\0"} {print $1"columnA",$1"columnB",$1"columnC"}' | column
line1columnA    line2columnA

質問

  • \0これをフィールド/列区切り文字として使用する方法はありますかcolumn
  • \0選択/追加の質問:列がこのように動作するのはなぜですか(管理されていない場合、列は完全に無視され、行全体が単一のフィールドとして印刷されると予想されます)。
  • \0選択/ボーナス質問2:この列のデータの一部は、ベストプラクティスとして使用したいファイルパスになります。潜在的に競合する可能性があるフィールド区切り文字をエスケープせずにファイルに「任意の文字列」を格納するより良い方法を提案しますか?

答え1

列内のフィールド/列区切り文字として\ 0を使用する方法はありますか?

columnいいえ、(私が知っている限り)両方の実装歴史の中のBSDそしてその一つユーティリティLinuxパッケージ、両方とも標準Cライブラリの文字列操作関数を使用して入力行を解析し、これらの関数は文字列がNULで終わると仮定して機能します。つまり、NUL バイトは次のことを意味します。いつも表示終わり~のどのひも。

選択/追加の質問:列がこのように動作するのはなぜですか(管理されていない場合は\ 0が完全に無視され、行全体が単一のフィールドとして印刷されると予想されます)。

上記に加えて、このオプション-s言葉数値。そうではないエスケープ構文を解析します\0(また\n重要ではありません)。これは意味するa と a を有効な区切り文字としてcolumn扱うように言っています。\0入力してください。

$''これをサポートする多くのシェルの1つを使用している場合(例では使用できますがbash 使用できませんdash)、文字列構文を介してエスケープシーケンスを提供できます。したがって、たとえば(<newline>を列区切り文字として指定)は、これらのシェルの1つによって実行される場合にcolumn -s $'\n'有効です。

しかし、あなたの期待が何であるかはわかりませんcolumn。 NULを区切り文字としてサポートしていても、その入力の各行を出力時に列全体に変換します。たぶん-t行ごとに個々のフィールドを劣化させるためにsoを使用したいですか?

オプション/追加の質問2:この列のデータの一部はファイルパスになり、\ 0をベストプラクティスとして使用したいと思います。潜在的に競合する可能性があるフィールド区切り文字をエスケープせずにファイルに「任意の文字列」を格納するより良い方法を提案しますか?

私が知っている唯一の方法は、各フィールドの前に長さを付けてテキストまたはバイナリ形式(適切だと思うように)で表現することです。しかし、確かにそれらをcolumn

また、問題がファイルパスの場合は、次の点を考慮する必要があります。いいえ\nファイル名には完全に有効な文字なので、「構造」区切り文字として使用してください。

概念証明と同様に、例に基づいていますが、NULを構造体/レコード区切り文字として使用し、長さフィールドを使用します。(また、マルチバイト文字を含むようにサンプル文字列を少し変更しました。)

echo -e 'line1\nline2 ò' \ | LC_ALL=C awk '
    BEGIN {
        ORS="\0"
# here we just move arguments away from ARGV
# so that awk reads input from stdin
        for (i in ARGV) {
            c[i]=ARGV[i]
            delete ARGV[i]
        }
    }
    {
# first field is the line read
        printf "%4.4d%s", length, $0
# then a field for each argument
        for(i=1; i<length(c); i++)
            printf "%4.4d%s", length(c[i]), c[i]
        printf "%s", ORS
    }
' "€ column A" $'colu\nmnB' "column C"

使用議論awk任意の数の列文字列を渡します。

その後、対応する仮想スクリプト(実際には、または処理する必要がawkあります)は次のようになります。gawkmawkRS="\0"

LC_ALL=C awk '
    BEGIN { RS="\0" }
    {
        nf=0; while(length) {
            field_length = substr($0, 1, 4)
            printf "field %d: \"%s\""ORS, ++nf, substr($0, 5, field_length)
            $0 = substr($0, 5+field_length)
        }
        printf "%s", ORS
    }
'

指定することに注意してください同じ両方のスクリプトのロケール設定が文字サイズと一致します。どちらも指定してLC_ALL=Cも大丈夫です。

答え2

あなたの列はawkコマンドにも達しません。 echo コマンドの前でも最初の 0 以降の内容はすべて失われます。変数にバイナリゼロを格納できません。

var=$'zzz\x00zzz'
echo "${#var}"
3
var=$'zzz\xFFzzz'
echo "${#var}"
7

実行したい操作を開始する前に、trすべてのゼロを必要な別の区切り文字に変更できます。

またはシェルをzsh

関連情報