巨大なファイルの先頭と末尾に行を追加します


23

巨大なファイルの最初と最後に行を追加するシナリオがあります。

以下に示すように試しました。

  • 最初の行の場合:

    sed -i '1i\'"$FirstLine" $Filename
  • 最後の行:

    sed -i '$ a\'"$Lastline" $Filename  

しかし、このコマンドの問題は、ファイルの最初の行を追加し、ファイル全体を走査することです。最後の行については、再びファイル全体を走査し、最後の行を追加します。非常に大きなファイル(14GB)であるため、これには非常に長い時間がかかります。

ファイルを1回だけ読み取りながら、ファイルの先頭と末尾に行を追加するにはどうすればよいですか?

回答:


20

sed -i実装の詳細として一時ファイルを使用します。これはあなたが経験していることです。ただし、既存のコンテンツを上書きせずにデータストリームの先頭にデータを追加するには、ファイルを書き換える必要がありますsed -i。これを回避する場合でも、回避する方法はありません。

ファイルの書き換えがオプションではない場合は、読み取り時に操作することを検討できます。次に例を示します。

{ echo some prepended text ; cat file ; } | command

また、sedはストリームの編集用です。ファイルはストリームではありません。edやexなど、この目的のためのプログラムを使用します。-ised のオプションは移植性が高いだけでなく、ファイルへのシンボリックリンクを破壊します。これは、基本的に削除して再作成するためです。これは無意味です。

これは、次のedような単一のコマンドで実行できます。

ed -s file << 'EOF'
0a
prepend these lines
to the beginning
.
$a
append these lines
to the end
.
w
EOF

edの実装によっては、ページングファイルを使用する場合があり、少なくともその容量を使用できる必要があることに注意してください。


こんにちは、提供されたedコマンドは、巨大なファイルに対して非常にうまく機能しています。しかし、Test、Test1、Test 2のような3つの巨大なファイルがあります。ed-s Tes * << 'EOF' 0aのようなコマンドをこれらの行の先頭に追加しました。$ aは、これらの行を末尾に追加します。w EOFただし、テストファイルのみを取得し、最初/最後の行を追加します。すべてのファイルの最初と最後の行を追加する必要があるように、同じコマンドで変更を行うにはどうすればよいですか。
UNIXbest

@UNIXbest- forループの使用:for file in Tes*; do [command]; done
クリスダウン

こんにちは、Tes *のファイルに以下のコマンドを使用しました。do ed -s Tes * << 'EOF' 0a HEllO HDR。$ a Hello TLR。w EOF完了しかし、まだ最初のファイルに書き込み中。
UNIXbest

正しいのは、の引数としてでは"$file"なくを使用する必要があるためです。Tes*ed
クリスダウン

2
@UNIXbestこの回答で問題が解決した場合は、受け入れることを検討する必要があります。
ジョセフR.

9

ファイルのコピー全体をディスクに割り当てることを避けたい場合は、次のようにすることができます。

sed '
1i\
begin
$a\
end' < file 1<> file

これは、stdin / stdoutがファイルの場合、sed ブロックごとに読み書きするという事実を利用しています。したがって、ここでは、追加する最初の行がsedブロックサイズ(4kまたは8kのようなもの)よりも小さい限り、読み込んでいるファイルを上書きしてもかまいません。

ただし、何らかの理由でsed失敗した場合(強制終了、マシンクラッシュなど)、ファイルの半分が処理されることになり、最初の行のサイズが途中で失われることになります。

また、your sedがGNU でない限り、sedバイナリデータでは機能しません(ただし、を使用-iしているため、GNU sedを使用しています)。


Ubuntu 16.04でのこのエラー
Csaba Toth

4

いくつかの選択肢があります(いずれもファイルの新しいコピーを作成するため、十分なスペースがあることを確認してください)。

  • 単純なエコー/猫

    echo "first" > new_file; cat $File >> new_file; \
      echo "last" >> new_file; 
  • awk / gawkなど

    gawk 'BEGIN{print "first\n"}{print}END{print "last\n"}' $File > NewFile 

    awkそしてそのilkはファイルを1行ずつ読み込みます。BEGIN{}ブロックは、最初の行と前に実行されたEND{}最後の行の後にブロック。したがって、上記のコマンドはを意味しprint "first" at the beginning, then print every line in the file and print "last" at the endます。

  • Perl

    perl -ne 'BEGIN{print "first\n"} print;END{print "last\n"}' $File > NewFile

    これは、上記のPerlで記述されたgawkと本質的に同じです。


1
これらのすべてのケースで、新しいファイル用に少なくとも14GBの追加スペースが必要になることに注意してください。
クリスダウン

@ChrisDown良い点は、それを明確にするために答えを編集しました。sed -i一時ファイルを作成するOPが使用されているため、これは問題ではないと想定しました。
テルドン

3

私ははるかにシンプルなものを好む:

gsed -i '1s/^/foo\n/gm; $s/$/\nbar/gm' filename.txt

