以前は、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
バックティックを使用しないでください。代わりに使用してください
$()
。同じことを行いますが、参照を中断せずに入れ子にすることができます。files
for
配列のようにループで使用されますが、配列ではありません。これをスカラー文字列(の出力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")
変数拡張を二重引用符で囲みます。中かっこを使うことはいいえ引用された代替。バラよりスペースやその他の特殊文字が原因でシェルスクリプトが停止するのはなぜですか?そして$VAR対${VAR}と引用理由があります。
2番目のスクリプトでは、出力
cut -d, -f2
をcut -d, -f3
。それは動作しません。最初は
cut
1つのフィールド(フィールド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
。これが問題かどうかはわかりませんが、知っておくべき部分です。スクリプトは入力ファイルと同じ名前ですが、プレフィックス付きの出力ファイルにstdoutをリダイレクトします
data_
。したがって、入力ファイルdata_batch_1.csv
がdata_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_」という文字列が先頭の$3
stdoutを使用して印刷されます。FILENAME
cut
これは、処理する各ファイルに対して一度に複数回分岐してgrep
実行する必要がないため、はるかに高速です。
最後に、CSVファイルには二重引用符で囲まれた文字列フィールドを含めることができ、しばしば含まれます。これらの引用符付きフィールドにはコンマを含めることができます。引用符なしでカンマを含むフィールドを持たない単純なカンマ区切りファイルは、を使用して確実に処理できますcut
。すべてのオプションのアドインを含む実際のCSVにはCSVパーサーが必要です。最良の方法は、次を使用することです。
答え2
awkが利用可能な場合:
$ cat /tmp/abc
name1,0.0.0.0,NetworkName1
name2,0.4.2.3,NetworkName2
name3,0.1.43.5,NetworkName3
$ awk 'BEGIN { FS = "," } ;{printf $2","$3"\n"}' /tmp/abc
0.0.0.0,NetworkName1
0.4.2.3,NetworkName2
0.1.43.5,NetworkName3
したがって、この場合
for i in $(ls | grep -E ^test.*[.]csv$)
do
cat $i | cut -d , -f2,3 >> testing.txt
done
できる
$ awk 'BEGIN { FS = "," } ;{printf $2","$3"\n"}' test*.csv > testing.txt
構造化されたテキスト処理が多い場合は、awkを学ぶのに時間を費やすことが役に立ちます。
答え3
私は次のような幸運を享受しました。
ディレクトリの内容:
$ ls
test.csv test1.csv test3csv test5.txt
各ファイルには次の行が含まれています。
name1,0.0.0.0,NetworkName1
name2,0.4.2.3,NetworkName2
name3,0.1.43.5,NetworkName3
スクリプト:
for i in $(ls | grep -E ^test.*[.]csv$)
do
cat $i | cut -d , -f2,3 >> testing.txt
done
これにより、testで始まり、で終わるすべてのファイルがインポートされ、.csv
フィールド2と3が削除され、ファイルに追加されますtesting.txt
。
それ以降の出力ファイルは次のようになります。
0.0.0.0,NetworkName1
0.4.2.3,NetworkName2
0.1.43.5,NetworkName3
各 IP アドレスと各ネットワーク名を別々の行にリストします。
スクリプトが出力ファイルの内容を上書きするのは、現在のファイル>
のすべての内容を上書きする演算子を使用しているためです。一方、必要なのは、>>
ファイルの末尾にテキストを追加する演算子です。ファイルの。