बैश लूप में काउंटर इंक्रीमेंट काम नहीं कर रहा है


125

मेरे पास निम्नलिखित सरल स्क्रिप्ट है जहां मैं एक लूप चला रहा हूं और एक बनाए रखना चाहता हूं COUNTER। मैं यह पता लगाने में असमर्थ हूं कि काउंटर अपडेट क्यों नहीं कर रहा है। यह सबटाइटल thats के कारण बनाया जा रहा है? मैं संभावित रूप से इसे कैसे ठीक कर सकता हूं?

#!/bin/bash

WFY_PATH=/var/log/nginx
WFY_FILE=error.log
COUNTER=0
grep 'GET /log_' $WFY_PATH/$WFY_FILE | grep 'upstream timed out' | awk -F ', ' '{print $2,$4,$0}' | awk '{print "http://domain.com"$5"&ip="$2"&date="$7"&time="$8"&end=1"}' | awk -F '&end=1' '{print $1"&end=1"}' |
(
while read WFY_URL
do
    echo $WFY_URL #Some more action
    COUNTER=$((COUNTER+1))
done
)

echo $COUNTER # output = 0


लूप को सबशेल में रखने की जरूरत नहीं है। केवल लूप के चारों ओर कोष्ठक हटा दें, यह पर्याप्त है। या फिर यदि आपको इसे लूप सबस्क्रिप्शन में डालना है, तो ऐसा करने के बाद, काउंटर को एक बार अस्थायी फ़ाइल में डंप करें, और इस फाइल को सबस्क्रिप्शन से बाहर पुनर्स्थापित करें। मैं जवाब में आपको अंतिम प्रक्रिया तैयार करूंगा।
ज़नीक

जवाबों:


156

पहला, आप काउंटर नहीं बढ़ा रहे हैं। बदलने COUNTER=$((COUNTER))में COUNTER=$((COUNTER + 1))या COUNTER=$[COUNTER + 1]यह वृद्धि होगी।

दूसरा, यह सरलीकरण के रूप में आप को शांत करने के लिए चर चर वापस फैलाने के लिए मुश्किल है। एक सब-वेरिएबल में सब-सब्सक्रिप्शन के बाहर उपलब्ध नहीं हैं। ये बच्चे की प्रक्रिया के लिए स्थानीय चर हैं।

इसे हल करने का एक तरीका मध्यवर्ती मूल्य को संग्रहीत करने के लिए एक अस्थायी फ़ाइल का उपयोग करना है:

TEMPFILE=/tmp/$$.tmp
echo 0 > $TEMPFILE

# Loop goes here
  # Fetch the value and increase it
  COUNTER=$[$(cat $TEMPFILE) + 1]

  # Store the new value
  echo $COUNTER > $TEMPFILE

# Loop done, script done, delete the file
unlink $TEMPFILE

30
$ [...] पदावनत है।
Chepner

1
@chepner क्या आपके पास एक संदर्भ है जो कहता $[...]है कि पदावनत किया गया है? क्या कोई वैकल्पिक उपाय है?
BLong

9
$[...]द्वारा इस्तेमाल किया गया था bashइससे पहले कि $((...))POSIX खोल द्वारा अपनाया गया था। मुझे यकीन नहीं है कि यह कभी औपचारिक रूप से पदावनत किया गया था, लेकिन मैं bashमैन पेज में इसका कोई उल्लेख नहीं पा सकता हूं , और यह केवल पश्चगामी संगतता के लिए समर्थित प्रतीत होता है।
chepner

इसके अलावा, $ (...) को अधिक पसंद किया जाता है...
लेननर्ट रोलैंड

7
@blong यहां $ [...] बनाम $ (...) पर एक एसओ प्रश्न है, जो चर्चा और संदर्भ में
विक्षेपण करता है

87
COUNTER=1
while [ Your != "done" ]
do
     echo " $COUNTER "
     COUNTER=$[$COUNTER +1]
done

टेस्टेड बैश: सेंटोस, एसयूएसई, आरएच


1
@kroonwijk को वर्ग कोष्ठक से पहले एक स्थान होना चाहिए (शब्दों को 'परिसीमित करना', औपचारिक रूप से बोलना)। बैश अन्यथा पिछली अभिव्यक्ति का अंत नहीं देख सकता है।
एडवर्डगार्सन

1
प्रश्न एक पाइप के साथ थोड़ी देर के बारे में थे, इसलिए जहां एक सबहेल बनाया गया है, आपका उत्तर सही है लेकिन आप पाइप का उपयोग नहीं करते हैं, इसलिए यह प्रश्न का उत्तर नहीं दे रहा है
chrisweb

2
एक और जवाब पर चेपनर की टिप्पणी के अनुसार, $[ ]वाक्यविन्यास को हटा दिया गया है। stackoverflow.com/questions/10515964/…
मार्क Haferkamp

