बैश में दो फ्लोटिंग पॉइंट नंबरों की तुलना कैसे करें?


156

मैं दो अस्थायी बिंदु संख्याओं को बैश स्क्रिप्ट के भीतर तुलना करने की कोशिश कर रहा हूं। मैं चर, उदाहरण के लिए है

let num1=3.17648e-22
let num2=1.5

अब, मैं बस इन दो नंबरों की एक सरल तुलना करना चाहता हूं:

st=`echo "$num1 < $num2" | bc`
if [ $st -eq 1]; then
  echo -e "$num1 < $num2"
else
  echo -e "$num1 >= $num2"
fi

दुर्भाग्य से, मुझे num1 के सही उपचार के साथ कुछ समस्याएं हैं जो "ई-प्रारूप" की हो सकती हैं। :(

कोई मदद, संकेत स्वागत है!


2
"ई-प्रारूप" से मेरा मतलब है घातीय संकेतन (जिसे वैज्ञानिक संकेतन भी कहा जाता है)
जोनास

जवाबों:


181

अधिक आसानी से

यह बैश के संख्यात्मक संदर्भ का उपयोग करके अधिक आसानी से किया जा सकता है:

if (( $(echo "$num1 > $num2" |bc -l) )); then
  
fi

व्याख्या

बुनियादी कैलकुलेटर कमांड के माध्यम से पाइपिंग bc1 या 0 पर वापस आती है।

विकल्प -lके बराबर है --mathlib; यह मानक गणित पुस्तकालय को लोड करता है।

डबल कोष्ठक के बीच पूरी अभिव्यक्ति (( ))को शामिल करने से इन मूल्यों को क्रमशः सही या गलत में बदल दिया जाएगा।

कृपया, सुनिश्चित करें कि bcमूल कैलकुलेटर पैकेज स्थापित है।

यह समान रूप से वैज्ञानिक प्रारूप में तैरने के लिए काम करता है, बशर्ते कि एक बड़े अक्षर Eको नियोजित किया जाए, जैसेnum1=3.44E6


1
के रूप में ही मुद्दा stackoverflow.com/questions/8654051/... जैसे $ गूंज "1.1 + 2 ई +02" | bc (standard_in) 1: सिंटैक्स त्रुटि
निमो

1
@MohitArora कृपया, सुनिश्चित करें कि आपके पास bcकैलकुलेटर पैकेज स्थापित है।
सर्ज स्ट्रोबंड्ट

1
मैं 0: not foundबयान के साथ मिलता हूं if (( $(echo "$TOP_PROCESS_PERCENTAGE > $THRESHOLD" | bc -l) )); then
स्टीफन

1
उन सभी जो मिलता है "कमांड नहीं मिला" के लिए, आपको शामिल करनी याद bcया तो बैकटिक या में $()में तो और (( ))... यानी (( $(bc -l<<<"$a>$b") ))और नहीं (( bc -l<<<"$a>$b" ))
Normadize

@ नीमो एक कैपिटल लेटर के साथ साइंटिफिक नोटेशन में नंबर लिखें E, और सभी सिंटैक्स एरर हो जाएंगे।
सर्ज स्ट्रोबंड्ट

100

बैश केवल पूर्णांक गणित को संभालता है लेकिन आप bcनिम्नानुसार कमांड का उपयोग कर सकते हैं :

$ num1=3.17648E-22
$ num2=1.5
$ echo $num1'>'$num2 | bc -l
0
$ echo $num2'>'$num1 | bc -l
1

ध्यान दें कि घातांक चिन्ह अपरकेस होना चाहिए


3
हां, लेकिन गलत गणना करने के लिए इसकी गलत गणना करने के लिए वैज्ञानिक नंबर नोटेशन में 'ई' साइन अप करने की जरूरत है और पूर्व-निर्धारित गणित दिनचर्या के लिए बीसी प्रोग्राम के लिए -l फ्लैग का उपयोग करें
alrusdi

2
आपको अपने जवाब में यह बताना चाहिए कि केवल एक समान समाधान पोस्ट करने के बजाय और महत्वपूर्ण अंतर का उल्लेख नहीं करना चाहिए।
डैनियल पर्ससन

4
यह बहुत समान समाधान नहीं है। Alrusdi का समाधान bcउपकरण का उपयोग करता है और यही वह है जो मैं किसी BASH प्रोग्रामर को सुझाऊंगा । BASH टाइपलेस भाषा है। हाँ, यह पूर्णांक अंकगणित कर सकता है, लेकिन फ़्लोटिंग पॉइंट के लिए आपको कुछ बाहरी टूल का उपयोग करना होगा। बीसी सबसे अच्छा है क्योंकि यह वही है जो इसके लिए बना है।
देहलायनिक

8
चूंकि वह इसे एक बयान में उपयोग करने की कोशिश कर रहा है, तो मैं उसे दिखाऊंगा। अगर [$ (... | bc -l) == 1]; फिर ...
रॉबर्ट जैकब्स

27

awkगैर पूर्णांक गणित के लिए उपयोग करना बेहतर है । आप इस बैश उपयोगिता फ़ंक्शन का उपयोग कर सकते हैं:

numCompare() {
   awk -v n1="$1" -v n2="$2" 'BEGIN {printf "%s " (n1<n2?"<":">=") " %s\n", n1, n2}'
}

और इसे कॉल करें:

numCompare 5.65 3.14e-22
5.65 >= 3.14e-22

numCompare 5.65e-23 3.14e-22
5.65e-23 < 3.14e-22

numCompare 3.145678 3.145679
3.145678 < 3.145679

2
मुझे यह जवाब पसंद है, लोग अजीब एस्प शुरुआती से दूर भागते हैं, उन्हें लगता है कि यह वास्तव में कठिन है, मुझे लगता है कि लोगों को घुंघराले ब्रेसिज़ और प्रतीत होने वाली भाषा मिश्रित वाक्य रचना (एक नज़र में) से डर लगता है। और चूंकि awk को बहुत ज्यादा गारंटी दी जाती है कि वह लक्ष्य प्रणाली पर मौजूद है, जैसे bc (निश्चित नहीं कि कौन सा, यदि कोई हो, कभी स्थापित नहीं है)। मुझे बैश स्क्रिप्टिंग पसंद है लेकिन यह कोई फ़्लोटिंग पॉइंट नहीं है, यहां तक ​​कि एक 2 दशमलव स्थान भी नहीं है (मुझे लगता है कि कोई व्यक्ति इसके लिए 'नकली' आवरण लिख सकता है), वास्तव में कष्टप्रद है ...
11

2
प्राचीन काल से शैल लिपियों का उपयोग करना awkऔर bcएक मानक अभ्यास है, मैं कहूंगा कि कुछ विशेषताओं को गोले में कभी नहीं जोड़ा गया है क्योंकि वे awk, bc और अन्य यूनिक्स उपकरणों में उपलब्ध हैं। शेल स्क्रिप्ट में शुद्धता की कोई आवश्यकता नहीं है।
पीयूसीक

1
@WanderingMind ऐसा करने का एक तरीका है कि 0 या 1 पास करना होगा exitताकि Awk परिणाम को एक उचित, मशीन-पठनीय तरीके से वापस शेल में संचार कर सके। if awk -v n1="123.456" -v n2="3.14159e17" 'BEGIN { exit (n1 <= n2) }' /dev/null; then echo bigger; else echo not; fi... हालांकि ध्यान दें कि स्थिति कैसे उलटी है (निकास स्थिति 0 का मतलब शेल की सफलता है)।
त्रिपली

1
सिर्फ क्यों python? आपके पास perlकई लिनक्स / यूनिक्स सिस्टम पर डिफ़ॉल्ट रूप से स्थापित .. भी phpभी
anubhava

1
यह awkसमाधान मेरे मामले में अधिक मजबूत bcहै क्योंकि मुझे नहीं मिला एक कारण के लिए गलत परिणाम देता है।
एमबीआर

22

घातीय संकेतन, अग्रणी या अनुगामी शून्य के बिना तैरने की तुलना के लिए शुद्ध बैश समाधान:

if [ ${FOO%.*} -eq ${BAR%.*} ] && [ ${FOO#*.} \> ${BAR#*.} ] || [ ${FOO%.*} -gt ${BAR%.*} ]; then
  echo "${FOO} > ${BAR}";
else
  echo "${FOO} <= ${BAR}";
fi

तार्किक परिचालकों का आदेश मायने रखता है । पूर्णांक भागों की तुलना संख्याओं के रूप में की जाती है और आंशिक भागों की तुलना जानबूझकर की जाती है। चर इस विधि का उपयोग करके पूर्णांक और भिन्नात्मक भागों में विभाजित होते हैं

फ्लोटर्स की तुलना पूर्णांक (डॉट के बिना) से नहीं करेंगे।


15

आप शर्त के साथ संयुक्त awk का उपयोग कर सकते हैं यदि स्थिति, awk 1 या 0 प्रिंट करेगा और उन लोगों द्वारा व्याख्या की जाएगी यदि सच या गलत के साथ खंड ।

if awk 'BEGIN {print ('$d1' >= '$d2')}'; then
    echo "yes"
else 
    echo "no"
fi

Awk का उपयोग करना बहुत अच्छा है क्योंकि यह फ्लोटिंग पॉइंट नंबरों को संभालने में सक्षम है, लेकिन मैं व्यक्तिगत रूप से सिंटैक्स पसंद करता हूंif (( $(echo $d1 $d2 | awk '{if ($1 > $2) print 1;}') )); then echo "yes"; else echo "no"; fi
डेविड जॉर्ज

7

पैकेज संस्करणों की तुलना करते समय सावधान रहें, जैसे कि अगर grep 2.20 संस्करण 2.6 से अधिक है, तो जाँच करें:

$ awk 'BEGIN { print (2.20 >= 2.6) ? "YES" : "NO" }'
NO

$ awk 'BEGIN { print (2.2 >= 2.6) ? "YES" : "NO" }'
NO

$ awk 'BEGIN { print (2.60 == 2.6) ? "YES" : "NO" }'
YES

मैंने इस तरह की समस्या को ऐसे शेल / awk फ़ंक्शन के साथ हल किया:

# get version of GNU tool
toolversion() {
    local prog="$1" operator="$2" value="$3" version

    version=$($prog --version | awk '{print $NF; exit}')

    awk -vv1="$version" -vv2="$value" 'BEGIN {
        split(v1, a, /\./); split(v2, b, /\./);
        if (a[1] == b[1]) {
            exit (a[2] '$operator' b[2]) ? 0 : 1
        }
        else {
            exit (a[1] '$operator' b[1]) ? 0 : 1
        }
    }'
}

if toolversion grep '>=' 2.6; then
   # do something awesome
fi

एक डेबियन-आधारित प्रणाली पर, dpkg --compare-versionsअक्सर उपयोगी होता है। इसमें निर्मित डेबियन पैकेज संस्करणों की तुलना करने के लिए इसके पास पूर्ण तर्क हैं, जो कि केवल की तुलना में अधिक जटिल हैं x.y
नील मैय्यू

5

बेशक, अगर आपको वास्तव में फ्लोटिंग-पॉइंट अंकगणित की आवश्यकता नहीं है, तो उदाहरणार्थ डॉलर के मान पर अंकगणित। जहाँ हमेशा दो दशमलव अंक होते हैं, आप बस डॉट को छोड़ सकते हैं (प्रभावी रूप से 100 से गुणा कर सकते हैं) और परिणामी पूर्णांक की तुलना करें।

if [[ $((10#${num1/.})) < $((10#${num2/.})) ]]; then
    ...

यह स्पष्ट रूप से आपको यह सुनिश्चित करने की आवश्यकता है कि दोनों मानों में दशमलव स्थानों की संख्या समान है।


3

मैंने यहाँ से उत्तरों का उपयोग किया और उन्हें एक फ़ंक्शन में रखा, आप इसे इस तरह से उपयोग कर सकते हैं:

is_first_floating_number_bigger 1.5 1.2
result="${__FUNCTION_RETURN}"

एक बार बुलाया, इस मामले में echo $resultहोगा 1, अन्यथा0

कार्यक्रम:

is_first_floating_number_bigger () {
    number1="$1"
    number2="$2"

    [ ${number1%.*} -eq ${number2%.*} ] && [ ${number1#*.} \> ${number2#*.} ] || [ ${number1%.*} -gt ${number2%.*} ];
    result=$?
    if [ "$result" -eq 0 ]; then result=1; else result=0; fi

    __FUNCTION_RETURN="${result}"
}

या डीबग आउटपुट के साथ एक संस्करण:

is_first_floating_number_bigger () {
    number1="$1"
    number2="$2"

    echo "... is_first_floating_number_bigger: comparing ${number1} with ${number2} (to check if the first one is bigger)"

    [ ${number1%.*} -eq ${number2%.*} ] && [ ${number1#*.} \> ${number2#*.} ] || [ ${number1%.*} -gt ${number2%.*} ];
    result=$?
    if [ "$result" -eq 0 ]; then result=1; else result=0; fi

    echo "... is_first_floating_number_bigger: result is: ${result}"

    if [ "$result" -eq 0 ]; then
        echo "... is_first_floating_number_bigger: ${number1} is not bigger than ${number2}"
    else
        echo "... is_first_floating_number_bigger: ${number1} is bigger than ${number2}"
    fi

    __FUNCTION_RETURN="${result}"
}

फ़ंक्शन को एक अलग .shफ़ाइल में सहेजें और इसे इस तरह शामिल करें:

. /path/to/the/new-file.sh

3

मैं इसे https://stackoverflow.com/a/56415379/1745001 के उत्तर के रूप में पोस्ट कर रहा था, जब यह इस प्रश्न के एक भाग के रूप में बंद हो गया, तो यह यहाँ भी लागू होता है:

सादगी और स्पष्टता के लिए बस गणना के लिए awk का उपयोग करें क्योंकि यह एक मानक UNIX उपकरण है और इसलिए सिंटैक्टिक रूप से काम करने के लिए bc के रूप में मौजूद होने और बहुत आसान होने की संभावना है।

इस सवाल के लिए:

$ cat tst.sh
#!/bin/bash

num1=3.17648e-22
num2=1.5

awk -v num1="$num1" -v num2="$num2" '
BEGIN {
    print "num1", (num1 < num2 ? "<" : ">="), "num2"
}
'

$ ./tst.sh
num1 < num2

और उस अन्य प्रश्न के लिए जिसे इस एक के रूप में बंद कर दिया गया था:

$ cat tst.sh
#!/bin/bash

read -p "Operator: " operator
read -p "First number: " ch1
read -p "Second number: " ch2

awk -v ch1="$ch1" -v ch2="$ch2" -v op="$operator" '
BEGIN {
    if ( ( op == "/" ) && ( ch2 == 0 ) ) {
        print "Nope..."
    }
    else {
        print ch1 '"$operator"' ch2
    }
}
'

$ ./tst.sh
Operator: /
First number: 4.5
Second number: 2
2.25

$ ./tst.sh
Operator: /
First number: 4.5
Second number: 0
Nope...

@ ड्यूडीबॉय नहीं, यह स्पष्ट, सरल, पोर्टेबल awk कोड या गैर-स्पष्ट, अस्पष्ट, शेल-आश्रित शेल + बीओडी कोड है।
एड मॉर्टन

3

awk और उपकरण इसे पसंद करते हैं (मैं आपको घूर रहा हूं) sed ...) को पुरानी परियोजनाओं के डस्टबिन में फिर से शामिल किया जाना चाहिए, इस कोड के साथ कि हर कोई छूने से बहुत डरता है क्योंकि यह एक कभी-कभी भाषा में नहीं लिखा गया था।

या आप अपेक्षाकृत दुर्लभ प्रोजेक्ट हैं, जिसमें कोड रखरखाव अनुकूलन पर सीपीयू उपयोग अनुकूलन को प्राथमिकता देने की जरूरत है ... जिस स्थिति में, जारी है।

यदि नहीं, तो क्यों, इसके बजाय केवल पठनीय और स्पष्ट कुछ का उपयोग न करें, जैसे कि python? आपके साथी कोडर और भविष्य के स्वयं आपको धन्यवाद देंगे। आप pythonअन्य सभी की तरह ही बैश के साथ इनलाइन का उपयोग कर सकते हैं।

num1=3.17648E-22
num2=1.5
if python -c "exit(0 if $num1 < $num2 else 1)"; then
    echo "yes, $num1 < $num2"
else
    echo "no, $num1 >= $num2"
fi

@Witiko मेरा मूल संस्करण थोड़ा स्नार्कियर था।
CivFan

यहां तक ​​कि अधिक रसीला: के not(...)बजाय का उपयोग करें0 if ... else 1
नील मैय्यू

1
यदि आप awk और sed (मैं CivFan को देख रहा हूँ) को इतिहास के कूड़ेदान में जमा कर रहे हैं, तो आप एक घटिया सिस्टम प्रशासक हैं और आप बहुत अधिक कोड टाइप कर रहे हैं। (और मैं अजगर को पसंद करता हूं और उपयोग करता हूं, इसलिए यह उस बारे में नहीं है)। -1 गलत स्नार्कनेस के लिए। उन उपकरणों के लिए सिस्टम डोमेन में एक जगह है, पायथन या नहीं।
माइक एस

1
दिलचस्प है, मैं अच्छे राजभाषा पर्ल के साथ समाप्त हुआ! awk '${print $5}' ptpd_log_file | perl -ne '$_ > 0.000100 && print' > /tmp/outfile। बहुत आसान। हर भाषा का अपना स्थान होता है।
माइक एस

1
सेडान सेनिक वैक्सनेस के साथ गांठ जागना नहीं है। अजगर के विपरीत, awk प्रत्येक UNIX इंस्टालेशन पर एक अनिवार्य उपयोगिता है और इसके समतुल्य awk python -c "import sys; sys.exit(0 if float($num1) < float($num2) else 1)"बस है awk "BEGIN{exit ($num1 > $num2 ? 0 : 1)}"
एड मॉर्टन

2

यह स्क्रिप्ट मदद कर सकता है जहां मैं जाँच रहा हूँ कि स्थापित grailsसंस्करण न्यूनतम आवश्यकता से अधिक है या नहीं। आशा करता हूँ की ये काम करेगा।

#!/bin/bash                                                                                         

min=1.4                                                                                             
current=`echo $(grails --version | head -n 2 | awk '{print $NF}' | cut -c 1-3)`                         

if [ 1 -eq `echo "${current} < ${min}" | bc` ]                                                          
then                                                                                                
    echo "yo, you have older version of grails."                                                   
else                                                                                                                                                                                                                       
    echo "Hurray, you have the latest version" 
fi

2
num1=0.555
num2=2.555


if [ `echo "$num1>$num2"|bc` -eq 1 ]; then
       echo "$num1 is greater then $num2"
else
       echo "$num2 is greater then $num1"
fi

2

कृपया नीचे संपादित कोड की जाँच करें: -

#!/bin/bash

export num1=(3.17648*e-22)
export num2=1.5

st=$((`echo "$num1 < $num2"| bc`))
if [ $st -eq 1 ]
  then
    echo -e "$num1 < $num2"
  else
    echo -e "$num1 >= $num2"
fi

यह अच्छा काम करता है।


2

अपरकेस और लोअरकेस एक्सपोजर (जैसे, 12.00e4) दोनों के साथ वैज्ञानिक संकेतन सहित सभी संभव संकेतन का समर्थन करने वाला एक समाधान :

if (( $(bc -l <<< "${value1/e/E} < ${value2/e/E}") ))
then
    echo "$value1 is below $value2"
fi 

1

कोर्न शेल का उपयोग करें, बैश में आपको दशमलव भाग की तुलना अलग से करनी पड़ सकती है

#!/bin/ksh
X=0.2
Y=0.2
echo $X
echo $Y

if [[ $X -lt $Y ]]
then
     echo "X is less than Y"
elif [[ $X -gt $Y ]]
then
     echo "X is greater than Y"
elif [[ $X -eq $Y ]]
then
     echo "X is equal to Y"
fi

2
समस्या यह है कि कई वितरण ksh स्थापित के साथ नहीं आते हैं, और यदि आपकी स्क्रिप्ट दूसरों द्वारा उपयोग की जाने वाली है, तो वे अतिरिक्त सामान को स्थापित करना पसंद नहीं करते हैं, खासकर जब इसकी सिर्फ एक स्क्रिप्ट जो बाश में लिखी जानी चाहिए। -सोचिए कि उन्हें ऐसा करने के लिए ANOTHER शेल की आवश्यकता नहीं है, जो पहली बार में एक bash स्क्रिप्ट का उपयोग करने के पूरे कारण को कम करता है - हमें यह भी पता चल सकता है कि C ++ में यह कोड कहां जा सकता है, लेकिन क्यों?
ओसिरिसगोत्रा ​​10

वितरण के बिना आने वाले वितरण क्या हैं?
piokuc

1
उदाहरण के लिए @piokuc, उबंटू डेस्कटॉप और सर्वर। मैं कहूंगा कि यह बहुत बड़ी बात है ...
Olli

इसके अलावा, सवाल विशेष रूप से समाधान के लिए पूछता है जो बैश में काम करता है। उसके लिए वास्तव में अच्छे कारण हो सकते हैं। कहो, यह बड़े अनुप्रयोग का हिस्सा है और ksh के लिए सब कुछ माइग्रेट करना संभव नहीं है। या यह एम्बेडेड प्लेटफॉर्म पर चल रहा है जहां एक और शेल स्थापित करना वास्तव में एक समस्या है।
ओली 12

1

Bashj ( https://sourceforge.net/projects/bashj/ ) का उपयोग करते हुए , जावा सपोर्ट वाला बैश म्यूटेंट, आप बस लिखते हैं (और इसे पढ़ना आसान है):

#!/usr/bin/bashj

#!java
static int doubleCompare(double a,double b) {return((a>b) ? 1 : (a<b) ? -1 : 0);}

#!bashj
num1=3.17648e-22
num2=1.5
comp=j.doubleCompare($num1,$num2)
if [ $comp == 0 ] ; then echo "Equal" ; fi
if [ $comp == 1 ] ; then echo "$num1 > $num2" ; fi
if [ $comp == -1 ] ; then echo "$num2 > $num1" ; fi

बेशक bashj बाश / जावा संकरण अधिक प्रदान करता है ...


0

इस बारे में कैसा है? = D

VAL_TO_CHECK="1.00001"
if [ $(awk '{printf($1 >= $2) ? 1 : 0}' <<<" $VAL_TO_CHECK 1 ") -eq 1 ] ; then
    echo "$VAL_TO_CHECK >= 1"
else
    echo "$VAL_TO_CHECK < 1"
fi

1
अक्क स्क्रिप्ट को केवल exit 0सत्य की रिपोर्ट करना चाहिए और exit 1झूठी लौटना चाहिए ; फिर आप उल्लेखनीय रूप से सुरुचिपूर्ण को सरल कर सकते हैं if awk 'BEGIN { exit (ARGV[1] >= ARGV[2]) ? 0 : 1 }' "$VAL_TO_CHECK" 1; then... (अधिक सुरुचिपूर्ण अभी भी यदि आप एक शेल फ़ंक्शन में Awk स्क्रिप्ट को एन्क्रिप्ट करते हैं)।
त्रिवेणी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.