私は、作業中のXMLの1つへの入力として使用できるように、特定の形式の文字列を生成するシェルスクリプトを作成しています。
次の形式の入力ファイルが提供されます。<attribute field>,<data_type>,<size>
instanceid,varchar,256
sysdate,date
status,number
notes,varchar,4000
created_on,date
「チェックサム」を変数(例:md5( INSTANCEID || STATUS || NOTES)
Or'd)のフィールド以外のすべてのプロパティフィールドに保存したいと思います。
私が書いたスクリプトはこれです
IFS=$'\n'
file=$(cat source.txt)
line_number=$(cat source.txt | wc -l)
checksum="md5( "
for line in $file
do
let line_number=line_number-1
data_field=$(echo $line | cut -f1 -d','| tr "a-z" "A-Z")
data_type=$(echo $line | cut -f2 -d',' | tr "a-z" "A-Z")
if [ $data_type != "DATE" ] && [ $line_number -gt 0 ]
then checksum+="$data_field || "
elif [ $data_type != "DATE" ] && [ $line_number -eq 0 ]
then checksum+=" $data_field "
fi
done
checksum+=")"
echo $checksum
このスクリプトは、日付型の属性を持つ最後の行を除くすべての入力シナリオで機能します。
この場合、変数の値は次のようになります。md5( INSTANCEID || STATUS || NOTES || )
最後の行が日付であることを確認するためにコマンドを試しましたが、最後の行が日付タイプのtail
場合は再び失敗します。
||
最後のアイテムをどのように削除できますか?
答え1
checksum="${checksum% || })"
代わりに速い答えはですchecksum+=")"
。各ステップで無条件文字列を追加し、||
最後に不要な最後の文字列を削除するだけです(したがって、line_number
もう計算は不要です)。
より良い方法は
awk -F, 'BEGIN { printf "md5( " }
toupper($2) != "DATE" { printf "%s%s", sep, toupper($1); sep = " || " }
END { print ")" }' source.txt
答え2
cat
シェルスクリプトではほとんど役に立たないことは注目に値します。$(cat source.txt | wc -l)
古典です役に立たない使用cat
;これは、ファイルの行数を数える必要がある場合は$(wc -l < source.txt)
よりきれいな方法です。- ただし、計算には行数は必要ありません
source.txt
。 file=$(cat source.txt)
ファイルを読むための醜い方法です。読みながら… する ︙ 完了<ファイル名
より良い。read
これの利点は、行をフィールドに分割することです。tr
ファイル全体に対して一度だけ実行するだけですが、ファイルの各行に対して2回実行するのは愚かです。場合によっては、ト… <ファイル名|読みながら... する ︙ 完璧
良い結果。しかし、問題があります。while
ループはサブシェルで実行されるため、シェルchecksum
変数(たとえば)への変更はループの終了後には表示されません。 Terdonはこの問題を解決する1つの方法を示しています。ト… <ファイル名|{読みながら… する ︙ 変更される可能性のある注文チェックサム。 ︙ 完璧 ︙ 使用されるコマンド$チェック島。 ︙ }
ご存知のように、最後に発生した問題がどこにあるかを把握するのは難しいかもしれません。通常、最初の項目を識別する方が簡単です。
checksum="md5(" first=1 tr "a-z" "A-Z" < source.txt | { while IFS=, read data_field data_type size do if [ "$data_type" != "DATE" ] then if [ "$first" ] then first= else checksum+=" || " fi checksum+="$data_field" fi done checksum+=")" echo "$checksum" }
参考にしてください本物2回テストする必要はありません
if [ "$data_type" != "DATE" ]
。また、妥当な理由がなく、実行している操作を確実に知らない限り、常に
シェル変数(たとえば)への参照を引用する必要があります。"$data_type"
追加の最適化でこの
first
変数を削除し、checksum
それ自体を使用してループの最初の反復を識別できます。checksum= tr "a-z" "A-Z" < source.txt | { while IFS=, read data_field data_type size do if [ "$data_type" != "DATE" ] then if [ "$checksum" != "" ] then checksum+=" || " fi checksum+="$data_field" fi done checksum="md5($checksum)" echo "$checksum" }
答え3
あなたが書くのと同じくらい複雑なものは必要ありません。次のことができます。
#!/usr/bin/env bash
checksum="md5("
## Read each line into the fields array (read -a fields), with fields
## separated by commas (IFS=,)
while IFS=, read -a fields
do
## If the 2nd element of the array is not "DATE"
if [ ${fields[1]} != "DATE" ]
then
## Add this to $checksum
checksum+="${fields[0]} || "
fi
## The tr is making everything upper case and then feeds
## directly into the while loop.
done < <(tr "a-z" "A-Z" < "$1")
## Get rid of the last || and add the closing ")"
checksum="${checksum% || })"
printf "OUT is: %s\n" "$checksum"
その後、ファイルを入力として使用してスクリプトを実行できます。
$ foo.sh file
OUT is: md5(INSTANCEID || STATUS || NOTES)