予期しない順序で出力をエコーするbashスクリプトに問題があります。スクリプトは次のとおりです。問題は、行30〜32の出力にあります。
1 IFS=$'\n'
2 i=1
3 bluered=""
4 blueyellow=""
5 redyellow=""
6 all=""
7 while [ $i -le `cat sorted.csv | wc -l` ]
8 do
9 for j in {0..2}
10 do
11 # cat sorted.csv | head -$i | tail -1 | awk -F',' '{print $1}'
12 declare "`cat sorted.csv | head -$i | tail -1 | awk -F',' '{print $1}'`=`cat sorted.csv | head -$i | tail -1 | awk -F',' '{print $5}'`"
13 i=$((i+1))
14 done
15
16 if [[ ${blue} == ${red} ]]; then bluered=1; else bluered=0; fi
17 if [[ ${blue} == ${yellow} ]]; then blueyellow=1; else blueyellow=0; fi
18 if [[ ${red} == ${yellow} ]]; then redyellow=1; else redyellow=0; fi
19 if [[ ${blue} == ${red} ]] && [[ ${red} == ${yellow} ]]; then all=1; else all=0; fi
20
21 echo "`cat sorted.csv | head -$((i-3)) | tail -1`"
22 echo ",$all,$bluered,$blueyellow,$redyellow"
23 echo "`cat sorted.csv | head -$((i-2)) | tail -1`"
24 echo ",$all,$bluered,$blueyellow,$redyellow"
25 echo "`cat sorted.csv | head -$((i-1)) | tail -1`"
26 echo ",""$all"",""$bluered"",""$blueyellow"",""$redyellow"
27
28
29
30 echo "`cat sorted.csv | head -$((i-3)) | tail -1`,$all,$bluered,$blueyellow,$redyellow"
31 echo "`cat sorted.csv | head -$((i-2)) | tail -1`"",$all,$bluered,$blueyellow,$redyellow"
32 echo "`cat sorted.csv | head -$((i-1)) | tail -1`"",""$all"",""$bluered"",""$blueyellow"",""$redyellow"
33 done
30〜32行目は、正しく機能させるためにさまざまなことを試みていたため、二重引用符のフォーマットがわずかに異なります。 21〜26行目は、30〜32行目を2つの部分に分解したものにすぎません(つまり、21〜22行目は30行目と同じです)。
入力ファイル「sorted.csv」に基づいて、30〜32行目の正しい出力(入力ファイルの最初の3行)は次のようになります。
blue,1,WCC131035882,0,e89d89d7ca7c502ca8d3b2e0d7c4980dba346a63d57a437d8f1428065fb83e9f,0,0,0,1
red,1,Z292V5DB,0,68a4917c878f1b26e370264097f476840aa995dc6b8d6d2e552a78a6bdd77c68,0,0,0,2
yellow,1,Z292V94K,0,68a4917c878f1b26e370264097f476840aa995dc6b8d6d2e552a78a6bdd77c68,0,0,0,1
ただし、実際の出力は次のとおりです。
,0,0,0,1CC131035882,0,e89d89d7ca7c502ca8d3b2e0d7c4980dba346a63d57a437d8f1428065fb83e9f #(line 30 output)
,0,0,0,192V5DB,0,68a4917c878f1b26e370264097f476840aa995dc6b8d6d2e552a78a6bdd77c68 #(line 31 output)
,0,0,0,1,Z292V94K,0,68a4917c878f1b26e370264097f476840aa995dc6b8d6d2e552a78a6bdd77c68 #(line 32 output)
21〜26行目は、次の出力を返します。
blue,1,WCC131035882,0,e89d89d7ca7c502ca8d3b2e0d7c4980dba346a63d57a437d8f1428065fb83e9f #(line 21 output)
,0,0,0,1 #(line 22 output)
red,1,Z292V5DB,0,68a4917c878f1b26e370264097f476840aa995dc6b8d6d2e552a78a6bdd77c68 #(line 23output)
,0,0,0,1 #(line 24 output)
yellow,1,Z292V94K,0,68a4917c878f1b26e370264097f476840aa995dc6b8d6d2e552a78a6bdd77c68 #(line 25)
,0,0,0,1 #(line 26 output)
要するに、行30-32のような3つの単一行コマンドを使用して行21-22、23-24、および25-26からの出力を連結したい(ただし構文は正しい)。注:行21-26はにのみ含まれています。 30行目の2つの部分(31または32)が2行に分割されたときに正しく機能していることを示すスクリプト。現在、30行目は、21行目から22行目ではなく、22行目から21行目までの出力を効果的に連結しています。ただし、この逆連結を行うと、21行目の出力の最初の8文字も切り捨てられます(出力に注意してください)。 22行目のは正確に8文字です)。
30〜32行目を正しく記述して、目的の出力を作成するにはどうすればよいですか?
よろしくお願いします。
簡潔な「use _dos2unix
_」よりも洞察力があります。
どうやら_sorted.csv
_はCR + LF行末を使用しますが、 LFのみ を使用する必要があります。
_`something`
_を使用すると、something
の出力の最後で改行(LF)が削除されますが、キャリッジリターン(CR)は削除されません。あなたの場合、_text+CR+LF
_は_text+CR
_になりました。 echo
への唯一の入力である場合、ツールは通常どおり改行を追加し、最後にCR + LFがあります。コンソールに印刷している間、このCRは何も変更しません。
ただし、_echo "`foo`bar"
_の場合、foo
によって返されるCR文字は結果の文字列の中央に留まるため、後続の文字はコンソールの左端から出力され、前の部分が上書きされます。
解決策は、_dos2unix sorted.csv
_を使用することです すでに述べたように 。
しかし、それだけではありません。
cat
の無用な使用$(stuff)
と_`stuff`
_の違いは何ですか?printf
がecho
よりも優れている理由""
_の多くの出現(たとえば32行目)は何もしません。それらは、開く+閉じるのではなく、閉じる+開くです。コードを難読化するだけです。私はそれらが「実験的」であることを理解しており、明らかに何も変わらないと言っています。そしてたぶん
echo $(stuff)
または_echo `stuff`
_の何が問題になっていますか?
私はこの異議がここで疑わしいことを認めます。 _"`foo`bar"
_は、foo
の出力から末尾のLFを取り除き、それをbar
と連結するのに役立ちます。あなたの場合は重要です。次に、構文_echo "`foo`"
_が動作の関連する違いを示すために使用されたように、あなたはこれを明示的に述べました。
_dos2unix
_は確かに質問に答えません。それは[私の強調]です:
30〜32行目を正しく書き込んで、目的の出力を作成するにはどうすればよいですか?
正しく書かれた32行目は、少なくともprintf
を使用し、cat
は使用しません。コードは$( … )
と適切な引用符を使用すると読みやすくなりますが、バッククォートと過度の引用符が残る場合があります。フォーマットをデータから分離することも検討できます。printf
を使用すると簡単です。
_printf '%s,%s,%s,%s,%s\n' "$( <sorted.csv head -$((i-1)) | tail -1 )" "$all" "$bluered" "$blueyellow" "$redyellow"
_
さらに、他の場所には不十分な解決策があります。
while [ $i -le `cat sorted.csv | wc -l` ]
_(cat
とバッククォートを除く)。反復ごとに行数を取得する必要はありません。 _wc -l sorted.csv
_は、ループの前に1回実行する必要があり、その結果は変数に格納されます(実行中に行数が変わると予想しない限り、そうではないと思います。スクリプトのロジックは、番号は変わりません)。あなたは何度も何度もファイルを新たに読んだ。行数が多いほど、再度開いて最初から読み、毎回単一行を選択する回数が増えます。フローを再設計して、ファイルが1行ずつ、おそらく前の_wc -l
_なしで、おそらくパイプのように解析されるようにする必要があります(つまり、1回だけ開いて、スキップせずに1回だけ読み取ります)。 declare
ビルトインを使用しているので、_while IFS= read -r … ; done <sorted.csv
_はおそらく必須です(おそらく予備のawk
、たとえば_<sorted.csv awk … | while IFS= read -r …
_)。 read
を3回実行して、3つの異なる変数を作成してから、操作を実行できます。次に、次の3行を読みます。 read
自体は非効率的ですが、ファイルを何度も再度開くよりもエレガントです。ファイルに行を追加するたびに、スクリプトの効率が低下することに注意してください。ファイルがread
の非効率性を明らかにするのに十分な大きさである場合、アプローチのパフォーマンスはさらに低下する可能性があります。
しかし、この全体的な変更は、あなたがそれを行えば、些細なことではありません。
ファイルの名前は、それほど多くの場所でハードコーディングしないでください。ファイルを1回だけ読み取ると、当然この問題は軽減されます。それでも、_input_file="sorted.csv"
_から始めて、必要に応じて_"$input_file"
_を使用することをお勧めします。ファイルパスをコマンドライン引数として渡すことにした場合は、_input_file="$1"
_と入力するのと同じくらい簡単です。
なぜシバンがないのですか?
全体像を考慮すると、30〜32行目は次のようになります。
_#!/bin/bash
input_file="sorted.csv
…
while IFS= read -r pre_previous_line && IFS= read -r previous_line && IFS= read -r current_line; do
…
# useful bashism instead of echo "$previous_line" | some_command
some_command <<< "$previous_line"
…
# printf will reuse the format if there are more arguments than the format needs
# so this one line will be enough for your three
# (split for readability, it's still one line for the Shell)
printf '%s,%s,%s,%s,%s\n' \
"$pre_previous_line" "$all" "$bluered" "$blueyellow" "$redyellow" \
"$previous_line" "$all" "$bluered" "$blueyellow" "$redyellow" \
"$current_line" "$all" "$bluered" "$blueyellow" "$redyellow"
…
done <"$input_file"
_
おそらく、フィルターとして機能する唯一のawk
がこれを行うことができます。ファイルはそれにパイプされます。 awk
では、後で使用するために、変数に情報を格納することもできます。条件付きもご利用いただけます。このようなものテストが不十分の例:
_#!/usr/bin/awk -f
BEGIN { FS="," }
{
if ($1 == "blue") blue=$5
if ($1 == "red") red=$5
if ($1 == "yellow") yellow=$5
if (NR%3 == 1) prepre=$0
if (NR%3 == 2) pre=$0
if (NR%3 == 0)
{
bluered=($blue == $red)
blueyellow=($blue == $yellow)
redyellow=($red == $yellow)
all= bluered * blueyellow
suffix=","all","bluered","blueyellow","redyellow
print prepre suffix
print pre suffix
print $0 suffix
}
}
_
(注:私がテストしたawk
は実際にはmawk
です)。それを保存し、実行可能にし、_sorted.csv
_をそれにパイプします(例:_<sorted.csv ./the_script
_)。
ファイルにあるキャリッジリターンについてのコメントは死んでいた。 dox2unixと思われる入力ファイルを実行した後、スクリプトは期待どおりに実行されました。ありがとう、ゴードン。