Unixのテキストファイルから所定の範囲の行を抽出するにはどうすればよいですか?


531

複数のデータベースに相当するデータを含む、最大23000行のSQLダンプがあります。このファイルの特定のセクション(つまり、単一のデータベースのデータ)を抽出して、新しいファイルに配置する必要があります。必要なデータの開始行番号と終了行番号の両方を知っています。

例えば、16224と16482の間のファイルからすべての行を抽出し、それらを新しいファイルにリダイレクトするUnixコマンド(または一連のコマンド)を知っている人はいますか?


あなたが大きなファイルを言及しているので、私はコメントをチェックすることをお勧めstackoverflow.com/questions/83329/...
sancho.s ReinstateMonicaCellio

回答:


792
sed -n '16224,16482p;16483q' filename > newfile

sedマニュアルから:

p-パターンスペースを(標準出力に)印刷します。このコマンドは通常、-nコマンドラインオプションと組み合わせてのみ使用されます。

n-自動印刷が無効になっていない場合は、パターンスペースを印刷してから、パターンスペースを次の入力行で置き換えます。入力がない場合、sedはコマンドを処理せずに終了します。

q-sedこれ以上コマンドや入力を処理せずに終了します。-nオプションで自動印刷が無効になっていない場合は、現在のパターンスペースが印刷されることに注意してください。

そして

sedスクリプトのアドレスは、次のいずれかの形式にすることができます。

number 行番号を指定すると、入力のその行だけに一致します。

アドレス範囲は、2つのアドレスをコンマ(、)で区切って指定できます。アドレス範囲は、最初のアドレスが一致するところから始まり、2番目のアドレスが(包括的に)一致するまで続きます。


3
これが元のファイルを変更するかどうか私は気になりました。念のためバックアップしましたが、予想どおり、元のファイルは変更されなかったようです。
アンディグロフ

@AndyGroff。ファイルをインプレースで変更するには、「-i」パラメーターを使用します。それ以外の場合、ファイルは変更されません。
youri 2013年

175
私のように、非常に大きなファイルでこれを行う必要がある場合は、次の行にquitコマンドを追加すると役立ちます。それからsed -n '16224,16482p;16483q' filenameです。それ以外の場合、sedは最後までスキャンを続けます(または少なくとも私のバージョンではそうです)。
wds 2013

7
@MilesRoutの人々は、「なぜ反対票なのか」と尋ねるようです。多くの場合、おそらく「誰も気にしない」の代わりに「私は気にしない」を意味します
マーク

1
@wds-あなたのコメントは、トップに上がる答えに値します。それは昼と夜の違いを生むことができます。
sancho.s ReinstateMonicaCellio

203
sed -n '16224,16482 p' orig-data-file > new-file

16224,16482は、開始行番号と終了行番号を含みます。これは1インデックスです。 -n入力が出力としてエコーされないようにします。これは明らかに望ましくありません。番号は、次のコマンドを操作するための行の範囲を示します。コマンドpは関連する行を出力します。


7
大きなファイルでは、上記のコマンドは、目的の範囲が見つかった後もファイル全体をウォークし続けます。範囲が出力されたら、sedがファイルの処理を停止する方法はありますか?
ゲイリー

39
まあ、ここの答えから範囲の終わりで停止することは次のようにして達成できるようsed -n '16224,16482p;16482q' orig-data-file > new-fileです:
ゲイリー

