Bashスクリプトは1つのファイルを入力として使用し、別のファイルでawkコマンドを実行します。

Bashスクリプトは1つのファイルを入力として使用し、別のファイルでawkコマンドを実行します。

ファイルをインポートし、各行を別のファイルで実行されるawkコマンド文字列として実行するスクリプトを作成しようとしています。これが私が現在持っているものです。

#!/bin/bash

FILE=$1
FILE_TO_SEARCH=$2

exec 4> "FILE_TO_SEARCH"

while read -ru 3 LINE; do
    awk -v RS='' -v ORS='\n\n' "$LINE" <&4
done 3< "$FILE"

スクリプトを実行しようとすると、次の結果が表示されます。

./bashscript2.sh: line 8: read: read error: 3: Bad file descriptor

たとえば、検索するファイル(FILE)の内容は次のとおりです。

hostAbC
host123
host345
hostMos
hostDef

次に、以下の内容に似ていますが、より多くの内容を含むファイル(FILE_TO_SEARCH)に対してawkコマンドを実行します。

* * * * * * * * *  * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 id: urn:storageos:Initiator:
    clustername = BLAHBLAHBLAH
    creationTime = java.util.GregorianCalendar[
                time=1490279415811
                2017-03-23 14:30:15 811ms UTC
,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2017,MONTH=2,WEEK_OF_YEAR=12,WEEK_OF_MONTH=4,DAY_OF_MONTH=23,DAY_OF_YEAR=82,DAY_OF_WEEK=5,DAY_OF_WEEK_IN_MONTH=4,AM_PM=1,HOUR=2,HOUR_OF_DAY=14,MINUTE=30,SECOND=15,MILLISECOND=811,ZONE_OFFSET=0,DST_OFFSET=0]
    host = URI: 
    hostname = hostAbC
    inactive = false
    ininode = 01:01:01:01:01:01:01:01
    iniport = 01:01:01:01:01:01:01:01
    internalFlags = 0
    isManualCreation = true
    label = 01:01:01:01:01:01:01:01
    status = OpStatusMap {}
    protocol = FC
    registrationStatus = REGISTERED


 * * * * * * * * *  * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 id: urn:storageos:Initiator:
    clustername = YADAYADAYADA
    creationTime = java.util.GregorianCalendar[
                time=1485972630239
                2017-02-01 18:10:30 239ms UTC
,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2017,MONTH=1,WEEK_OF_YEAR=5,WEEK_OF_MONTH=1,DAY_OF_MONTH=1,DAY_OF_YEAR=32,DAY_OF_WEEK=4,DAY_OF_WEEK_IN_MONTH=1,AM_PM=1,HOUR=6,HOUR_OF_DAY=18,MINUTE=10,SECOND=30,MILLISECOND=239,ZONE_OFFSET=0,DST_OFFSET=0]
    host = URI: 
    hostname = hostMos
    inactive = false
    ininode = 01:01:01:01:01:01:01:01
    iniport = 01:01:01:01:01:01:01:01
    internalFlags = 0
    isManualCreation = false
    label = 01:01:01:01:01:01:01:01
    status = OpStatusMap {}
    protocol = FC
    registrationStatus = REGISTERED


* * * * * * * * *  * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

その後、awkコマンドはアイテムをawk -v RS='' -v ORS='\n\n'見つけて、あるhostAbCスペースから別のスペースにアイテム全体を返します。

私はそれを動作させる方法を知りません。

答え1

exec 4> "FILE_TO_SEARCH"

問題は、書き込み用にのみファイルを開くが(したがってコンテンツを削除する)、ファイル記述子を読み取り用に使用しようとしていることです。

exec 4< "$FILE_TO_SEARCH"

答え2

私の意見で指摘したように、私たちに何も言わなかったら、ここではstdinとstdout以外のファイル記述子を使用する必要はなく、ファイル名をawkコマンドに渡さない理由もないようです。ライン。

while read同じ入力ファイルで複数回実行するようにシェルループを作成するawkことは、目的のタスクを実行するための非常に悪い方法です。おそらくテキストファイルを処理する最も悪い方法でしょう。 awk(またはsedやPerlなど)で同じことをするよりも数百または数千倍遅くなります。

次のようにしてみてください。

#!/bin/bash

FILE1="$1"
FILE_TO_SEARCH="$2"

awk 'NR==FNR { gsub(/([\\.^$(){}\[\]|*+?])/,"\\\\&",$0);
               if (search == "") {
                 search = $0;
               } else {
                 search = search "|" $0;
               };
               next;
             };

     match($0,search)' "$FILE1" RS='' ORS='\n\n' "$FILE_TO_SEARCH"

(読みやすくするために改行とインデントを追加しました。この操作のawk部分もすべて1行に圧縮しました。)

$FILE_TO_SEARCHこれにより、検索パターンに一致するすべてのレコードが印刷されます$FILE1

$FILE1デフォルト&を使用してRS最初のファイル()を読み、ORSここで正規表現検索パターンを設定します。このgsub()関数呼び出しは、検索パターンに各行を追加する前に、すべての正規表現メタ文字をバックスラッシュにエスケープするために使用されます。つまり、すべての行は固定文字列として扱われます。各行を正規表現にするには、以下の2番目のバージョンを参照してください。

上記の例では、$FILE1検索パターンは次のようになります。

hostAbC|host123|host345|hostMos|hostDef 

次に、RS=''とを使用してORS='\n\n'2番目のファイル($FILE_TO_SEARCH)を読み取り、検索パターンに一致するすべてのレコードを印刷します。


$FILE1各行を固定文字列ではなく正規表現として解釈するには、次のバージョンを使用できます。

#!/bin/bash

FILE1="$1"
FILE_TO_SEARCH="$2"

awk 'NR==FNR { if (search == "") {
                 search = "(" $0 ")" ;
               } else {
                 search = search "|(" $0 ")";
               };
               next;
             };

     match($0,search)' "$FILE1" RS='' ORS='\n\n' "$FILE_TO_SEARCH"

このバージョンの検索パターンの例は次のとおりです。

(hostAbC)|(host123)|(host345)|(hostMos)|(hostDef)

このバージョンでは、何も一致しない、または一致しすぎる壊れた検索パターンを簡単に構築できます。 $ FILE1でリテラル文字列として解釈する正規表現メタ文字をエスケープするには、バックスラッシュを使用する必要があります。たとえば、テキストを一致させるには、その|テキストをファイルに含める必要があります。それ以外の場合は、正規表現代替演算子\|として解釈されます。OR

関連情報