ファイルを入力として別のファイルでawkコマンドを実行するBashスクリプト


0

1つのファイルを取り、各行を別のファイルで実行される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
まさにそのスクリプトで得られるエラーだと確信していますか?理由がわからないのでread、そこにエラーが発生し、そのエラーも再現できません。(awk読み取りのために指定されたfdが書き込み用に開かれているため、エラーを投げますが、それはここに投稿したエラーではありません。)
ilkkachu

1. stdinとstdout以外のファイル記述子をここで使用する必要がある特別な理由はありますか?2. 1行のawkスクリプトを束ねてsh forループを実行し、毎回同じ入力ファイルを処理するのはおかしく、1 awkスクリプトよりも数百(数千とは言わないまでも)遅くなります。
cas

回答:


0

私のコメントで述べたように、あなたが私たちに語っていない何かがありますしない限り、ここでは標準入出力以外のファイル記述子を使用する必要があるとは思えない、またちょうどにファイル名を渡すことはない何らかの理由がある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'、それは第二のファイルを読み込み($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代替演算子として解釈されます。


ありがとう それは機能しますが、$ FILE1の最後のエントリにヒットしているだけで、25個すべてをループしているわけではありません。そこでループが必要なのかもしれません。
フアンアギレラ

それは変だ。あなたが与えたサンプルデータからどのような出力を得ていますか?両方のレコードを取得しています(の行なし* * * *...)。最初のレコードが一致hostAbCし、2番目のレコードが一致しhostMosます。ところで、ループがあります-すべてのawkスクリプトは、その入力ファイル/ stdinを読み取る暗黙のループです。
cas

1
exec 4> "FILE_TO_SEARCH"

問題は、書き込み専用にファイルを開き(その内容を削除する)、読み取りにはファイル記述子を使用しようとすることです。

exec 4< "$FILE_TO_SEARCH"

1
スクリプトにはこの名前の変数があるため$、前に行方不明の可能性もありFILE_TO_SEARCHます。
クサラナナンダ

見てくれてありがとう、私はあなたが推奨したような変更を行いましたが、私はまだ同じ「./bashscript2.sh:行8:読み取り:読み取りエラー:3:不正なファイル記述子」
フアンアギレラ

@JuanAguilera私は知らない、私のbashバージョンでそのように動作します。execfd 4のようにループの前でfd 3を開いてみるか、fd 3をまったく省略します。
ホークレイジング
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.