यह मुख्य प्रश्न को हल नहीं करता है, मुख्य लूप को सब
रील के

42
COUNTER=$((COUNTER+1)) 

आधुनिक प्रोग्रामिंग में काफी अनाड़ी निर्माण है।

(( COUNTER++ ))

अधिक "आधुनिक" लगता है। आप भी उपयोग कर सकते हैं

let COUNTER++

अगर आपको लगता है कि पठनीयता में सुधार होता है। कभी-कभी, बैश चीजों को करने के बहुत सारे तरीके देता है - पर्ल दर्शन मुझे लगता है - जब शायद पायथन "इसे करने का केवल एक सही तरीका है" अधिक उपयुक्त हो सकता है। यह एक बहस का मुद्दा है अगर कभी कोई था! वैसे भी, मैं सुझाव दूंगा कि (इस मामले में) सिर्फ एक चर को बढ़ाना नहीं है, बल्कि (सामान्य नियम) भी कोड लिखना है जिसे कोई और समझ सकता है और समर्थन कर सकता है। अनुरूपता एक लंबा रास्ता तय करती है।

HTH


यह मूल प्रश्न को संबोधित नहीं करता है, जो कि (उप-प्रक्रिया) लूप को समाप्त करने के बाद काउंटर में अद्यतन मान प्राप्त करने के लिए है
लुइस वाज़केज़

16

उपयोग करने का प्रयास करें

COUNTER=$((COUNTER+1))

के बजाय

COUNTER=$((COUNTER))

8
या बसlet "COUNTER++"
सुस्त

2
क्षमा करें, यह एक टाइपो था। इसका वास्तव में ((COUNTER + 1))
Sparsh Gupta

8
@AaronDigulla: (( COUNTER++ ))(कोई डॉलर का संकेत नहीं)
अगली सूचना तक

2
मुझे यकीन नहीं है कि मैं क्यों देख रहा हूं, लेकिन मेरा एक स्क्रिप्ट बार-बार उपयोग करते समय विफल रहता है (( COUNTER++ ))लेकिन जब मैंने COUNTER=$((COUNTER + 1))इसे स्विच किया तो काम किया। GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)
स्टीवन लू

हो सकता है कि आपकी हैश बैंग लाइन / बिन / बैश के बजाय बैश / बिन / श के रूप में चलती हो?
मैक्स

12

मुझे लगता है कि यह एकल awk कॉल आपके बराबर है grep|grep|awk|awk पाइपलाइन के : कृपया इसका परीक्षण करें। आपकी अंतिम awk कमांड कुछ भी नहीं बदलती प्रतीत होती है।

COUNTER के साथ समस्या यह है कि जबकि लूप एक सबशेल में चल रहा है, इसलिए सबमिशन से बाहर आने पर वेरिएबल में कोई बदलाव गायब हो जाता है। आपको उसी उपधारा में COUNTER के मान तक पहुँचने की आवश्यकता है। या @ डेनिसविलियम्सन की सलाह लें, एक प्रक्रिया प्रतिस्थापन का उपयोग करें, और पूरी तरह से उपखंड से बचें।

awk '
  /GET \/log_/ && /upstream timed out/ {
    split($0, a, ", ")
    split(a[2] FS a[4] FS $0, b)
    print "http://example.com" b[5] "&ip=" b[2] "&date=" b[7] "&time=" b[8] "&end=1"
  }
' | {
    while read WFY_URL
    do
        echo $WFY_URL #Some more action
        (( COUNTER++ ))
    done
    echo $COUNTER
}

1
धन्यवाद, अंतिम awk मूल रूप से अंत = 1 के बाद सब कुछ हटा देगा और अंत में एक नया अंत = 1 डाल देगा (ताकि अगली बार हम वह सब कुछ हटा सकें जो इसके बाद जोड़ा जाता है)।
आदर्श गुप्ता

1
@SparshGupta, पिछले awk "अंत = 1" के बाद कुछ भी प्रिंट नहीं करता है।
ग्लेन जैकमैन

यह प्रश्न पटकथा के लिए बहुत अच्छा सुधार करता है, लेकिन
उपधारा के


11

एक अस्थायी फ़ाइल का उपयोग करने के बजाय, आप whileप्रक्रिया प्रतिस्थापन का उपयोग करके लूप के चारों ओर एक उप- संस्करण बनाने से बच सकते हैं ।

while ...
do
   ...
done < <(grep ...)

वैसे, आपको उस सभी grep, grep, awk, awk, awkको एक में बदलने में सक्षम होना चाहिए awk

बैश 4.2 के साथ शुरू, वहाँ एक lastpipeविकल्प है कि

