行番号で指定された非隣接行の変更

行番号で指定された非隣接行の変更

行番号を事前に知って別のファイルに保存します。

cat linenos
2
15
42
44
... etc

ご覧のとおり、行が隣接していないため、rangeは使用できませんsed。目標は、ターゲットファイルの行を変更することです。たとえば、ターゲットファイル行の前にMARKERなどのマーカーを追加します。

sed簡単なアプローチは、各行を変更するために複数回呼び出すことです。

for l in $(cat linenos)
do 
  sed -i "${l}s/^/MARKER/" target_file
done

これは明らかにsedを何度も呼び出すでしょう。

警告する:*このアプローチは非効率的であるだけでなく、そのようなマークアップを挿入しない修正がある場合にも問題が発生する可能性があります。 sedコマンド(darなど)で行を削除または挿入すると、ループ内の次のsed実行のためにlinenosの最初の行番号が無効になります。

改善/最適化をどのように提案しますか?

例 linenos ファイル

cat linenos
2
5

サンプルオブジェクトファイル

cat target_file
line one
line two
line three
line four
line five
line six

修正されたtarget_fileの期待される結果

cat target_file
line one
MARKERline two
line three
line four
MARKERline five
line six

私が考えた可能な方法は、sedシーンを動的に生成することです。

SEDCMD=$(for l in $(cat linenos); do echo -n "${l}s/^/MARK/;" ; done)

sed -i -e "$SEDCMD" targetfile

@スチールドライバ次のような方法はアイデアと同じですが、よりエレガントで簡潔です。

答え1

sed自体(または他の選択したテキスト処理ユーティリティ)を使用して行番号をsed式に変換し、スイッチを使用して-fsedに渡すことができます。

前任者。

sed 's:$:s/^/MARKER/:' linenos | sed -f- -i target_file

これは少なくともsedのみを呼び出します。二重

答え2

perl(GNUソースsed)を使う-i

perl -pi -e '
  BEGIN{$l{0+$_}=1 while <STDIN>}
  $_ = "MARKER$_" if $l{$.}' target_file < linenos

perl私たちはstdinに行番号のリストを提供します。これはBEGIN塊として読み込まれます。

各入力行にを使用して、行を数値に変換します0+$_。これにより、改行文字が消え、数字が正規化されます(1e0、1、01がすべて1になります)。

ハッシュテーブルは各行番号の値をキーで%l埋めます。1

target_file現在行番号()がゼロ以外の値である行の前に追加されるメインループ-pで処理されます。MARKERS$.%l

答え3

$ awk 'NR==FNR{a[$1]="MARKER"; next} {print a[FNR] $0}' linenos target_file
line one
MARKERline two
line three
line four
MARKERline five
line six

または少しメモリを節約します。

$ awk 'NR==FNR{a[$1]; next} {print (FNR in a ? "MARKER" : "") $0}' linenos target_file
line one
MARKERline two
line three
line four
MARKERline five
line six

「内部」編集(perlおよびGNU sedと同じ-i)が必要な場合は、GNU awkを使用してawk '...'文の前にaを追加してawk -i inplace '...'ファイルを空にしないようにしてください。 IMHO awk(または他のUNIXツール)を使用してこれを行う方が簡単です。print;nextlinenos

awk 'script' linenos target_file > tmp && mv tmp target_file

答え4

別のアプローチは、適切な場所で修正するed代わりに使用されます。sedtarget_file

(while IFS= read n; do echo "${n}s/^/MARKER/"; done < linenos; echo w) |  ed -s target_file

関連情報