ファイルから特定の行(n、n + 4、n + 8、n + 12…)を選択する方法は?


11

入力:

1
hgh
h2b
h4h
2
ok
koko
lkopk
3
uh
ju
nfjvn
4

期待される出力:

1
2
3
4

したがって、出力ファイルには、ファイルの1番目、5番目、9番目、13番目の値のみが必要です。これを行う方法?


2
参照:unix.stackexchange.com/questions/325985/…GNU sedを使用すると、次のことができますsed -n '1~4p'
Sundeep

回答:


28

AWKの使用:

awk '!((NR - 1) % 4)' input > output

これがどのように機能するかを理解することは、読者の練習問題として残されています。


この短いawkコースに感謝します!
darxmurf

20
NR % 4 == 1より読みやすいIMOになります。
ステファンシャゼル

12
合意@ステファン; これはおそらく私の側では疑わしいですが、宿題の可能性がある質問については、私の回答を少し難読化しようとしています...
スティーブン・キット

@StephenKittはあなたの答えを難読化しますか?本当に?これはそれを行う場所ではありません。
データ

22

split (GNU coreutils)を使用:

split -nr/1/4 input > output
  • -nCHUNKS出力ファイルを生成する

そして、CHUNKSなど

  • r/K/N ラウンドロビン分布を使用し、行/レコードを分割せずにNのKthだけを標準出力に出力します

1
吹き飛ばされた心。このような答えが、このSEが大好きな理由です。ありがとう!
user1717828

21

GNUの場合sed

sed '1~4!d' < input > output

標準を使ってsed

sed -n 'p;n;n;n' < input > output

1し、4$n$i変数:

sed "$n~$i!d" # GNU only
awk -v n="$n" -v i="$i" 'NR >= n && (NR % i) == (n % i)'

7

必須のperlソリューションを追加します。

perl -ne 'print if $. % 4 == 1' input > output

4

楽しみのためのPythonバージョン:

with open('input.txt') as f:
    for i, line in enumerate(f.readlines()):
        if i%4 == 0:
            print(line.strip())

enumerate(f)より少ないメモリを消費しながら仕事をすることができるはずです
iruvar

@iruvarいいですね!以前はそのことに気づかなかった。将来的に使用されます。この回答に自由に編集してください。他のBashの答え(特にこれ)は間違いなく進むべき道なので、最適化でそれを維持するつもりはありません。
user1717828

使用する場合readlines(したがって、ファイル全体をメモリに丸呑みする場合)、を使用f.readlines()[::4]して4行ごとに取得できます。したがって、使用できますprint(''.join(f.readlines()[::4]))
Nick Matteo

3

POSIX sed:このメソッドはposixly sedを使用するため、どこでも実行でき、少なくともposixを尊重するsedを実行できます。

 $ sed -ne '
   /\n/!{
    H;s/.*//;x
   }

   :loop
       $bdone
       N;s/\n/&/4
       tdone
   bloop

   :done
   s/.//;P
 ' input.file

もう1つは、スケーラビリティを目的としたプログラムによるsedコードの生成です。

$ code=$(yes n | head -n 4 | paste -sd\; | sed s/n/p/)
$ sed -ne "$code" input.file

Perl:配列Aをサイズが4になるまで埋めます。次に、その最初の要素を出力し、配列も消去します。

$ perl -pe '
   $A[@A] = @A ? <> : $_ while @A < 4;
   $_ = (splice @A)[0];
' input.file

1

scriptname filename skip(あなたの場合は4)で呼び出しiterます。ファイルの先頭から行を引き出し、最後の行だけを出力することで機能します。次に、の値がinを超えていない限り、増分iterskips、繰り返します。iterlinesfile

#!/bin/bash
file="$1"
lines=`wc -l < "$file"`
skips="$2" || "4"
iter=1
while [ "$iter" -le "$lines" ]; do
 head "$file" -n $iter | tail -n 1
 iter=$(( $iter + $skips ))
done

1

ピュアバッシュ:

mapfile -t lines < input
for (( i=0; i < ${#lines[@]}; i+=4 ))
do printf "%s\n" "${lines[$i]}"
done

mapfileはBash 4で追加された組み込みコマンドで、標準入力を配列(ここでは)に読み取りlines、エントリごとに1行ずつ入力します。この-tオプションは、最後の改行を取り除きます。

4行目から4行ごとに印刷する場合は、mapfileコールバックオプションを使用して1つのコマンドで印刷できます-C。これにより、指定された間隔で、提供されたコードが非常に多くの行ごとに実行され-cます。現在の配列インデックスと割り当てられる次の行は、引数としてコードに渡されます。

mapfile -t -c4 -C 'printf "%.0s%s\n"' < input

これはprintf組み込みを使用します。書式コード%.0sは最初の引数(インデックス)を抑制するため、行のみが出力されます。

同じコマンドを使用して、1、2、または3行目から開始して4行ごとに印刷できますが、inputにフィードする前に3、2、または1行を先頭にmapfile追加する必要があります。 。

これも機能します:

mapfile -t lines < input
printf "%s%.0s%.0s%.0s\n" "${lines[@]}"

ここでprintflines、一度に配列の4つのエントリを使用し、最初のエントリのみを出力し、他の3 つのエントリはでスキップし%.0sます。手動でさまざまな間隔または開始点のフォーマット文字列をいじる必要があるので、私はこれが好きではありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.