ฉันต้องการเปรียบเทียบตัวเลขทศนิยมสองตัวในเชลล์สคริปต์ รหัสต่อไปนี้ใช้งานไม่ได้:
#!/bin/bash
min=12.45
val=10.35
if (( $val < $min )) ; then
min=$val
fi
echo $min
ฉันต้องการเปรียบเทียบตัวเลขทศนิยมสองตัวในเชลล์สคริปต์ รหัสต่อไปนี้ใช้งานไม่ได้:
#!/bin/bash
min=12.45
val=10.35
if (( $val < $min )) ; then
min=$val
fi
echo $min
คำตอบ:
คุณสามารถตรวจสอบส่วนจำนวนเต็มและเศษส่วนแยกต่างหาก:
#!/bin/bash
min=12.45
val=12.35
if (( ${val%%.*} < ${min%%.*} || ( ${val%%.*} == ${min%%.*} && ${val##*.} < ${min##*.} ) )) ; then
min=$val
fi
echo $min
ดังที่ fered บอกไว้ในความคิดเห็นมันใช้งานได้ก็ต่อเมื่อตัวเลขทั้งสองมีส่วนที่เป็นเศษส่วนและส่วนที่เป็นเศษส่วนทั้งสองมีจำนวนหลักเท่ากัน นี่คือรุ่นที่ใช้งานได้สำหรับจำนวนเต็มหรือเศษส่วนและตัวดำเนินการ bash ใด ๆ :
#!/bin/bash
shopt -s extglob
fcomp() {
local oldIFS="$IFS" op=$2 x y digitx digity
IFS='.' x=( ${1##+([0]|[-]|[+])}) y=( ${3##+([0]|[-]|[+])}) IFS="$oldIFS"
while [[ "${x[1]}${y[1]}" =~ [^0] ]]; do
digitx=${x[1]:0:1} digity=${y[1]:0:1}
(( x[0] = x[0] * 10 + ${digitx:-0} , y[0] = y[0] * 10 + ${digity:-0} ))
x[1]=${x[1]:1} y[1]=${y[1]:1}
done
[[ ${1:0:1} == '-' ]] && (( x[0] *= -1 ))
[[ ${3:0:1} == '-' ]] && (( y[0] *= -1 ))
(( ${x:-0} $op ${y:-0} ))
}
for op in '==' '!=' '>' '<' '<=' '>='; do
fcomp $1 $op $2 && echo "$1 $op $2"
done
1.00000000000000000000000001
2
Bash ไม่เข้าใจคณิตศาสตร์เลขทศนิยม มันถือว่าตัวเลขที่มีจุดทศนิยมเป็นสตริง
ใช้ awk หรือ bc แทน
#!/bin/bash
min=12.45
val=10.35
if [ 1 -eq "$(echo "${val} < ${min}" | bc)" ]
then
min=${val}
fi
echo "$min"
หากคุณต้องการดำเนินการทางคณิตศาสตร์จำนวนมากมันน่าจะดีกว่าถ้าคุณใช้ python หรือ perl
คุณสามารถใช้แพ็คเกจnum-utils สำหรับการปรับเปลี่ยนอย่างง่าย ๆ ...
สำหรับคณิตศาสตร์ที่รุนแรงยิ่งขึ้นให้ดูที่ลิงค์นี้ ...ซึ่งจะอธิบายตัวเลือกต่างๆเช่น
ตัวอย่างของ numprocess
echo "123.456" | numprocess /+33.267,%2.33777/
# 67.0395291239087
A programs for dealing with numbers from the command line
The 'num-utils' are a set of programs for dealing with numbers from the
Unix command line. Much like the other Unix command line utilities like
grep, awk, sort, cut, etc. these utilities work on data from both
standard in and data from files.
Includes these programs:
* numaverage: A program for calculating the average of numbers.
* numbound: Finds the boundary numbers (min and max) of input.
* numinterval: Shows the numeric intervals between each number in a sequence.
* numnormalize: Normalizes a set of numbers between 0 and 1 by default.
* numgrep: Like normal grep, but for sets of numbers.
* numprocess: Do mathematical operations on numbers.
* numsum: Add up all the numbers.
* numrandom: Generate a random number from a given expression.
* numrange: Generate a set of numbers in a range expression.
* numround: Round each number according to its value.
นี่คือbash
แฮ็ค ... มันเพิ่ม 0 นำไปสู่จำนวนเต็มเพื่อทำการเปรียบเทียบสตริงจากซ้ายไปขวาที่มีความหมาย รหัสชิ้นส่วนนี้ต้องการให้ทั้ง minและvalมีจุดทศนิยมและอย่างน้อยหนึ่งหลัก
min=12.45
val=10.35
MIN=0; VAL=1 # named array indexes, for clarity
IFS=.; tmp=($min $val); unset IFS
tmp=($(printf -- "%09d.%s\n" ${tmp[@]}))
[[ ${tmp[VAL]} < ${tmp[MIN]} ]] && min=$val
echo min=$min
เอาท์พุท:
min=10.35
สำหรับการคำนวณอย่างง่าย ๆ เกี่ยวกับจำนวนจุดลอยตัว (+ - * / และการเปรียบเทียบ) คุณสามารถใช้ awk
min=$(echo 12.45 10.35 | awk '{if ($1 < $2) print $1; else print $2}')
หรือถ้าคุณมี ksh93 หรือ zsh (ไม่ใช่ bash) คุณสามารถใช้เลขคณิตในตัวของเชลล์ซึ่งรองรับตัวเลขทศนิยม
if ((min>val)); then ((val=min)); fi
สำหรับการคำนวณจุดลอยสูงขึ้นมองขึ้นBC ใช้งานได้จริงกับหมายเลข fixpoint ที่แม่นยำโดยพลการ
คำสั่งsort
มีตัวเลือก-g
( --general-numeric-sort
) ที่สามารถใช้สำหรับการเปรียบเทียบ<
"น้อยกว่า" หรือ>
"ใหญ่กว่า" โดยค้นหาต่ำสุดหรือสูงสุด
ตัวอย่างเหล่านี้กำลังค้นหาขั้นต่ำ:
$ printf '12.45\n10.35\n' | sort -g | head -1
10.35
มันทำงานกับสัญกรณ์ทั่วไปของตัวเลขจุดลอยตัวเช่นเดียวกับ E-Notation
$ printf '12.45E-10\n10.35\n' | sort -g | head -1
12.45E-10
หมายเหตุE-10
ทำให้หมายเลขแรกแน่นอนน้อยกว่า0.000000001245
10.35
มาตรฐานจุดลอยตัวIEEE754กำหนดค่าพิเศษบางอย่าง สำหรับการเปรียบเทียบเหล่านี้สิ่งที่น่าสนใจINF
สำหรับอินฟินิตี้ นอกจากนี้ยังมีอินฟินิตี้ลบ ทั้งสองเป็นค่าที่กำหนดไว้อย่างดีในมาตรฐาน
$ printf 'INF\n10.35\n' | sort -g | head -1
10.35
$ printf '-INF\n10.35\n' | sort -g | head -1
-INF
ในการค้นหาการใช้งานสูงสุดsort -gr
แทนการsort -g
ย้อนกลับลำดับการจัดเรียง:
$ printf '12.45\n10.35\n' | sort -gr | head -1
12.45
ในการใช้การเปรียบเทียบ<
("น้อยกว่า") ดังนั้นจึงสามารถใช้ในif
ฯลฯ เปรียบเทียบค่าต่ำสุดกับค่าใดค่าหนึ่ง หากค่าต่ำสุดเท่ากับค่าเมื่อเปรียบเทียบกับข้อความจะน้อยกว่าค่าอื่น ๆ :
$ a=12.45; b=10.35
$ [ "$a" = "$(printf "$a\n$b\n" | sort -g | head -1)" ]
$ echo $?
1
$ a=12.45; b=100.35
$ [ "$a" = "$(printf "$a\n$b\n" | sort -g | head -1)" ]
$ echo $?
0
a == min(a, b)
a <= b
มันน่าสังเกตว่านี่ไม่ได้ตรวจสอบอย่างเคร่งครัดน้อยกว่า หากคุณต้องการทำเช่นนั้นคุณต้องตรวจสอบa == min(a, b) && a != max(a, b)
ในคำอื่น ๆa <= b and not a >= b
เพียงแค่ใช้ksh
( ksh93
แม่นยำ) หรือzsh
ซึ่งทั้งสองสนับสนุนการคำนวณเลขทศนิยม:
$ cat test.ksh
#!/bin/ksh
min=12.45
val=10.35
if (( $val < $min )) ; then
min=$val
fi
echo "$min"
$ ./test.ksh
10.35
แก้ไข: ขออภัยฉันพลาดksh93
ไปแล้วแนะนำ การรักษาคำตอบของฉันเพียงเพื่อให้ชัดเจนสคริปต์ที่โพสต์ในคำถามเปิดสามารถใช้โดยไม่มีการเปลี่ยนแปลงนอกเชลล์สวิทช์
แก้ไข 2: โปรดทราบว่าksh93
ต้องใช้เนื้อหาตัวแปรให้สอดคล้องกับสถานที่ของคุณเช่นกับสถานที่ภาษาฝรั่งเศสต้องใช้เครื่องหมายจุลภาคแทนที่จะเป็นจุด:
...
min=12,45
val=10,35
...
โซลูชันที่มีประสิทธิภาพมากขึ้นคือการตั้งค่าภาษาที่จุดเริ่มต้นของสคริปต์เพื่อให้แน่ใจว่ามันจะทำงานโดยไม่คำนึงถึงภาษาของผู้ใช้:
...
export LC_ALL=C
min=12.45
val=10.35
...
.
(ไม่ใช่ครึ่งโลกที่มีตัวคั่นทศนิยม,
) zsh
ไม่มีปัญหานั้น
LC_ALL
ซึ่งหมายความว่าจะไม่แสดงตัวเลข (หรืออินพุต) ในรูปแบบที่ผู้ใช้ต้องการ ดูunix.stackexchange.com/questions/87745/what-does-lc-all-c-do/…สำหรับแนวทางที่ดีกว่า
.
แล้ว
min=$(echo "${min}sa ${val}d la <a p" | dc)
ที่ใช้dc
เครื่องคิดเลขเพื่อs
ฉีกค่าสำหรับ$min
ในการลงทะเบียนa
และd
uplicates ค่าของ$val
ลงบนด้านบนของกองดำเนินการหลักของมัน จากนั้นl
ists เนื้อหาของa
ลงบนด้านบนของสแต็ก ณ จุดที่มีลักษณะ:
${min} ${val} ${val}
<
ปรากฏด้านบนสองรายการออกจากสแต็คและเปรียบเทียบพวกเขา ดังนั้นสแต็กจะมีลักษณะดังนี้:
${val}
หากรายการบนสุดมีค่าน้อยกว่ารายการที่สองขึ้นไปรายการจะส่งเนื้อหาของa
ไปยังด้านบนดังนั้นสแต็กจะมีลักษณะดังนี้:
${min} ${val}
ไม่เช่นนั้นจะทำอะไรและสแต็กยังคงมีลักษณะ:
${val}
จากนั้นมันก็จะp
ลบรายการสแต็คด้านบน
ดังนั้นสำหรับปัญหาของคุณ:
min=12.45
val=12.35
echo "${min}sa ${val}d la <a p" | dc
###OUTPUT
12.35
แต่:
min=12.45
val=12.55
echo "${min}sa ${val}d la <a p" | dc
###OUTPUT
12.45
ทำไมไม่ใช้ของเก่าดีexpr
ล่ะ?
ไวยากรณ์ตัวอย่าง:
if expr 1.09 '>' 1.1 1>/dev/null; then
echo 'not greater'
fi
สำหรับนิพจน์ที่แท้จริงรหัสออกจาก expr คือ 0 โดยส่งสตริง '1' ไปที่ stdout กลับด้านสำหรับนิพจน์เท็จ
ฉันได้ตรวจสอบกับ GNU และ FreeBSD 8 expr แล้ว
expr 1.09 '<' -1.1
จะพิมพ์1
และออกด้วย0
(สำเร็จ)
ในการตรวจสอบว่ามีสองหมายเลข (อาจเป็นเศษส่วน) ในลำดับหรือsort
ไม่เป็นแบบพกพา (พอสมควร):
min=12.45
val=12.55
if { echo $min ; echo $val ; } | sort -n -c 2>/dev/null
then
echo min is smallest
else
echo val is smallest
fi
if
แต่ถ้าคุณต้องการจริงที่จะให้ค่าต่ำสุดปรับปรุงแล้วคุณไม่จำเป็นต้อง จัดเรียงตัวเลขและใช้หนึ่ง (อย่างน้อย) แรก:
min=12.45
val=12.55
smallest=$({ echo $min ; echo $val ; } | sort -n | head -n 1)
echo $smallest
min=$smallest
ฉันมักจะทำสิ่งที่คล้ายกับรหัสหลามฝังตัว:
#!/bin/sh
min=12.45
val=10.35
python - $min $val<<EOF
if ($min > $val):
print $min
else:
print $val
EOF
$ min=12.45
$ val=10.35
$ [ "$min" \< "$val" ] && echo $val || echo $min
$ 12.45
$ val=13
$ [ "$min" \< "$val" ] && echo $val || echo $min
$ 13
0.5
และ0.06
) คุณควรใช้เครื่องมือที่เข้าใจสัญลักษณ์ทศนิยมอยู่แล้ว