ファイルをインポートし、各行を別のファイルで実行される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