単語の最初と最後の出現の間の行を見つけるためのunixコマンドが欲しい
例えば:
1000行あるとしましょう。10 行目には「stackoverflow」という単語が含まれており、35 行目には「stackoverflow」という単語が含まれています。
10 から 35 までの行を印刷して、新しいファイルに書き込みたいと考えています。
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_file
grep を実行すると、次のような出力が表示されます。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
行数の上限 (たとえば 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
出力に一致する最初と最後の行を含める必要があるかどうかという質問からは、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 に置き換えることができます。
パールの使用:
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 番目のオカレンスを出力します。