awkに一致後に次の列を提供させる方法

awkに一致後に次の列を提供させる方法

次のファイル(somefile.txt)があります。

/A/1/B/1/C/1/D/1/E/1/F/2/G/1/H/1/I/1/J/1/K/1/
/B/1/C/1/D/1/E/1/F/5/G/1/H/1/I/1/J/1/K/1/
/C/1/D/1/E/1/F/9/G/1/H/1/I/1/J/1/K/1/
/D/1/E/1/F/7/G/1/H/1/I/1/J/1/K/1/
/A/1/B/1/C/1/D/1/E/1/F/8/G/1/H/1/I/1/J/1/K/1/
/A/1/B/1/C/1/D/1/E/1/F/3/G/1/H/1/I/1/J/1/K/1/
/A/1/B/1/C/1/D/1/E/1/F/6/G/1/H/1/I/1/J/1/K/1/
/B/1/C/1/D/1/E/1/F/8/G/1/H/1/I/1/J/1/K/1/
/D/1/E/1/F/3/G/1/H/1/I/1/J/1/K/1/
/C/1/D/1/E/1/F/6/G/1/H/1/I/1/J/1/K/1

私は次の結果が欲しい(後の次の数字F):

2
5
9
7
8
3
6
8
3
6

1行あたりの列数が可変であれば、次のようにする方法はありますか? :

awk -F'/' '/F/ {print <column_of_match> + 1 }' somefile.txt

答え1

Perlを使用すると配列の分割が便利なので、配列内の各要素のペアをハッシュのキーと値として扱うことができます。

$ perl -F/ -lane '%f = @F[1..$#F]; print $f{F}' input.txt 
2
5
9
7
8
3
6
8
3
6

Perl-F-a(自動分割)はawkと同様に機能しますが、行を自動的に$ 1、$ 2、$ 3などに分割するのではなく、各行をという配列に自動的に分割します@F

このスクリプトは、@F配列スライス(0番目の要素を除くすべての要素)を名前付きハッシュ(連想配列)に変換し、%fキー「F」を使用して要素を印刷します。%f


機能/動作方法(そして空の文字列である@ Fのゼロ番目の要素を除外する理由)を強調するために、使用時には次のようになり@Fます%fデータ::ダンプモジュールdump機能:

$ perl -F/ -MData::Dump=dump -lane '
    %f = @F[1..$#F];
    print join("\n", $_, dump(@F), dump(\%f), $f{F}), "\n"' input.txt 
/A/1/B/1/C/1/D/1/E/1/F/2/G/1/H/1/I/1/J/1/K/1/
("", "A", 1, "B", 1, "C", 1, "D", 1, "E", 1, "F", 2, "G", 1, "H", 1, "I", 1, "J", 1, "K", 1)
{ A => 1, B => 1, C => 1, D => 1, E => 1, F => 2, G => 1, H => 1, I => 1, J => 1, K => 1 }
2

/B/1/C/1/D/1/E/1/F/5/G/1/H/1/I/1/J/1/K/1/
("", "B", 1, "C", 1, "D", 1, "E", 1, "F", 5, "G", 1, "H", 1, "I", 1, "J", 1, "K", 1)
{ B => 1, C => 1, D => 1, E => 1, F => 5, G => 1, H => 1, I => 1, J => 1, K => 1 }
5

/C/1/D/1/E/1/F/9/G/1/H/1/I/1/J/1/K/1/
("", "C", 1, "D", 1, "E", 1, "F", 9, "G", 1, "H", 1, "I", 1, "J", 1, "K", 1)
{ C => 1, D => 1, E => 1, F => 9, G => 1, H => 1, I => 1, J => 1, K => 1 }
9
...and so on...

F注:入力がない場合は空白行が印刷されます。これが望ましくない場合は、次のようにします。

perl -F/ -lane '%f = @F[1..$#F];
                if (defined $f{F}) {
                  print $f{F}
                } else {
                   print STDERR "Error on input line $.: F has absconded"
                }' input.txt

答え2

使用された回答は次のとおりですsed

$ sed -n 's|.*F/\([0-9]\).*|\1|p' <<EOF
/A/1/B/1/C/1/D/1/E/1/F/2/G/1/H/1/I/1/J/1/K/1/
/B/1/C/1/D/1/E/1/F/5/G/1/H/1/I/1/J/1/K/1/
/C/1/D/1/E/1/F/9/G/1/H/1/I/1/J/1/K/1/
/D/1/E/1/F/7/G/1/H/1/I/1/J/1/K/1/
/A/1/B/1/C/1/D/1/E/1/F/8/G/1/H/1/I/1/J/1/K/1/
/A/1/B/1/C/1/D/1/E/1/F/3/G/1/H/1/I/1/J/1/K/1/
/A/1/B/1/C/1/D/1/E/1/F/6/G/1/H/1/I/1/J/1/K/1/
/B/1/C/1/D/1/E/1/F/8/G/1/H/1/I/1/J/1/K/1/
/D/1/E/1/F/3/G/1/H/1/I/1/J/1/K/1/
/C/1/D/1/E/1/F/6/G/1/H/1/I/1/J/1/K/1
EOF
2
5
9
7
8
3
6
8
3
6

説明-n 's|.*F/\([0-9]\).*|\1|p':

  • -n明示的に指示しない限り、何も印刷しないことを意味します。
  • p式の尾は、「この式が一致するとこの行を印刷します」を意味します。これは、シンボルのない行はF/[0-9]印刷されないことを意味します。
  • s|foo|bar|表現の意味は次fooのとおりです。で置き換えますbar。と思われますが、s/foo/bar/表現にaがあって避けたんです。/|
  • 競争部分(foo):
    • .*F/[0-9].*意味:F/数字を含むすべての行。
    • .*F/\([0-9]\).*F/意味:数字を含む行全体と一致しますが、その数字を覚えておいてください。
  • 交換部品(bar):
    • \1私たちが覚えている数字を表します。

簡単に言うと:

  • 一致する行を見つけて*F/[0-9]*数字だけに置き換えます。

複数桁の正の整数を使用できる場合は、式を簡単に調整できます。

sed -n 's|.*/F/\([0-9]\+\)/.*|\1|p'

答え3

区切り文字に一致するパターンを使用して、対応する部分文字列を配列に分割してFサブフィールドを印刷します。

テストコード:

$ awk 'match ($0, "/F/[^/]/") {
    split (substr ($0, RSTART, RLENGTH), V, "/");
    print V[3];
}' Match.txt

フィールドを繰り返すか、2つのプロセスを使用する必要はありません。

文字列インデックスを調整してsplit不要な部分を削除することもできますが、これにより使用率が低下し、ワンタイムエラーが発生する可能性が高くなります。

awk 'match ($0, "/F/[^/]/") {
    print substr ($0, RSTART+3, RLENGTH-4);
}' Match.txt

答え4

以下は、awkを2回使用することに関連する問題に対する考えられる解決策です。一度は正しい位置で分割し、次回は数字を取得して印刷します。

スクリプトは次のとおりです。

awk -F "/F/" '{print $2}' prova.txt  | awk -F "/" '{print $1}'

最初の部分では、2番目の部分の最初の文字が私たちが探している数字になるように入力文字列を分割し、/F/スクリプトの2番目の部分ではその数字を分離します。

これは、1行に最大1つしかない場合に機能しますF(空白行のみを印刷するため、Fなしで動作します)。

関連情報