web-dev-qa-db-ja.com

行単位でファイルを切り捨てる方法は?

ファイルが多数あり、そのうちのいくつかは非常に長いです。ファイルの末尾を削除してサイズが大きい場合は、特定のサイズに切り詰めたいと思います。しかし、私は行全体を削除したいだけです。これどうやってするの? Linuxツールチェーンで処理されるようなもののように感じますが、適切なコマンドがわかりません。

たとえば、300バイトの行がある120,000バイトのファイルがあり、それを10,000バイトに切り詰めようとしているとします。最初の33行はそのまま(9900バイト)、残りは切り捨てます。行を部分的に残すため、正確に10,000バイトでカットしたくありません。

もちろん、ファイルの長さは異なり、行はすべて同じ長さではありません。

理想的には、結果のファイルは少し長くなるのではなく少し短くなります(ブレークポイントが長い行にある場合)が、それほど重要ではなく、それが簡単であれば少し長くなる可能性があります。変更をファイルに直接適用したいと思います(おそらく、新しいファイルが別の場所にコピーされ、元のファイルが削除され、新しいファイルが移動されますが、ユーザーのPOVから同じです)。データを一連の場所にリダイレクトし、その後ファイルを破損する可能性を招く解決策は、それを回避したいのですが...

13
Charles

sedを使用すると、以前の回答でwc/awkの複雑さを回避できます。 OPから提供された例を使用して(complete 10000バイトより前の行を表示):

_awk '{i += (length() + 1); if (i <= 10000) print $ALL}' myfile.txt
_

そのバイトが行の終わりにない場合、10000番目のバイトを含む完全な行も表示します。

_awk '{i += (length() + 1); print $ALL; if (i >= 10000) exit}' myfile.txt
_

上記の答えは以下を前提としています:

  1. テキストファイルはUnixの行末記号(_\n_)です。 Dos/Windowsテキストファイル(_\r\n_)の場合、length() + 1length() + 2に変更します
  2. テキストファイルには1バイト文字しか含まれていません。マルチバイト文字(ユニコード環境など)がある場合は、環境_LC_CTYPE=C_を設定して、バイトレベルで解釈を強制します。
1
Abel Cheung

sedアプローチは問題ありませんが、すべての行をループすることはできません。保持する行数がわかっている場合(例として、ここでは99を使用しています)、次のように実行できます。

_sed -i '100,$ d' myfile.txt
_

説明:sedは正規表現プロセッサーです。オプション_-i_を指定すると、ファイルを読み取って標準出力に書き込むだけでなく、直接( "インライン")でファイルを処理します。 _100,$_は単に「100行目からファイルの終わりまで」を意味し、その後にコマンドdが続きます。これはおそらく「削除」を表すために正しく推測したものです。つまり、コマンドは、「myfile.txtから100行目からファイルの終わりまでのすべての行を削除する」という意味です。 99行を保持したいので、100が最初に削除される行です。

編集:一方、保持したいログファイルがある場合は、たとえばlast100行:

_[ $(wc -l myfile.txt) -gt 100 ] && sed -i "1,$(($(wc -l myfile.txt|awk '{print $1}') - 100)) d" myfile.txt
_

ここで何が起こっているのですか?

  • [ $(wc -l myfile.txt) -gt 100 ]:ファイルに100行を超える場合にのみ、次の操作を行います
  • $((100 - $(wc -l myfile.txt|awk '{print $1}'))):削除する行数を計算します(つまり、保持する(最後の)100を除くファイルのすべての行)
  • 1, $((..)) d:最初の行から計算された行までのすべての行を削除します

編集:質問は詳細を示すために編集されただけなので、この追加情報も回答に含めます。追加された事実は:

  • 特定のサイズは、ファイル(10,000バイト)に残ります。
  • 各行には特定のサイズのバイト(例では300バイト)があります。

これらのデータから、「/」として残る行数を計算できます。この例では33行になります。計算のシェル用語:$((size_to_remain / linesize))(少なくともBashを使用するLinuxでは、結果は整数です)。調整されたコマンドは次のようになります。

_# keep the start of the file (OPs question)
sed -i '34,$ d' myfile.txt
# keep the end of the file (my second example)
[ $(wc -l myfile.txt) -gt 33 ] && sed -i "1,33 d" myfile.txt
_

サイズは事前にわかっているので、sedコマンドに埋め込む計算は必要ありません。ただし、柔軟性を高めるために、一部のシェルスクリプトでは変数を使用できます。

ファイルサイズに基づく条件付き処理の場合、次の「test」構成を使用できます。

_[ "$(ls -lk $file | awk ' {print $5}')" -gt 100 ] &&
_

つまり、「_$file_のサイズが100kBを超える場合は、次のようにします...」(_ls -lk_は、位置5のファイルサイズをkBで示します。したがって、awkを使用してこれを正確に抽出します。 )。

15
Izzy

これを行うコマンドが見つからないので、簡単なスクリプトを作成しました(テストされていません)。

#!/bin/sh

# Usage: $0 glob.* 25000
# where glob.* is a wildcard pattern and 25000 is the maximum number of bytes.

limit=20000
tmp=/tmp/trim
[[ "$2" == +([0-9]) ]] || limit=$2
limit=`expr $len + 1`
for file in $1;
do
    [[ `wc -c $file` -lt $limit ]] && continue
    head -c $file > $tmp
    sed '$d' $tmp
    $tmp > $file
done
0
Charles