列1で最後に表示される文字列を見つけて、列3の対応する値に置き換えるにはどうすればよいですか?

列1で最後に表示される文字列を見つけて、列3の対応する値に置き換えるにはどうすればよいですか?

私のファイルには3つの列があります。

apple1        10109283      20012983
apple1        10983102      10293809
apple1        10293893      2349823049
apple10       109283019     109238901
apple10       192879234     234082034
apple10       234908443     3450983490

列1(この場合は行3または6)でこの文字列の最後の項目を見つけ、列3の対応する数字を別の数字に置き換えたいと思います。はい(行3、列3を444444444に置き換えます)

apple1        10109283      20012983
apple1        10983102      10293809
apple1        10293893      444444444
apple10       109283019     109238901
apple10       192879234     234082034
apple10       234908443     3450983490

これまでsedを試してみましたが、うまくいきません。

sed '$s/apple1*$/444444444/'

答え1

パイプのない純粋なsedソリューションtac

このような場合、行ごとのアプローチはsed役に立ちません。-zGNUのオプションのように、バッファ全体を一度に処理することをお勧めします(ポータブルな代替方法を見るにはsedlinuxとGNUを使用するようです)。sedこのQ&A)。

これで欲張りな性格を活用できます。他のすべての出現は食べられるので、.*パターンは.*apple1最後の出現を含むすべてと一致します。apple1.*

次に、次のフィールド(\s+列区切り文字、[0-9]+2番目の列、その他の列\s+、すべてのGNU拡張正規表現)を追加し、()それを置き換えて再利用できるようにします\1。次に、外部に3つの列を追加して置き換えると、()結果は次のようになります。

sed -zE 's/(.*\napple1\s+[0-9]+\s+)[0-9]+/\14444444/'

それはすべてです。

GNUを使用していないユーザーのsedための注意:ポータブルソリューションはあまり便利ではありません。

sed -E 'H;1h;$!d;x;s/(.*\napple1[[:space:]]+[0-9]+[[:space:]]+)[0-9]+/\14444444/'

答え2

tac file |
awk -v string='apple1' -v replace='444444444' '
    !flag && $1 == string { $3 = replace; flag = 1 }
                          { print }' |
tac

tacパイプラインは最初にGNU coreutilsを使用してデータの行順序を逆に置き換えます。最後の行は、最初の列が特定の文字列を含む場所なので、検索が簡単です。

このawkコマンドは単に最初の列を指定された文字列と比較し、まだ置換を実行していない場合(ゼロ以外の!flag場合)、最初の列で文字列を見つけると3番目の列を変更します。この操作を実行するときは、flag置き換えが行われないように1に設定してください。

プログラムの残りの部分はawk現在の行(変更された行を含む)のみを印刷します。

パイプラインの終わりに、我々は再びラインの順序を逆に変えますtac

質問のデータを考慮すると、出力は次のようになります。

apple1        10109283      20012983
apple1        10983102      10293809
apple1 10293893 444444444
apple10       109283019     109238901
apple10       192879234     234082034
apple10       234908443     3450983490

列3の変更により、変更された行の列は他の行の列とは若干異なります。もっと見やすくするために、column -tパイプラインの最後の追加ステップに結果を渡すことができます。これにより、出力は次のようになります。

apple1   10109283   20012983
apple1   10983102   10293809
apple1   10293893   444444444
apple10  109283019  109238901
apple10  192879234  234082034
apple10  234908443  3450983490

列の間に複数のスペースがあります。


の場合、sed文字列が最初の列に表示される最初の行の3番目の列を置き換えるのと同じくらい簡単ではありません(上記のパイプラインのようにデータ行を逆にすると仮定します)。私たちもしなければならないいいえ最初の列が文字列と一致していても、後続の行の3番目の列を置き換えます。

以下は、sedこれを正しく実行する編集スクリプトです。

/^apple1\>/ ! {
        p
        d
}

s/[[:digit:]]*$/444444444/

:loop
n
$ ! b loop

apple1最初の部分は、最初の列と一致しない入力の先頭に行を印刷することを担当します。式内\>の単語の終わりと一致するため、誤って一致または表示される可能性がある他の同様の文字列はapple1発生しません。入力の先頭の各行が実行され、(print)と(delete +スクリプトの上部の次の行に進みます)apple10apple12pd{ ... }いいえ式を一致させます。

このsコマンド(代替)は、入力の最初の行に対して実行されます。するapple1行の先頭で一致します。行末の数値文字列をsに置き換えます4

loop次に、現在の行を印刷し(do print and read)を使用して、次の行を読み取って変更されていない残りのデータを渡す役割を果たすマークされた部分があります。 「現在の行」は、ループを最初に通過するコマンドによって変更されます。nns

loop入力の最後の行に到達できなかった場合、最後の行はラベルに分岐します。

例を実行してください:

$ tac file | sed -f script.sed | tac
apple1        10109283      20012983
apple1        10983102      10293809
apple1        10293893      444444444
apple10       109283019     109238901
apple10       192879234     234082034
apple10       234908443     3450983490

答え3

次のコマンドを試してください。素晴らしい作品。

for i in `awk '{print $1}' file1| awk '{if(!seen[$1]++)print }'`; do j=`awk -v i="$i" '$1 == i {print $0}' file1| awk '{print NR}'| sed -n '$p'`; awk -v i="$i" '$1 == i {print $0}' file1|awk -v i="$i" -v j="$j" 'NR==j{$3="444444444"}1'; done

関連情報