私のファイルには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
役に立ちません。-z
GNUのオプションのように、バッファ全体を一度に処理することをお勧めします(ポータブルな代替方法を見るにはsed
linuxと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 +スクリプトの上部の次の行に進みます)apple10
apple12
p
d
{ ... }
いいえ式を一致させます。
このs
コマンド(代替)は、入力の最初の行に対して実行されます。するapple1
行の先頭で一致します。行末の数値文字列をsに置き換えます4
。
loop
次に、現在の行を印刷し(do print and read)を使用して、次の行を読み取って変更されていない残りのデータを渡す役割を果たすマークされた部分があります。 「現在の行」は、ループを最初に通過するコマンドによって変更されます。n
n
s
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