bashスクリプトを使用してcsvファイルの2つのフィールド(1つの代わりに)を出力として印刷するにはどうすればよいですか?

bashスクリプトを使用してcsvファイルの2つのフィールド(1つの代わりに)を出力として印刷するにはどうすればよいですか?

以前は、bashスクリプトを書くためにbashをあまり使用していませんでした。ファイルにはcsv形式で保存された多くのフィールドが含まれています。以下の最初のスクリプトはファイル内のすべてのIPを収集しますが、収集するのにも苦労しています。知的財産権別のフィールドが呼び出されます。ネットワーク。。私がこれを達成できるかどうかを知っている人はいますか?

files=`ls | grep data_batch_`
for file in ${files[@]}
do
  cat ${file} | cut -d , -f2 | grep -v "IP" > data_${file}
done

私は成功せずにブール演算子を追加してみました。また、もっとパイプを試しました。私はbashを頻繁に使用しないため、いくつかの構文が欠落しているか、これが許可されていない理由を理解できない可能性があります。

    files=`ls | grep data_batch`
for file in ${files[@]}
do
  cat ${file} | cut -d , -f2 | cut -d, -f3 | grep -v "IP" && "Network" > data_${file}
done

何らかの理由でこれを行うと上書きされるようです。知的財産権価値を与えるネットワーク値を同時に保存する代わりに。本質的に私が望むのは、1つのフィールドではなく2つのフィールドをファイルに印刷することですが、彼のソリューションを実装する方法がわかりません。どんなヒントでも役に立ちます。

私が望む出力は、ファイルに保存されているIPアドレス値とネットワーク値です。現在私が得るのはIPだけです。以下は希望の出力です。

1.1.1.1
Network5

ベストアンサー1

スクリプトには多くの問題があります。

files=`ls | grep data_batch_`
for file in ${files[@]}
do
  cat ${file} | cut -d , -f2 | grep -v "IP" > data_${file}
done
  1. lsを解析しないでください

  2. バックティックを使用しないでください。代わりに使用してください$()。同じことを行いますが、参照を中断せずに入れ子にすることができます。

  3. filesfor配列のようにループで使用されますが、配列ではありません。これをスカラー文字列(の出力ls | grep ...)として定義します。配列を定義するには、次のように括弧を使用する必要があります。

    filesこれは文字列として定義されます。

    $ files=$(echo 1 2 3)
    $ declare -p files
    declare -- files="1 2 3"
    

    これは配列として定義されていますが:

    $ files=( $(echo 1 2 3) )
    $ declare -p files
    declare -a files=([0]="1" [1]="2" [2]="3")
    

    mapfileまたは(別名)を使用できますreadarray

     $ mapfile -t files < <(printf "%s\n" 1 2 3)
     $ declare -p files
     declare -a files=([0]="1" [1]="2" [2]="3")
    
  4. 変数拡張を二重引用符で囲みます。中かっこを使うことはいいえ引用された代替。バラよりスペースやその他の特殊文字が原因でシェルスクリプトが停止するのはなぜですか?そして$VAR対${VAR}と引用理由があります。

  5. 2番目のスクリプトでは、出力cut -d, -f2cut -d, -f3。それは動作しません。

    最初はcut1つのフィールド(フィールド2)のみを出力します。 2番目の項目は、cut入力にフィールドが1つだけ(またはカンマがないためフィールドがない)、存在しないフィールドを出力するように指示したため、まったく同じように出力されます。 3.実行してからecho 1,2,3 | cut -d, -f2実行すると、次の結果がecho 1,2,3 | cut -d, -f2 | cut -d, -f3表示されます。どちらのコマンドも出力は同じです。2

    2つの出力フィールドを使用するには、cut -fカンマで区切ってリストします。たとえば、

    cut -d, -f2,3
    

    ただし、を使用してフィールドの範囲を指定することもできます-。たとえば、フィールド2〜5を出力するには、次のものを使用できますcut -d, -f2-5。望むよりman cut

  6. これが問題かどうかはわかりませんが、知っておくべき部分です。スクリプトは入力ファイルと同じ名前ですが、プレフィックス付きの出力ファイルにstdoutをリダイレクトしますdata_。したがって、入力ファイルdata_batch_1.csvdata_data_batch_1.csv

    これはあなたが望むものかもしれません。ただし、スクリプトを再実行すると、ファイルglobが元の入力ファイルと一致することを意味します。そして最初の実行では、出力ファイルが生成されます。その結果data_data_data_batch_1.csv


それにもかかわらず、これは問題です。以下はいくつかの回避策です。次の方法を試してください。

for file in *data_batch_*; do
  cut -d, -f2,3 "$file" | grep -v IP > "data_$file"
done

ファイル名の配列を実際に使用するには、たとえば、およびmapfileを使用できます。find-print0

mapfile -t -d '' files < <(find . -maxdepth 1 -type f -name '*data_batch_*' -print0)
for file in "${files[@]}"; do
   cut -d, -f2,3 "$file" | grep -v IP > "data_$file"
done

awkまたは、次のものを代わりに使用できますcut

awk -F, -v OFS=, '$2$3 !~ /IP/ { print $2, $3 > "data_" FILENAME }' *data_batch_*

$2「IP」または「IP」の両方が含まれていない場合は、現在のファイル名(awkの変数)と同じ名前のファイルにリダイレクトされ、「data_」という文字列が先頭の$3stdoutを使用して印刷されます。FILENAME

cutこれは、処理する各ファイルに対して一度に複数回分岐してgrep実行する必要がないため、はるかに高速です。


最後に、CSVファイルには二重引用符で囲まれた文字列フィールドを含めることができ、しばしば含まれます。これらの引用符付きフィールドにはコンマを含めることができます。引用符なしでカンマを含むフィールドを持たない単純なカンマ区切りファイルは、を使用して確実に処理できますcut。すべてのオプションのアドインを含む実際のCSVにはCSVパーサーが必要です。最良の方法は、次を使用することです。

  1. すべての機能を備えたCSVパーサーが既にある言語(例perlテキスト::CSVモジュールpythonを含みますデータセット図書館。

  2. このようなツールミラーまたはcsvkit

おすすめ記事