वर्तमान शेल संदर्भ में पाइप लाइन की अंतिम कमांड चलाता है। यदि नौकरी नियंत्रण सक्षम है, तो अंतिम विकल्प का कोई प्रभाव नहीं है।

bash -c 'echo foo | while read -r s; do c=3; done; echo "$c"'

bash -c 'shopt -s lastpipe; echo foo | while read -r s; do c=3; done; echo "$c"'
3

प्रक्रिया प्रतिस्थापन बहुत अच्छा है यदि आप लूप के अंदर एक काउंटर को बढ़ाना चाहते हैं और इसे बाहर का उपयोग करते हैं, जब किया जाता है, तो प्रक्रिया प्रतिस्थापन के साथ समस्या यह है कि मुझे निष्पादित कमांड का स्थिति कोड प्राप्त करने का कोई तरीका नहीं मिला, जो कि पाइप का उपयोग करते समय संभव है। $ {PIPESTATUS [*]} का उपयोग करके
chrisweb

@chrisweb: मैंने इसके बारे में जानकारी जोड़ी lastpipe। वैसे, आपको शायद "${PIPESTATUS[@]}"(तारांकन के बजाय) का उपयोग करना चाहिए ।
अगली सूचना तक रोक दिया गया।

शुद्धिपत्र। bash में (पर्ल में नहीं जैसा कि मैंने पहले गलती से लिखा है) एग्जिट कोड एक टेबल है, तो आप पाइप चेन में सेपरेटली सभी एग्जिट कोड चेक कर सकते हैं। पहले परीक्षण से पहले आपके चरण को इस तालिका की प्रतिलिपि बनाना होगा, अन्यथा पहले आदेश के बाद आप सभी मान खो देंगे।
ज़नीक

यह वह समाधान है जो चर के मूल्य को संग्रहीत करने के लिए मेरे लिए और बाहरी फ़ाइल का उपयोग किए बिना काम करता है जो कि मेरी राय में बहुत अधिक पैदल यात्री है।
लुइस वाज़क्वेज़

8

minimalist

counter=0
((counter++))
echo $counter

सरल एक :-)। धन्यवाद @geekzspot
हुसैन के

प्रश्न में उदाहरण के लिए काम नहीं करता है, क्योंकि
उप

3

आपको बस इतना करना है:

$((COUNTER++))

यहाँ बैश शेल , 3 डी संस्करण, पीपी 147, 148 सीखने का एक अंश दिया गया है :

बैश अंकगणित के भाव जावा और सी भाषाओं में उनके समकक्षों के बराबर हैं। [९] सी। तालिका में 6-2 के रूप में वरीयता और सहकारिता समान हैं, जो समर्थित हैं अंकगणितीय ऑपरेटरों को दर्शाता है। हालाँकि इनमें से कुछ (या सम्‍मिलित) विशेष वर्ण हैं, फिर भी इनसे बचने की कोई आवश्यकता नहीं है, क्योंकि वे $ ((...)) सिंटैक्स के भीतर हैं।

..........................

++ और - ऑपरेटर तब उपयोगी होते हैं जब आप एक मूल्य बढ़ाना या बढ़ाना चाहते हैं। [११] वे जावा और सी के समान काम करते हैं, उदाहरण के लिए, मूल्य ++ वेतन वृद्धि मूल्य 1 से। इसे पोस्ट-इंक्रीमेंट कहा जाता है ; एक पूर्व वेतन वृद्धि भी है : ++ मान । अंतर एक उदाहरण से स्पष्ट हो जाता है:

$ i=0
$ echo $i
0
$ echo $((i++))
0
$ echo $i
1
$ echo $((++i))
2
$ echo $i
2

Http://www.safaribooksonline.com/a/learning-the-bash/7572399/ देखें


यह मेरे लिए आवश्यक संस्करण है, क्योंकि मैं इसे एक ifबयान की स्थिति में उपयोग कर रहा था : if [[ $((needsComma++)) -gt 0 ]]; then printf ',\n'; fi सही या गलत, यह एकमात्र संस्करण है जो मज़बूती से काम करता है।
रास

इस फॉर्म के बारे में यह महत्वपूर्ण है कि आप एक कदम में एक वृद्धि का उपयोग कर सकते हैं। i=1; while true; do echo $((i++)); sleep .1; done
ब्रूनो ब्रोंस्की

1
@LS: if (( needsComma++ > 0 )); thenयाif (( needsComma++ )); then
अगली सूचना तक

बाश में "इको $ ((i ++))" का उपयोग करके मुझे हमेशा "/opt/xyz/init.sh: लाइन 29: i: कमांड मिला" नहीं है जो मैं गलत कर रहा हूं?
mmo