これにより、ファイルが変換されます。

asdf
qwer

ファイルに:

foo
asdf
qwer
bar

2

ExモードでVimを使用できます。

ex -sc '1i|ALFA' -c '$a|BRAVO' -cx file
  1. 1 最初の行を選択

  2. i テキストと改行を挿入する

  3. $ 最終行を選択

  4. a テキストと改行を追加する

  5. x 保存して閉じます


これを複数のファイルに実行したい場合はどうしますか?
geoyws

1
この質問のためのスコープ内に実際にはない@geoyws
スティーヴン・ペニー

これは%aではなく$ aですか?
カルロスロブルズ

2

ファイルの先頭にデータを挿入する方法はありません¹。できることは、新しいファイルを作成し、追加のデータを書き込み、古いデータを追加することだけです。したがって、最初の行を挿入するには、ファイル全体を少なくとも1回書き換える必要があります。ただし、ファイルを書き換えずに最後の行を追加できます。

sed -i '1i\'"$FirstLine" $Filename
echo "$LastLine" >>$Filename

または、1回のsedで2つのコマンドを組み合わせることができます。

sed -i -e '1i\'"$FirstLine" -e '$ a\'"$Lastline" $Filename

sed -i新しい出力ファイルを作成し、それを古いファイルの上に移動します。これは、sedが機能している間、スペースを消費しているファイルの2番目のコピーがあることを意味します。これを回避するには、ファイルを所定の場所に上書きしますが、大きな制限があります:追加する行はsedのバッファーよりも小さくする必要があり、システムがクラッシュした場合、破損したファイルと一部のコンテンツが失われます真ん中なので、それに対して強くお勧めします。

¹Linuxにはデータをファイルに挿入する方法がありますが、ファイルシステムブロックの整数個しか挿入できず、任意の長さの文字列を挿入できません。データベースや仮想マシンなどの一部のアプリケーションには役立ちますが、テキストファイルには役に立ちません。


違います。見fallocate()FALLOC_FL_INSERT_RANGE近代的なカーネルでXFSで利用可能とext4の(4.XX)man7.org/linux/man-pages/man2/fallocate.2.html
エリック・

@Ericブロック全体のみを挿入できますが、任意のバイト長ではなく、少なくともext4を使用したLinux 4.15.0の場合は挿入できます。任意のバイト長を挿入できるファイルシステムはありますか?
ジル「SO-悪

正しいが、それでもあなたの声明を正しくしない。「ファイルの先頭にデータを挿入する方法はありません」と書きました。それはまだ真実ではありません。ファイルの先頭にエクステントを挿入するメカニズムがあります。確かに警告が付いていますが、一部のユーザーはスペースやキャリッジリターンで埋めることによってブロックサイズの制限を気にしないかもしれないので、言及する価値があります。
エリック

0
$ (echo "Some Text" ; cat file1) > file2

4
コードだけの答えは受け入れられない、あなたの答え改善してください
Networkerの

回答を拡張して、提案の説明、またはソリューションをサポートするドキュメントへのリンクを含めることを検討してください。
HalosGhost 14

-1

最新のLinuxカーネル(4.1または4.2以降)は、ext4およびxfsファイルシステムでのfallocate()システムコールによるファイルの先頭へのデータの挿入をサポートしFALLOC_FL_INSERT_RANGEています。本質的に、これは論理シフト操作です。データは、より高いオフセットで論理的に再配置されます。

ファイルの先頭に挿入する範囲の粒度に関する制約が存在します。ただし、テキストファイルの場合、おそらく必要以上に(粒度の境界まで)割り当てて、スペースまたはキャリッジリターンで埋めることができますが、それはアプリケーションによって異なります

ファイルエクステントを操作する容易に利用できるLinuxユーティリティは知りませんが、書くのは難しくありませんfallocate()。ファイル記述子を取得し、適切な引数で呼び出します。詳細については、fallocateシステムコールのmanページを参照してください:http : //man7.org/linux/man-pages/man2/fallocate.2.html


ユーティリティーは問題ではありません(非組み込みLinuxを想定):util-linuxにはfallocateユーティリティーが含まれています。問題は、ブロック全体の粒度がほとんどのテキストファイルでこれを役に立たないことです。別の問題は、範囲の割り当てとその後の変更がアトミックではないことです。したがって、実際にはここで問題を解決することはできません。
ジル「SO-悪であるのをやめる」

粒度は既に言及した警告であり、いいえ、それは役に立たないわけではなく、アプリケーションに依存します。原子性が重要であるという質問でどこを見ましたか?私はパフォーマンスの問題しか見ることができません。それでも、このsyscallはアトミックであるようです:elixir.bootlin.com/linux/latest/source/fs/open.c#L228そしてアトミック性が重要になった場合(それは重要ではありませんが、それは引数のためであると言います)ファイルロックを使用するだけです。(fallocate原子性が壊れているカーネルコードの場所を教えてください、私は興味があります)
Eric
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.