Techioz Blog

Ruby での Awk の FNR==NR に相当するものは何ですか?

概要

Awk には、現在のファイルから読み取られたレコード数 (通常は行数) と合計を表す組み込み変数 FNR および NR があります。

awk では、次のものが一般的です。

$ awk 'FNR==NR {first file lines; next } {process remaining lines } f1 f2

通常、f1 には残りのファイルの処理方法を決定する値が含まれます。 (キーワード、行番号など)

Ruby には素晴らしいテキスト処理言語の素質が備わっています。ルビーには$があります。 awk の NR に相当するものとして FNR に相当するものは何ですか?

解決策

与えられる:

$ head f?.txt
==> f1.txt <==
line 1
line 2

==> f2.txt <==
line 3
line 4

Ruby には、STDIN を読み取るか、コマンド ラインからファイルを開く ARGF ストリーム (Perly を感じたい場合は $< というエイリアス) があります。 awk と同じ動作:

$ awk '{
    printf("FILENAME: %s, FNR: %s, NR: %s, %s\n", FILENAME, FNR,NR,$0)}
' f?.txt
FILENAME: f1.txt, FNR: 1, NR: 1, line 1
FILENAME: f1.txt, FNR: 2, NR: 2, line 2
FILENAME: f2.txt, FNR: 1, NR: 3, line 3
FILENAME: f2.txt, FNR: 2, NR: 4, line 4

$ ruby -lne '
    printf("FILENAME: %s, FNR: %s, NR: %s, %s\n", $<.file.path, $<.file.lineno, $., $_)
' f?.txt
FILENAME: f1.txt, FNR: 1, NR: 1, line 1
FILENAME: f1.txt, FNR: 2, NR: 2, line 2
FILENAME: f2.txt, FNR: 1, NR: 3, line 3
FILENAME: f2.txt, FNR: 2, NR: 4, line 4

STDIN とファイルの両方を読み取りたい場合は、ファイルのプレースホルダーとして - を使用します。

$ echo '123' | awk '1' - <(echo 456)
123
456
$ echo '123' | awk '1' <(echo 456) -
456
123

$ echo '123' | ruby -lne 'puts $_' - <(echo 456)
123
456
$ echo '123' | ruby -lne 'puts $_' <(echo 456) -
456
123

さらにいくつかの対応する変数:

╔══════════╦═══════════════════╦═════════════════════════════════════════╗
║   awk    ║       ruby        ║                 comment                 ║
╠══════════╬═══════════════════╬═════════════════════════════════════════╣
║ $0       ║ $_                ║ unsplit record (line usually)           ║
║ NF       ║ $F.length         ║ Number of fields from autosplit         ║
║ FNR      ║ ARGF.file.lineno  ║ Number records read from current source ║
║ NR       ║ ARGF.lineno or $. ║ Total number of records so far          ║
║ (magic)  ║ ARGF or $<        ║ stream from either STDIN or a file      ║
║ $1..$NF  ║ $F[0]..$F[-1]     ║ First to last field from autosplit      ║
║ FS       ║ $;                ║ Input field separator                   ║
║ RS       ║ $/                ║ Input record separator                  ║
║ FILENAME ║ $<.file.path      ║ Filename of file being processed        ║
╚══════════╩═══════════════════╩═════════════════════════════════════════╝      

したがって、f1 に行番号のリストがあり、それらの行番号でインデックスを付けたいテキスト ファイルがある場合 (awk または sed を使用する場合)、Ruby を使用することができます。

与えられる:

$ echo "1
2
44
2017" >f1
$ seq 10000 | awk '{print "Line", $1}' >f2

awk では次のようにします。

$ awk 'FNR==NR{ln[$1]; next} 
       FNR in ln'    f1 f2

Ruby では次のことができます。

$ ruby -lane 'BEGIN{h=Hash.new}
              if $<.file.lineno == $<.lineno
                 h[$F[0].to_i]=true
                 next
              end
              puts $_ if h[$<.file.lineno]' f1 f2

両方とも次のように出力されます。

Line 1
Line 2
Line 44
Line 2017

この例の awk バージョンは約 5 倍高速です (awk に進みます)。ただし、Ruby バージョンは、JSON、XML、複雑な CSV など、awk では不可能な入力を簡単にサポートします。

Ruby で awk -v [同じ変数] と同等の機能が必要な場合は、Ruby で複数の -e コマンド実行行を使用します。

n をシェル変数として awk でファイルの n 行を出力するには:

awk -v n="${sh_n}" 'FNR==n{print; exit}' big_file.txt

または:

awk 'FNR==n{print; exit}' n="${sh_n}" big_file.txt

Ruby で同じことを行うには、つまり、 の値を Ruby プログラムに渡すには、次のようにします。

ruby -e "n=${sh_n}.to_i" -e '$<.each_line{|line| if $.==n then puts line; exit(0) end}' big_file.txt

ここに、事前定義された Ruby 変数のリストがあります。

ここにグローバル変数のリストがあります