web-dev-qa-db-ja.com

awkは、CSVファイルの特定の列の文字列を検索して置換します。

17列と100万行のcsvファイルがあります。 16番目の列で特定の文字列を検索し、その文字列のすべてのインスタンスを別の文字列に置き換えます。プログラムの残りの部分ではbashスクリプトを使用しているので、Python search&replaceの代わりにawkを使用することを考えました。現在のOSはRhel6です。

以下は、私のデータのサンプル出力です。

SUBSCRIBER_ID|ACCOUNT_CATEGORY|ACCOUNT_ACTIVATION_DATE|PACKAGE_NAME|PACKAGE_TYPE|DURATION|ACTIVE_DATE|INACTIVE_DATE|STB_NO|PRIMARY_SECONDARY|MODEL_TYPE|VC_NO|MULTIROOM|STB_TYPE|IPKG|SERVICE_STATE|CURRENT_STATUS
1001098068|ResidentialRegular|01/20/2007|Annual package 199 May17 pack|Basic Package|Annual|08/28/2017||027445053518|Primary|Pace - 31|000223871682|Yes|AMP|Package 199 pack|Market1|Active
1001098068|ResidentialRegular|01/20/2007|Annual Pack|Premium Package|Annual|08/28/2017||027445053518|Primary|Pace - 31|000223871682|Yes|AMP|English Movies pack|Market1|Active
1001098068|ResidentialRegular|01/20/2007|Annual SingleUnit Jun17 Pack|Secondary Pack|Annual|08/28/2017||032089364015|Secondary|Kaon|000017213968|Yes|AMP|SingleUnit|Market2|Active

これの16番目の列はMarketです。ここでMarket1からMarketPrimeへ。ファイルの名前はmarketinfo_2018-06-26.csv

私は次のコードを試しました:

awk -F '| +' '{gsub("Market1","MarketPrime",$16); print}' OFS="|" marketinfo_2018-06-26.csv > marketinfo_2018-06-26.csv

これは出力なしで実行されますが、文字列Market1はまだ残っています。

3
Apricot
awk -F '|' -v OFS='|' '$16 == "Market1" { $16 = "MarketPrime" }1' file.csv >new-file.csv

コードの唯一の実際の問題は、入力ファイルの区切り文字を|だけでなくスペースにも設定することです。これにより、データ内のスペース区切りとしてスペースがカウントされ、正しいフィールド番号が何であるかを理解するのは非常に困難になります(一部のフィールドには可変数のスペースが含まれているため)。

また、読み取りに使用するのと同じファイル名にリダイレクトすることもできません。そうすると、シェルは最初に出力ファイルを切り捨て(空に)し、awkプログラムには読み取るデータがありません。

コードは正規表現の置換を行います。これは問題ありませんが、16番目のフィールドがMarket12またはTheMarket1のようなものである場合、アンカーポイントがないために置換がトリガーされることに注意する必要があります。置換する式として^Market1$を使用するか、文字列比較を使用する方が安全です。

上記のawkコマンドは、フィールド区切り文字として|のみを使用し、16番目のフィールドとの文字列比較を行います。そのフィールドがMarket1の場合、MarketPrimeに設定されます。

awkコードの末尾の末尾の1により、すべてのレコード(変更されているかどうかにかかわらず)が印刷されます。

8
Kusalananda

問題は、入力フィールドのセパレータにあります。

複数のフィールド区切り記号を使用する必要があるため(これは必須ではありません)、以下に示すように、各行のフィールド数は異なります。

$ awk -F '[| +]' '{print NF}' test.csv
17
26
23
21

iFSとして|のみを使用する場合、コードは機能します。以下に示すように、各行には17個のフィールドがあるため。

awk -F "|" '{print NF}' test.csv
17
17
17
17 

ソリューション1:複数のIFSを使用。

awk -F '[| +]' '{gsub("Market1","MarketPrime",$(NF-1)); print}' OFS="|" test.csv

SUBSCRIBER_ID|ACCOUNT_CATEGORY|ACCOUNT_ACTIVATION_DATE|PACKAGE_NAME|PACKAGE_TYPE|DURATION|ACTIVE_DATE|INACTIVE_DATE|STB_NO|PRIMARY_SECONDARY|MODEL_TYPE|VC_NO|MULTIROOM|STB_TYPE|IPKG|SERVICE_STATE|CURRENT_STATUS
1001098068|ResidentialRegular|01/20/2007|Annual|package|199|May17|pack|Basic|Package|Annual|08/28/2017||027445053518|Primary|Pace|-|31|000223871682|Yes|AMP|Package|199|pack|MarketPrime|Active
1001098068|ResidentialRegular|01/20/2007|Annual|Pack|Premium|Package|Annual|08/28/2017||027445053518|Primary|Pace|-|31|000223871682|Yes|AMP|English|Movies|pack|MarketPrime|Active
1001098068|ResidentialRegular|01/20/2007|Annual SingleUnit Jun17 Pack|Secondary Pack|Annual|08/28/2017||032089364015|Secondary|Kaon|000017213968|Yes|AMP|SingleUnit|Market2|Active

ソリューション2:固定フィールド16

awk -F '|' '{gsub("Market1","MarketPrime",$16); print}' OFS="|" test.csv

SUBSCRIBER_ID|ACCOUNT_CATEGORY|ACCOUNT_ACTIVATION_DATE|PACKAGE_NAME|PACKAGE_TYPE|DURATION|ACTIVE_DATE|INACTIVE_DATE|STB_NO|PRIMARY_SECONDARY|MODEL_TYPE|VC_NO|MULTIROOM|STB_TYPE|IPKG|SERVICE_STATE|CURRENT_STATUS
1001098068|ResidentialRegular|01/20/2007|Annual package 199 May17 pack|Basic Package|Annual|08/28/2017||027445053518|Primary|Pace - 31|000223871682|Yes|AMP|Package 199 pack|MarketPrime|Active
1001098068|ResidentialRegular|01/20/2007|Annual Pack|Premium Package|Annual|08/28/2017||027445053518|Primary|Pace - 31|000223871682|Yes|AMP|English Movies pack|MarketPrime|Active
1001098068|ResidentialRegular|01/20/2007|Annual SingleUnit Jun17 Pack|Secondary Pack|Annual|08/28/2017||032089364015|Secondary|Kaon|000017213968|Yes|AMP|SingleUnit|Market2|Active
2
Siva

同様の問題に直面する可能性がある他の人のために明確にするために:

これらの答えは両方とも、このシナリオで機能しました。

クサラナンダの答え:

awk -F '|' -v OFS='|' '$16 == "Market1" { $16 = "MarketPrime" }1' file.csv >new-file.csv

クサラナンダの答えに基づく私の修正された答え:

awk -F '|' '{gsub("Market1","MarketPrime",$16); print}' OFS="|" marketinfo_2018-06-26.csv > marketinfo_2018-06-26.csv
0
Apricot