grep
特定の行を渡し、keyword
出力を既存のファイルの特定の行番号にリダイレクトしたいと思います。
注文する
grep "key" temp_file >> desired.txt
私が必要とするのは、ファイルの特定のgrepped
行番号に行を追加できることです。x
desired.txt
答え1
実行する手順を正しく設定すると簡単になります。最も重要なことは、過度に競合しないソースファイルのバッファを取得することです。唯一の実際の方法は、別のファイルを使用することです。シェルを使用すると、これは非常に簡単です。
{ head -n "$((num_lines_before_insert))"
grep key temp_file; sed \$d
} <<SOURCE_FILE >desired.txt
$( cat <source_file;echo .)
SOURCE_FILE
したがって、ほとんどのシェルの場合(bash
およびは含まれていますが、またははzsh
含まれていません)dash
yash
<<
here_documentを取得すると、シェルは固有の名前の一時ファイルを生成して、指定した入力ファイル記述子に配置します${TMPDIR:-/tmp}
。exec
(または基本的に0のみ)- そしてすぐに削除してください。コマンドへの入力として使用される場合名前のないファイル - ファイルシステムへのリンクが残っておらず、完全に消える前にカーネルがクリーンアップされるのを待ちます。これは正しいファイルです。データがディスクのどこかに存在します。(または可能であれば少なくともVFS内でtmpfs
)そして、カーネルは少なくともファイル記述子を解放するまで作業を続けます。
これは、シェルがheredocの実際のバックアップファイルを取得する限り、一時ファイルの要件を処理する非常に安全で簡単な方法を表します。なぜなら、そのファイルが完全に作成され、読み取り前にすべてのファイルシステム名がわかっているからです。それから。したがって、作業中にそのデータを変調することはできません。
上記のブロックは最初に-を使用して一時ファイルに書き込み、cat
コマンドの置き換えですべて/末尾の空白行を保持しますecho
。これはファイルの末尾に行を追加します。{
複合ステートメントでは、}
3つのコマンドの出力desired.txt
(そのうちの2つはソースファイルの尾から読み取られますhead
)と一致するエントリをgrep
挿入するコマンドが記録されますkey
。
これが必要かどうかはわかりません。しかし、このようなシーケンスを使用して、ソースファイルを簡単かつ安全に完全に上書きできることを示すことに関連していると思います。
あなたの殻ならいいえheredocsから実際のファイルをインポートすると、その機能をシミュレートできます。
{ set "$$" "${TMPDIR:-/tmp}" "$@"
exec <"$2/$( set -C
>"$2/$1" cat &&
echo "$1")" >&1
rm -- "$2/$1";shift 2
head "-n$((before))"
grep ... keyfile; cat
} <source_file 1<>source_file
...これにより、元に戻せない操作を実行する前に、すべてのファイルが書き込み可能であり、ファイル記述子に安全に割り当てられていることを確認し、すべてのファイルシステムもクリーンアップされます。今後同じことをしてください。
これを証明するために私が実行したテストは次のとおりです。
cd /tmp
set "$$" "${TMPDIR:-/tmp}" "$@"
seq 5000000 >test
printf line\ %s\\n 1 2 3 4 5 >test2
{ exec <"$2/$( set -C
>"$2/$1" cat &&
echo "$1")" >&1
rm -- "$2/$1";shift 2
head -n2500000
grep 3 test2;cat
} <test 1<>test
最初の2つのファイルが作成されました。 1 つは/tmp/test
500 万行番号と命名され、seq
2 番目は/tmp/test2
5 行と命名されました。たとえば...
line 1
line 2
line 3
line 4
line 5
次に、上記のブロックを実行してから...
sed -n '1p;$p;2499999,2500002l' <test
wc -l test
...興味深いことに、これは実際に挿入操作と同じ時間がかかり、次のように印刷されます。
1
2499999$
2500000$
line 3$
2500001$
5000000
5000001 test
仕組みは次のとおりです。
- リダイレクト
1<>
は重要です。標準出力にO_RDWRフラグを設定し、ファイルに書き込む各プロセスがファイルの前の内容を上書きすることを保証します。つまり、ソース/ターゲットファイルが任意の時点で切り捨てられるのではなく、最初から最後まで書き換えられることを意味します。 - コマンドの置き換えにより、
exec
有効な部分をできるだけ早く完了できます。(または私ができることがわかったら)。コマンド内でノクロボさてset
、インタラクティブシェルでは、"${TMPDIR:-/tmp}/$$"
拡張結果がすでに存在する場合、プロセス全体がすぐに停止しますexec <"${TMPDIR:-/tmp}/"
。または、スクリプトはシェルがexec
ディレクトリをstdinにインポートできないため、意味のあるエラーでスクリプトを終了させることもできます。 - コマンド内では、サブコピーはまだ存在しない一時ファイルにコピーされ、
cat
名前は標準出力に書き込まれます。source_file
echo
- すべてのファイルハンドルが新しい一時ファイルとして
exec
編集されると、rm
unlink()
現在残っている唯一の一時ステートメントは割り当てられたばかり<
のリダイレクトです。 head
250万行を見つけ、source_file
最初の250万行を作成します。要点は、両方のファイルで同じオフセットを見つけることです。- 新しく作成されたtmpファイルがtmpfsにあり、ソースファイルがディスクにある場合は、I / Oのこの部分がより効率的になる可能性があることに注意してください(ここでI / Oが反転して
head
ディスクファイルから読み取ってここに書き込む場合)。 ) 高いRAMのファイル。 - これを行うには、
exec <>"$(... head ... <&1 >&0
tmpファイルを読み書き可能にし、可能であれば末尾に行数を指定するためにhead
使用する必要があります。tail
この場合、数字が正確である必要はありません。リング同様に、過渡入力 - 一度に少しずつオフセットを進めます。シェルの組み込み関数をread
使用してEOFをテストしたり、wc
ループを開くために使用できます。 - これは、EOFが表示されないため、標準入力
cat
にかかる可能性があるためです。<>
- 新しく作成されたtmpファイルがtmpfsにあり、ソースファイルがディスクにある場合は、I / Oのこの部分がより効率的になる可能性があることに注意してください(ここでI / Oが反転して
grep
他のファイルから一部のデータを読み書きすると、source_file
他の場所から読み取ったのと同じバイト数だけが上書きされます。cat
grep
stdinの残りの部分をstdoutに書き込むことによって生じる可能性がある違いを修正してください1<>source_file
。
答え2
大容量ファイルには適していませんが、コマンド出力を読み取り、アドレス指定された行の後に挿入するed
ことができますr
。例:
ed -s desired.txt <<IN
4r !grep "key" temp_file
w
q
IN
または1行:
printf '%s\n' '4r !grep "key" temp_file' w q | ed -s desired.txt
異なる行番号に別のコマンドの出力を挿入できます。逆さまに働かなければならないed
行番号アドレスを繰り返す場合:
ed -s desired.txt <<IN
48r !grep "another_key" another_temp_file
4r !grep "key" temp_file
w
q
IN
答え3
非常に大きなファイルの場合、desired.txt
最適化が必要な場合があります。おそらくシェルレベルでよりエレガントに行うことができますが、慣れていないので、そこでtcsh
動作することを提案したいと思います。
sed -n '1,4p' desired.txt >file.tmp
grep "key" temp_file >>file.tmp
sed -n '5,$p' desired.txt >>file.tmp
mv file.tmp desired.txt
答え4
なぜ誰もが簡単なことで問題を起こそうとしているのか理解できませんか?
sed -i "4a$(grep "key" temp_file)" desired.txt
4
(必要な行番号に変更)
または(複数grep
行出力の場合)
grep "key" temp_file > grepped.tmp
sed -i '4r grepped.tmp' desired.txt