Bashから2つのファイルを1行ずつ読み、各行でいくつかの操作を実行しようとしています。これは私のBashスクリプトです。
#!/usr/bin/env bash
die()
{
echo "$@" >&2
exit 1
}
extract_char()
{
echo "$1" | sed "s/.*'\([^']*\)'.*/\1/g"
}
file1=$1 # old
file2=$2 # new
counter=0
win_count=0
lose_count=0
test ! -z "$file1" || die "Please enter 2 files."
test ! -z "$file2" || die "Please enter 2 files."
while read -r line1 && read -r line2 <&3
do
let counter++
index=$(expr index "$line1" "'")
if [ $index -ne 0 ]; then
char=$(extract_char "$line1")
char2=$(extract_char "$line2")
test "$char" = "$char2" || die "Chars in line1 and line2 were not the same."
elif [ "${line1#char.}" != "$line1" ]; then
test "${line2#char.}" != "$line2" || die "Method signature found in line1, but not line2."
method=${line1%:}
method=${method#char.}
elif ! grep -q '[^[:space:]]'; then
# benchmark times
if [ $(date --date="$line1" +%s%N) -gt $(date --date="$line2" +%s%N) ]; then
echo "$char $method $counter: $line1 is greater than $line2"
let lose_count++
else
let win_count++
fi
fi
done < "$file1" 3< "$file2"
echo
echo "Lines where this made an improvement: $win_count"
echo "Lines where this made a regression: $lose_count"
使用法は次のとおりです。
./compare.sh oldresults.txt newresults.txt
ここoldresults.txt
で、newresults.txt
およびは、ベンチマーク結果を含む2つのファイルです。以下はサンプルファイルです。
Test results for '\u0020':
char.IsUpper:
00:00:00.1231231
00:00:00:4564564
char.IsLower:
00:00:00:3453455
00:11:22:4444444
Tests for '\u1234':
# and so on
何らかの理由で、read
ファイルの読み取りを終了する前にゼロ以外の終了ステータスを返すようです。以下はスクリプトをデバッグするときの出力です(通過bash --debug -x compare.sh [args]
)。
+ file1=oldresults.txt
+ file2=newresults.txt
+ counter=0
+ win_count=0
+ lose_count=0
+ test '!' -z oldresults.txt
+ test '!' -z newresults.txt
+ read -r line1
+ read -r line2
+ let counter++
++ expr index 'Test results for '\''\u0020'\'':
' \'
+ index=18
+ '[' 18 -ne 0 ']'
++ extract_char 'Test results for '\''\u0020'\'':
'
++ echo 'Test results for '\''\u0020'\'':
'
++ sed 's/.*'\''\([^'\'']*\)'\''.*/\1/g'
+ char='\u0020'
++ extract_char 'Test results for '\''\u0020'\'':
'
++ echo 'Test results for '\''\u0020'\'':
'
++ sed 's/.*'\''\([^'\'']*\)'\''.*/\1/g'
+ char2='\u0020'
+ test '\u0020' = '\u0020'
+ read -r line1
+ read -r line2
+ let counter++
++ expr index $'\r' \'
+ index=0
+ '[' 0 -ne 0 ']'
+ '[' $'\r' '!=' $'\r' ']'
+ grep -q '[^[:space:]]'
+ read -r line1 # exits the loop here
+ echo
+ echo 'Lines where this made an improvement: 0'
Lines where this made an improvement: 0
+ echo 'Lines where this made a regression: 0'
Lines where this made a regression: 0
ご覧のとおり、スクリプトは2行\u0020
、つまり引用符の間から抽出された「...のテスト結果」行とキャリッジリターンを繰り返します。その後はread -r line1
奇妙なことに失敗し、ループを終了するようです。
なぜこれが起こるのですか?この問題を解決するにはどうすればよいですか?ありがとうございます。
答え1
何が起こっているの?grep -q '[^[:space:]]'
標準入力の残りのライン処理(grep
入力を提供しない場合はデフォルトで行われます)次のステップでは何も残りません。read
ファイルポインタはEOFにあります。あなたが望むものですgrep -q '[^[:space:]]' <<< "$line1"
。
このエラーを回避する簡単な方法は、ループコードが重要な場合は常にデフォルトではないファイル記述子を使用することです。単一のコマンドでstdinをすべて飲み込む方法がありますが、基本的にFD 3以降を読み取ろうとするプログラムはまだ見たことがありません。