यह लूप के बाहर काउंटर मान प्राप्त करने के बारे में प्रश्न को संबोधित नहीं करता है।
लुइस वाज़क्वेज़

1

यह एक सरल उदाहरण है

COUNTER=1
for i in {1..5}
do   
   echo $COUNTER;
   //echo "Welcome $i times"
   ((COUNTER++));    
done

1
सरल उदाहरण है, लेकिन सवाल करने के लिए उपयुक्त नहीं है।
ज़निक

0

ऐसा लगता है कि आपने अपडेट नहीं किया counterहै यह स्क्रिप्ट, उपयोग हैcounter++


टाइपो के लिए माफी, मैं वास्तव में स्क्रिप्ट में ((COUNTER + 1)) का उपयोग कर रहा हूं जो काम नहीं कर रहा है
गुप्ता

यह कोई बात नहीं है यह मान + 1, या मान ++ द्वारा बढ़ाई गई है। सदस्यता समाप्त होने के बाद, काउंटर वैल्यू खो जाती है, और इस स्क्रिप्ट पर शुरू में सेट किए गए प्रारंभिक 0 मान पर वापस लौटें।
ज़नीक

0

दो स्थितियाँ थीं जिनके कारण अभिव्यक्ति ((var++))मेरे लिए असफल रही:

  1. अगर मैं सख्त मोड ( set -euo pipefail) को बैश करता हूं और अगर मैं शून्य (0) पर अपना वेतन वृद्धि शुरू करता हूं ।

  2. एक (1) से शुरू करना ठीक है लेकिन शून्य "+" का मूल्यांकन करने के लिए वेतन वृद्धि का कारण बनता है जब "++" का मूल्यांकन किया जाता है जो सख्त मोड में गैर-शून्य रिटर्न कोड विफलता है।

मैं या तो इस व्यवहार से बच सकता हूं ((var+=1))या var=$((var+1))बच सकता हूं


0

स्रोत स्क्रिप्ट में कुछ समस्याएँ हैं। पहला उदाहरण, आपको शायद सब्सक्रिप्शन की जरूरत नहीं है। लेकिन हमें नहीं पता कि "कुछ और कार्रवाई" के तहत क्या छिपा हुआ है। सबसे लोकप्रिय उत्तर में छिपा हुआ बग है, जो I / O को बढ़ाएगा, और उप-संस्करण के साथ काम नहीं करेगा, क्योंकि यह लूप के अंदर कूप्टर को पुनर्स्थापित करता है।

'' '' चिन्ह को न जोड़ें, यह रेखा की निरंतरता के बारे में दुभाषिया को सूचित करेगा। मुझे उम्मीद है कि यह आपकी या किसी की मदद करेगा। लेकिन मेरी राय में इस स्क्रिप्ट को पूरी तरह से AWK स्क्रिप्ट में बदल दिया जाना चाहिए, या फिर regexp, या perl का उपयोग करके अजगर को फिर से लिखा जाए, लेकिन वर्षों में पर्ल लोकप्रियता कम हो जाती है। बेहतर यह अजगर के साथ करते हैं।

बिना सबस्क्रिप्शन के सही किया गया संस्करण:

#!/bin/bash
WFY_PATH=/var/log/nginx
WFY_FILE=error.log
COUNTER=0
grep 'GET /log_' $WFY_PATH/$WFY_FILE | grep 'upstream timed out' |\
awk -F ', ' '{print $2,$4,$0}' |\
awk '{print "http://example.com"$5"&ip="$2"&date="$7"&time="$8"&end=1"}' |\
awk -F '&end=1' '{print $1"&end=1"}' |\
#(  #unneeded bracket
while read WFY_URL
do
    echo $WFY_URL #Some more action
    COUNTER=$((COUNTER+1))
done
# ) unneeded bracket

echo $COUNTER # output = 0

यदि यह वास्तव में आवश्यक है, तो उप-संस्करण के साथ संस्करण

#!/bin/bash

TEMPFILE=/tmp/$$.tmp  #I've got it from the most popular answer
WFY_PATH=/var/log/nginx
WFY_FILE=error.log
COUNTER=0
grep 'GET /log_' $WFY_PATH/$WFY_FILE | grep 'upstream timed out' |\
awk -F ', ' '{print $2,$4,$0}' |\
awk '{print "http://example.com"$5"&ip="$2"&date="$7"&time="$8"&end=1"}' |\
awk -F '&end=1' '{print $1"&end=1"}' |\
(
while read WFY_URL
do
    echo $WFY_URL #Some more action
    COUNTER=$((COUNTER+1))
done
echo $COUNTER > $TEMPFILE  #store counter only once, do it after loop, you will save I/O
)

COUNTER=$(cat $TEMPFILE)  #restore counter
unlink $TEMPFILE
echo $COUNTER # output = 0
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.