5
なぜあなたは不必要なスペースに入れ、そして引用しなければならないのでしょうか?(もちろん、不必要な問題を作成してそれらを解決することは、コンピューターサイエンスの半分の本質ですが、その理由と
Kaz

92

ヘッド/テールを使用して非常に簡単:

head -16482 in.sql | tail -258 > out.sql

sedの使用:

sed -n '16482,16482p' in.sql > out.sql

awkを使用:

awk 'NR>=10&&NR<=20' in.sql > out.sql

1
2番目と3番目のオプションは問題ありませんが、最初のオプションは2つのコマンドを使用するため、1つで十分なので、多くの選択肢よりも低速です。また、に正しい引数を取得するための計算も必要tailです。
Jonathan Leffler、2015年

3
質問と同じ行番号を維持するには、sedコマンドsed -n 16224,16482p' in.sql >out.sqlとawkコマンドを同じにする必要がありますawk 'NR>=16224&&NR<=16482' in.sql > out.sql
sibaz

3
また、最初の例の場合head -16482 in.sql | tail -$((16482-16224)) >out.sql、計算をbashに任せることも知っておく価値があります
。– sibaz

1
最初のファイルは、q-optionが追加されていても、sedバージョンよりも大きなファイルでWAYYYY高速です。ヘッドバージョンインスタントとsedバージョン1分後にCtrl-C ...ありがとう
宮城

2
tail -n +16224計算の削減にも使用できます
SOFe

35

'vi'を使用してから、次のコマンドを使用できます。

:16224,16482w!/tmp/some-file

または:

cat file | head -n 16482 | tail -n 258

編集:-説明を追加するために、最初の16482行を表示するにはhead -n 16482を使用し、最初の出力から最後の258行を取得するにはtail -n 258を使用します。


2
そして、viの代わりに、exを使用することができます。つまり、viからインタラクティブなコンソールを除いたものです。
Tadeusz A.Kadłubowski10年

1
catコマンドは必要ありません。headファイルを直接読み取ることができます。これは、1つで十分な2つのコマンド(図のように3つ)を使用するため、多くの代替手段よりも低速です。
Jonathan Leffler、2015年

1
@JonathanLefflerあなたはかなり間違っています。非常に高速です。500k行の2Gファイルから、数秒で(約なしでcat)200k行、約1Gを抽出します。他の解決策は少なくとも数分を必要とします。また、GNUでの最速のバリエーションはそうtail -n +XXX filename | head XXXです。
Antonis Christofides、2016

28

には別のアプローチがありますawk

awk 'NR==16224, NR==16482' file

ファイルが大きい場合はexit、最後に必要な行を読み取った後で問題がなくなる可能性があります。これにより、次の行を不必要に読み取ることがなくなります。

awk 'NR==16224, NR==16482-1; NR==16482 {print; exit}' file

awk 'NR==16224, NR==16482; NR==16482 {exit}' file

2
1+を使用してランタイムとリソースを節約しますprint; exit。よろしくお願いします!
バーニーライター

2番目の例のわずかな簡略化:awk 'NR==16224, NR==16482; NR==16482 {exit}' file
ロビンA.ミード

@ RobinA.Meade、ありがとうございます。あなたのアイデアを投稿で編集しました
fedorqui 'SO stop harming'


9
 # print section of file based on line numbers
 sed -n '16224 ,16482p'               # method 1
 sed '16224,16482!d'                 # method 2

6
cat dump.txt | head -16224 | tail -258

トリックを行う必要があります。このアプローチの欠点は、末尾の引数を決定し、「間」に終了行を含めるかどうかを計算するために計算を行う必要があることです。


4
catコマンドは必要ありません。headファイルを直接読み取ることができます。これは、1つで十分な2つのコマンド(図のように3つ)を使用するため、多くの代替手段よりも低速です。
Jonathan Leffler、2015年

@JonathanLefflerこの答えは、読みやすく、覚えやすいです。あなたが本当にパフォーマンスを気にしていたら、そもそもシェルを使っていなかっただろう。特定のツールを特定のタスク専用にすることをお勧めします。さらに、「算術」はを使用して解決できます| tail -$((16482 - 16224))
イエティ

6

boxxarの肩の上に立って、私はこれが好きです。

sed -n '<first line>,$p;<last line>q' input

例えば

sed -n '16224,$p;16482q' input

$手段「最後の行」、最初のコマンドが行うようsed行で始まるすべての行に印刷16224し、2番目のコマンドを作るには、sed終了後にラインを印刷します16428。(追加1のためqboxxarの溶液中の-rangeが必要ではないようです。)

終了行番号を2回指定する必要がないため、このバリアントが好きです。また、使用$してもパフォーマンスに悪影響が及ばないことを測定しました。



3

素早く汚い:

head -16428 < file.in | tail -259 > file.out

おそらくそれを行うための最良の方法ではありませんが、うまくいくはずです。

ところで:259 = 16482-16224 + 1。


これは、1つで十分な場合に2つのコマンドを使用するため、多くの代替手段よりも低速です。
Jonathan Leffler、2015年

3

私はスプリッターと呼ばれるHaskellプログラムを書きました。これはまさにこれを実行します私のリリースブログの投稿を読んでください

次のようにプログラムを使用できます。

$ cat somefile | splitter 16224-16482

そして、それがすべてです。それをインストールするにはHaskellが必要です。ただ:

$ cabal install splitter

これで完了です。このプログラムがお役に立てば幸いです。


splitter標準入力から読み取り専用?ある意味、それは重要ではありません。catコマンドは、それがないか持たないか不必要です。splitter 16224-16482 < somefileまたは(ファイル名引数を取る場合)を使用しますsplitter 16224-16482 somefile
Jonathan Leffler、2015年

3

これをコマンドラインで確認することもできます:

cat filename|sed 'n1,n2!d' > abc.txt

例えば:

cat foo.pl|sed '100,200!d' > abc.txt

6
catこれらのどちらでもコマンドは必要ありません。sedはそれ自体でファイルを完全に読み取ることができます。または、ファイルから標準入力をリダイレクトすることもできます。
Jonathan Leffler、2015年


2

ヘッド/テールトリックを投稿しようとしていましたが、実際にはおそらくemacsを起動するだけです。;-)

  1. esc- x後藤ラインret16224
  2. マーク(ctrl- space
  3. esc- x後藤ラインret16482
  4. esc-w

新しい出力ファイルを開く、ctl-y save

何が起こっているのか見てみましょう。


4
私の経験では、Emacsは非常に大きなファイルではうまく機能しません。
Greg Mattes、2011

スクリプト化されたアクションとして実行できますか、それともインタラクティブなオプションのみですか?
Jonathan Leffler、2015年

2

私は使うだろう:

awk 'FNR >= 16224 && FNR <= 16482' my_file > extracted.txt

FNRには、ファイルから読み取られる行のレコード(行)番号が含まれています。


2

変数を使用してスクリプトから同じことをしたいと思い、$ variableを引用符で囲んで変数名とpを区切ることでそれを実現しました。

sed -n "$first","$count"p imagelist.txt >"$imageblock"

リストを別々のフォルダーに分割したかったのですが、最初の質問と回答が有用なステップであることがわかりました。(私はコードを移植する必要がある古いosのオプションではない分割コマンド)。


1

PATHを更新してディレクトリを含める(またはPATHに既に含まれているディレクトリに配置する)ことができる限り、コマンドラインから実行できる小さなbashスクリプトを作成しました。

使用法:$ pinch filename start-line end-line

#!/bin/bash
# Display line number ranges of a file to the terminal.
# Usage: $ pinch filename start-line end-line
# By Evan J. Coon

FILENAME=$1
START=$2
END=$3

ERROR="[PINCH ERROR]"

# Check that the number of arguments is 3
if [ $# -lt 3 ]; then
    echo "$ERROR Need three arguments: Filename Start-line End-line"
    exit 1
fi

# Check that the file exists.
if [ ! -f "$FILENAME" ]; then
    echo -e "$ERROR File does not exist. \n\t$FILENAME"
    exit 1
fi

# Check that start-line is not greater than end-line
if [ "$START" -gt "$END" ]; then
    echo -e "$ERROR Start line is greater than End line."
    exit 1
fi

# Check that start-line is positive.
if [ "$START" -lt 0 ]; then
    echo -e "$ERROR Start line is less than 0."
    exit 1
fi

# Check that end-line is positive.
if [ "$END" -lt 0 ]; then
    echo -e "$ERROR End line is less than 0."
    exit 1
fi

NUMOFLINES=$(wc -l < "$FILENAME")

# Check that end-line is not greater than the number of lines in the file.
if [ "$END" -gt "$NUMOFLINES" ]; then
    echo -e "$ERROR End line is greater than number of lines in file."
    exit 1
fi

# The distance from the end of the file to end-line
ENDDIFF=$(( NUMOFLINES - END ))

# For larger files, this will run more quickly. If the distance from the
# end of the file to the end-line is less than the distance from the
# start of the file to the start-line, then start pinching from the
# bottom as opposed to the top.
if [ "$START" -lt "$ENDDIFF" ]; then
    < "$FILENAME" head -n $END | tail -n +$START
else
    < "$FILENAME" tail -n +$START | head -n $(( END-START+1 ))
fi

# Success
exit 0

1
これは、1つで十分な場合に2つのコマンドを使用するため、多くの代替手段よりも低速です。実際、このwcコマンドはファイルを2回読み取るため、特にギガバイトのファイルでは、ディスクの帯域幅が浪費されます。あらゆる方法で、これは十分に文書化されていますが、エンジニアリングのやり過ぎでもあります。
Jonathan Leffler、2015年

1

これはあなたのために働くかもしれません(GNU sed):

sed -ne '16224,16482w newfile' -e '16482q' file

またはbashを利用する:

sed -n $'16224,16482w newfile\n16482q' file

1

edの使用:

ed -s infile <<<'16224,16482p'

-s診断出力を抑制します。実際のコマンドはhere-stringにあります。具体的に16224,16482pは、p目的の行アドレス範囲で(印刷)コマンドを実行します。


0

acceptの-nは機能します。傾向がある場合の別の方法を次に示します。

cat $filename | sed "${linenum}p;d";

これは次のことを行います。

  1. ファイルの内容をパイプで送ります(または、必要に応じてテキストをフィードします)。
  2. sedは指定された行を選択して出力します
  3. 行を削除するにはdが必要です。それ以外の場合、sedはすべての行が最終的に印刷されると想定します。つまり、dを使用しない場合、$ {linenum} pの部分を印刷するように要求するため、選択した行によってすべての行が2回印刷されます。-nが基本的にここのdと同じことをしていると確信しています。

3
メモcat file | sedは次のように書くとより良いsed file
fedorqui 'SO stop harming'

また、これは行を出力するだけですが、問題はそれらの範囲についてです。
fedorqui 'SO stop harming'

0

ここでは、テキストファイルからテキストの行を抽出することについて説明しているので、特定のパターンに一致するすべての行を抽出する特別な場合について説明します。

myfile content:
=====================
line1 not needed
line2 also discarded
[Data]
first data line
second data line
=====================
sed -n '/Data/,$p' myfile

[Data]行と残りを印刷します。line1からパターンへのテキストが必要な場合は、sed -n '1、/ Data / p' myfileと入力します。さらに、2つのパターンがわかっている場合(テキスト内で一意である方が良い)、範囲の開始行と終了行の両方を一致で指定できます。

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