7

単語の最初と最後の出現の間の行を見つけるためのunixコマンドが欲しい

例えば:

1000行あるとしましょう。10 行目には「stackoverflow」という単語が含まれており、35 行目には「stackoverflow」という単語が含まれています。

10 から 35 までの行を印刷して、新しいファイルに書き込みたいと考えています。

4

4 に答える 4

9

2ステップで作れます。基本的な考え方は次のとおりです。

1) 最初と最後の一致の行番号を取得します。

2) これらの範囲の間の行の範囲を出力します。

$ read first last <<< $(grep -n stackoverflow your_file | awk -F: 'NR==1 {printf "%d ", $1}; END{print $1}')
$ awk -v f=$first -v l=$last 'NR>=f && NR<=l' your_file

説明

  • read first lastは 2 つの値を読み取り、 と に格納し$firstます$last
  • grep -n stackoverflow your_filegrep を実行すると、次のような出力が表示されます。number_of_line:output
  • awk -F: 'NR==1 {printf "%d ", $1}; END{print $1}')ファイル内の の最初と最後の一致の行数をstackoverflow表示します。

  • awk -v f=$first -v l=$last 'NR>=f && NR<=l' your_file$first行番号から行番号までのすべての行を出力します$last

テスト

$ cat a
here we
have some text
stackoverflow

and other things
bla
bla
bla bla
stackoverflow
and whatever else
stackoverflow
to make more fun
blablabla

$ read first last <<< $(grep -n stackoverflow a | awk -F: 'NR==1 {printf "%d ", $1}; END{print $1}')
$ awk -v f=$first -v l=$last 'NR>=f && NR<=l' a
stackoverflow

and other things
bla
bla
bla bla
stackoverflow
and whatever else
stackoverflow

手順:

$ grep -n stackoverflow a
3:stackoverflow
9:stackoverflow
11:stackoverflow

$ grep -n stackoverflow a | awk -F: 'NR==1 {printf "%d ", $1}; END{print $1}'
3 11

$ read first last <<< $(grep -n stackoverflow a | awk -F: 'NR==1 {printf "%d ", $1}; END{print $1}')

$ echo "first=$first, last=$last"
first=3, last=11
于 2013-10-21T14:15:45.627 に答える
1

行数の上限 (たとえば 100 万行) がわかっている場合は、次の単純な悪用スクリプトを使用できます。

(grep -A 100000 stackoverflow | grep -B 1000000 stackoverflow) < file

| tail -n +2 | head -n -1境界線を削除するために追加することもできます:

(grep -A 100000 stackoverflow | grep -B 1000000 stackoverflow
  | tail -n +2 | head -n -1) < file
于 2013-10-21T13:59:15.323 に答える
1

出力に一致する最初と最後の行を含める必要があるかどうかという質問からは、100% 確信が持てないので、そうであると想定しています。しかし、代わりに排他的にしたい場合、これは簡単に変更できます。

この純粋な bash ソリューションは、すべてを 1 つのステップで実行します。つまり、ファイル (またはパイプ) は 1 回だけ読み取られます。

#!/bin/bash

function midgrep {
    while read ln; do
        [ "$saveline" ] && linea[$((i++))]=$ln
        if [[ $ln =~ $1 ]]; then
            if [ "$saveline" ]; then
                for ((j=0; j<i; j++)); do echo ${linea[$j]}; done
                i=0
            else
                saveline=1
                linea[$((i++))]=$ln
            fi
        fi
    done
}

midgrep "$1"

これをスクリプト (例: midgrep.sh) として保存し、次のように任意の出力をパイプします。

$ cat input.txt | ./midgrep.sh stackoverflow

これは次のように機能します。

  • 配列の最初の要素で最初に一致する行とバッファを見つける
  • 次の一致まで行を読み続け、進むにつれて配列にバッファリングします
  • 後続の各一致で、バッファ配列を出力にフラッシュします
  • ファイルを最後まで読み続けます。一致するものがない場合、最後のバッファは単純に破棄されます。

このアプローチの利点は、入力を 1 回だけ読み通すことです。欠点は、各試合の間にすべてをバッファリングすることです。各試合の間に多くの行がある場合、次の試合にヒットするまで、これらはすべてメモリにバッファリングされます。

また、これは bash=~正規表現演算子を使用して、この純粋な bash を維持します。ただし、より快適な場合は、代わりにこれを grep に置き換えることができます。

于 2013-10-21T16:39:58.443 に答える
0

使用:

perl -00 -lne '
    chomp(my @arr = split /stackoverflow/);
    print join "\nstackoverflow", @arr[1 .. $#arr -1 ]
' file.txt | tee newfile.txt

この背後にあるアイデアは、分割する「stackoverflow」文字列を使用して、入力ファイル全体の配列をチャンクにフィードすることです。次に、join "stackoverflow" を使用して、最後の -1 までの 2 番目のオカレンスを出力します。

于 2013-10-21T14:46:59.637 に答える