次の行の検索列インデックスから文字列を抽出しますか?

次の行の検索列インデックスから文字列を抽出しますか?

単純なディスク使用シェルスクリプト(ダッシュ)を作成しようとしていますが、AWK構文に慣れていないため、文字列を解析する方法がわかりません。

私は次のコマンドを使用します。df -P -BG /たとえば、次のような出力が表示されます。

Filesystem                  xxxxxxxxxx-blocks  Used Available Capacity Mounted on
/dev/mapper/xxx_crypt                    500G  200G      100G      xx% /

「使用済み」(最終的には「使用可能」)の下の列を抽出したいのですが、どうすればよいかわかりません。 AWKには次の検索コマンドがあることがわかります。awk '/Used/ {print}'

しかし、その下の行を取得する方法がわかりません。どのようなヒントがありますか?たとえば、df -P -BG / | awk '...'シェル変数に格納するので、1行でパイプとして機能することが重要ですが、複数行のAWKを使用できるため、必ずしも必要とは思いません。

答え1

実装がサポートしている場合は、構文解析の代わりにdf使用できます。私のアーチLinux--outputから:man df

--output[=FIELD_LIST]
  use the output format defined by FIELD_LIST, or print all fields if
  FIELD_LIST is omitted.

[...]

FIELD_LIST is a comma-separated list of columns to be included.  
Valid field names  are:  'source',  'fstype',  'itotal',  'iused',
'iavail',  'ipcent', 'size', 'used', 'avail', 'pcent', 
'file' and 'target' (see info page).

したがって、あなたの場合は、次のことを行います。

$ df  -BG --output="used" /
 Used
  200G

本当に構文解析が必要な場合は、パターンマッチングにまったく気にしません。入力を制御しても変更されないため、3番目のフィールド(私のシステムの出力)のみを印刷できます。

$ df -P -BG / | awk '{print $3}'
Used
44G

フィールドの順序が変わっても機能する必要がある場合、文字列を含むフィールドを見つける必要がある場合は、Used次のように複雑な操作を実行できます。

$ df -P -BG / | 
   awk '{ if(NR==1){for(i=1; i<=NF; i++){ if($i ~ /Used/){want=i}}} print $want}'
Used
44G

答え2

FPAT、FIELDIDTHS、\s、\S、および gensub() とともに GNU awk を使用します。

$ cat tst.awk
BEGIN { FPAT="\\S+|\\s+"; OFS="\t" }
NR == 1 {
    sub(/Mounted on/,"Mounted_on")
    for ( i=1; i<=NF; i+=2 ) {
        tags[++numTags] = $i
        wids = wids " " length($i $(i+1))
    }
    FIELDWIDTHS = wids
    $0 = $0
}
{
    for ( i=1; i<=NF; i++ ) {
        f[tags[i]] = gensub(/^\s+|\s+$/,"","g",$i)
    }
    print f["Filesystem"], f["Available"], f["Used"]
}

$ cat file | awk -f tst.awk | column -s $'\t' -t
Filesystem             Available  Used
/dev/mapper/xxx_crypt  100G       200G

私にないものと交換してくださいcat file。ソートのために列をdf -P -BG /パイプで接続しましたが、columnこれは必要ありません。

これにより、名前で印刷、比較、並べ替え、目的の列の値に対して算術演算を実行するなどの操作を実行できます。コードで修正しなければならなかった唯一のことは、スペースをアンダースコアに変更して「インストール」することでした。

以下は、すべてのawkで動作するバージョンです。

$ cat tst.awk
BEGIN { OFS="\t" }
NR == 1 {
    sub(/Mounted on/,"Mounted_on")
    while ( match(substr($0,totWid+1),/[^ \t]+[ \t]*/) ) {
        tag = substr($0,totWid+1,RLENGTH)
        sub(/[ \t]+$/,"",tag)
        tags[++numTags] = tag
        begs[numTags] = totWid + 1
        wids[numTags] = RLENGTH
        totWid += RLENGTH
    }
}
{
    for ( i=1; i<=numTags; i++ ) {
        val = substr($0,begs[i],wids[i])
        gsub(/^[ \t]+|[ \t]+$/,"",val)
        f[tags[i]] = val
    }
    print f["Filesystem"], f["Available"], f["Used"]
}

$ awk -f tst.awk file | column -s $'\t' -t
Filesystem             Available  Used
/dev/mapper/xxx_crypt  100G       200G

答え3

使用幸せ(以前のPerl_6)

~$ raku -e 'my $target = "Used"; 
            my @table = lines.map: *.split(/ \h+ /).List; 
            my $col = @table.head.grep(/ $target /, :k).cache; 
            .put for @table.map: *.[ $col.flat ];'   file

上記は、Perlシリーズのプログラミング言語であるRakuで書かれた答えです。つまり、$-sigiled スカラーが宣言され、「Used」という文字列が割り当てられます。その後、lines読み取ると各ペッドが水平スペースmapに入ります。通常、これらの戻りはRakuのaという軽量データ構造に格納されますが、ここではデータを配列に格納することを強制します。split\hSeqList@table

それでは正しい列を見てください。@table.headテーブルの元のヘッダー行(つまり行)を取得します。grep一致を見つけるためにこれらの要素を調べて、列$targetインデックス:k番号のキーを取得して保存します$col。最後に、必要な列のみを返してputデータを出力します。@a.table$col

入力例:

Filesystem                  xxxxxxxxxx-blocks  Used Available Capacity Mounted on
/dev/mapper/xxx_crypt                    500G  200G      100G      xx% /

出力例:

Used
200G

OPは、シェル変数を受け入れるスクリプトの希望を表現しました。 Rakuには、%*ENVシェル変数にアクセスするために使用できる特別な関連配列があります。したがって、次はcolName環境(つまりシェル)からシェル変数を取得します。

~$ env colName="Used" raku -e 'my $target = %*ENV<colName>;   \
                      my @table = lines.map: *.split(/ \h+ /).List;     \
                      my $col = @table.head.grep(/$target/, :k).cache;  \
                      .put for @table.map: *.[ $col.flat ];'   file
Used
200G

次のように、わずかな調整で同様に2つの列などを返すことができます。

~$ env colNames="Used Available" raku -e '   \
                      my @target = %*ENV<colNames>.split(/ \h+ /).List;  \
                      my @table = lines.map: *.split(/ \h+ /).List;      \
                      my @col = @table.head.grep(/@target/, :k);         \
                      .put for @table.map: *.[ @col.flat ];'   file
Used Available
200G 100G

https://docs.raku.org/routine/grep
https://raku.org

関連情報