বাশ লুপে কাউন্টার ইনক্রিমেন্ট কাজ করছে না


125

আমার নীচের সরল স্ক্রিপ্টটি রয়েছে যেখানে আমি একটি লুপ চালাচ্ছি এবং একটি বজায় রাখতে চাই COUNTER। কাউন্টার কেন আপডেট হচ্ছে না তা জানার জন্য আমি অক্ষম। এটি কি সাব-শেল থ্যাটস তৈরি হওয়ার কারণে রয়েছে? আমি কীভাবে এটি ঠিক করতে পারি?

#!/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


সাবশেলে লুপ করার সময় আপনাকে লাগানোর দরকার নেই। লুপ চলাকালীন চারদিকে বন্ধনী সরিয়ে ফেলুন, এটি যথেষ্ট। অথবা অন্যথায় যদি আপনাকে অবশ্যই এটি সাবসেলের মধ্যে লুপ স্থাপন করতে হয়, তবে কাজটি শেষ করার পরে একবার অস্থায়ী ফাইলের মধ্যে কাউন্টার ডাম্প করুন এবং সাবস্কেলের বাইরে এই ফাইলটি পুনরুদ্ধার করুন। আমি আপনাকে উত্তর চূড়ান্ত পদ্ধতি প্রস্তুত করব।
Znik

উত্তর:


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
$ [...] অবচিত করা হয়েছে।
চিপনার

1
@ চেপনার আপনার কাছে এমন একটি উল্লেখ রয়েছে যা বলে যে হ্রাস পেয়েছে $[...]? বিকল্প সমাধান আছে কি?
ব্লুং

9
$[...]ব্যবহার করত bashআগে $((...))POSIX শেল দ্বারা গৃহীত হয়। আমি নিশ্চিত নই যে এটি কখনও আনুষ্ঠানিকভাবে অবহেলিত হয়েছিল, তবে আমি bashম্যান পৃষ্ঠায় এর কোনও উল্লেখ পাই না এবং এটি কেবল পিছনের সামঞ্জস্যের জন্য সমর্থনযোগ্য বলে মনে হয়।
চিপনার

এছাড়াও, $ (...) এর চেয়ে বেশি অগ্রাধিকার দেওয়া হয়েছে...
লেনার্ট রোলল্যান্ড

7
এখানে @blong $ একটি কাজেই প্রশ্ন আসে সেই [...] বনাম $ ((...)) যে আলোচনা এবং তথ্যসূত্র থামিয়ে দেওয়া: stackoverflow.com/questions/2415724/...
রাক্ষস Psalm33

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

পরীক্ষিত বাশ: সেন্টোস, সুস, আরএইচ


1
@ ক্রুনউইজকের স্কোয়ার ব্র্যাকেটের আগে একটি জায়গা থাকা দরকার ('শব্দগুলি সীমিত করতে', আনুষ্ঠানিকভাবে বলতে)। বাশ অন্যথায় পূর্বের এক্সপ্রেশনটির শেষ দেখতে পাবে না।
এডওয়ার্ডারসন

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))
স্পর্শ গুপ্ত

8
@ অ্যারোনডিগুল্লা: (( COUNTER++ ))(কোনও ডলারের চিহ্ন নেই)
পরবর্তী বিজ্ঞপ্তি না দেওয়া পর্যন্ত

2
আমি নিশ্চিত না কেন তবে আমি আমার স্ক্রিপ্টটি বারবার দেখছি যখন ব্যবহার করার সময় ব্যর্থ হয় (( COUNTER++ ))তবে আমি যখন COUNTER=$((COUNTER + 1))এটিতে স্যুইচ করি তখন এটি কাজ করে। GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)
স্টিভেন লু

হতে পারে আপনার হ্যাশ ব্যাং লাইনটি / বিন / ব্যাশের পরিবর্তে / বিন / শ হিসাবে বাশ চালায়?
সর্বাধিক

12

আমি মনে করি এই একক অ্যাজক কলটি আপনার 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
ধন্যবাদ, শেষ পাতাগুলি মূলত শেষ = 1 এর পরে সমস্ত কিছু সরিয়ে ফেলবে এবং শেষের দিকে একটি নতুন প্রান্ত = 1 রাখবে (যাতে পরের বারের পরে আমরা এর পরে সংযুক্ত সমস্ত কিছু সরিয়ে ফেলতে পারি)।
স্পর্শ গুপ্ত 9'12

1
@ স্পর্শগুপ্ত, আগের অ্যাডক "শেষ = 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

প্রক্রিয়া প্রতিস্থাপন দুর্দান্ত যদি আপনি লুপের অভ্যন্তরে একটি কাউন্টার বাড়িয়ে তুলতে চান এবং বাইরে হয়ে গেলে এটি ব্যবহার করতে চান, প্রক্রিয়া বিকল্পের সাথে সমস্যাটি হ'ল আমি এক্সিকিউটড কমান্ডের স্ট্যাটাস কোড পাওয়ারও কোনও উপায় খুঁজে পাইনি, যা পাইপ ব্যবহারের সময় সম্ভব {IP পাইপস্ট্যাটাস [*]} ব্যবহার করে
ক্রিসওয়েব

@ ক্রিসওয়েব: আমি সম্পর্কিত তথ্য যুক্ত করেছি lastpipe। যাইহোক, আপনার সম্ভবত ব্যবহার করা উচিত "${PIPESTATUS[@]}"(নক্ষত্রের পরিবর্তে)।
পরবর্তী বিজ্ঞপ্তি না দেওয়া পর্যন্ত বিরতি দেওয়া হয়েছে।

ত্রুটি-বিচ্যুতি। ইন ব্যাশ (ভুল হিসাবে আমি আগে লিখেছি পার্লে নয়) প্রস্থান কোডটি একটি টেবিল, তারপরে আপনি পাইপ শৃঙ্খলে সমস্ত প্রস্থান কোড আলাদাভাবে পরীক্ষা করতে পারেন। প্রথমে পরীক্ষা করার আগে আপনার পদক্ষেপটি অবশ্যই এই টেবিলটি অনুলিপি করা উচিত, অন্যথায় প্রথম আদেশের পরে আপনি সমস্ত মান হারাবেন।
Znik

এই সমাধানটি আমার পক্ষে এবং বহিরাগত ফাইলটি ব্যবহার না করে ভেরিয়েবলের মান সংরক্ষণ করার জন্য কাজ করেছিল যা আমার মতে অনেক পথচারীর।
লুইস ওয়াজকুয়েজ

8

অল্পস্বল্প

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

সাধারণ একটি :-)। ধন্যবাদ @ গিগজস্পট
হুসেন কে

প্রশ্নে উদাহরণস্বরূপ কাজ করে না, কারণ সাবশেল রয়েছে
জিনিক

3

আপনার যা করার দরকার তা হ'ল:

$((COUNTER++))

এখানে বাশ শেল , তৃতীয় সংস্করণ, পৃষ্ঠা 147, 148 শেখার একটি অংশ রয়েছে :

বাশ গাণিতিক এক্সপ্রেশন জাভা এবং সি ভাষায় তাদের সমকক্ষগুলির সমতুল্য [[9] অগ্রাধিকার এবং সাহচর্যতা সি এর মতোই সারণী 6-2 সারণীতে পাটিগণিত অপারেটরগুলি দেখায়। যদিও এর মধ্যে কয়েকটি বিশেষ অক্ষর (বা ধারণ করে) তবে এগুলিকে ব্যাকস্ল্যাশ-পালানোর দরকার নেই, কারণ এগুলি $ ((...)) সিনট্যাক্সের মধ্যে রয়েছে।

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

আপনি যখন একের সাথে একটি মান বাড়িয়ে বা হ্রাস করতে চান তখন ++ এবং - অপারেটরগুলি দরকারী [[11] এগুলি জাভা এবং সি এর মতোই কাজ করে, উদাহরণস্বরূপ, মান ++ ইনক্রিমেন্টের মান ১ দ্বারা। এটিকে পোস্ট-ইনক্রিমেন্ট বলা হয় ; একটি প্রাক-বর্ধনও রয়েছে : ++ মান । পার্থক্য একটি উদাহরণ দিয়ে স্পষ্ট হয়:

$ 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
ব্রুনো ব্রোনোস্কি 21'18 এ 21'১

1
@ এলএস: if (( needsComma++ > 0 )); thenবাif (( needsComma++ )); then
পরবর্তী বিজ্ঞপ্তি হওয়া পর্যন্ত

"ইকো? ((আমি ++))" ব্যাশে আমি সর্বদা "/opt/xyz/init.sh: লাইন 29: i: কমান্ড পাইনি" আমি কী ভুল করছি?
mmo

এটি লুপের বাইরে পাল্টা মান পাওয়ার প্রশ্নটিকে সম্বোধন করে না।
লুইস ওয়াজকেজ


0

দেখে মনে হচ্ছে আপনি আপডেটটি আপডেট করেননি এটি counterহ'ল স্ক্রিপ্ট, ব্যবহারcounter++


টাইপোর জন্য ক্ষমা চাই, আমি আসলে ((COUNTER + 1)) স্ক্রিপ্টে ব্যবহার করছি যা কাজ করছে না
স্পর্শ গুপ্ত

এটি +1 মান বা ++ দ্বারা মান বাড়িয়ে তোলা যায়। সাবহেল সমাপ্ত হওয়ার পরে, পাল্টা মানটি নষ্ট হয়ে যায় এবং এই স্ক্রিপ্টের শুরুতে প্রাথমিক 0 মান সেটটিতে ফিরে আসে।
জিনিক

0

দুটি শর্ত ছিল যা ((var++))আমার জন্য অভিব্যক্তিটি ব্যর্থ করেছিল:

  1. যদি আমি কঠোর মোডে বাশ সেট করি ( set -euo pipefail) এবং যদি আমি আমার বৃদ্ধি শূন্য (0) থেকে শুরু করি ।

  2. এক (1) থেকে শুরু করা ভাল তবে শূন্যের ফলে "++" মূল্যায়ন করার সময় বৃদ্ধি "1" ফিরে আসে যা কঠোর মোডে একটি শূন্য-শূন্য রিটার্ন কোড ব্যর্থতা।

আমি হয় ব্যবহার করতে পারেন ((var+=1))বা var=$((var+1))এই আচরণ থেকে বাঁচতে


0

উত্স স্ক্রিপ্টটিতে সাব-শেল নিয়ে কিছু সমস্যা আছে। প্রথম উদাহরণ, আপনার সম্ভবত সাবশেলের দরকার নেই। তবে আমরা জানি না "আরও কিছু পদক্ষেপ" এর আড়ালে কী লুকানো আছে। সর্বাধিক জনপ্রিয় উত্তরের মধ্যে লুক্কায়িত বাগ রয়েছে যা আই / ও বাড়িয়ে তুলবে এবং সাবশেলের সাথে কাজ করবে না, কারণ এটি লুপের অভ্যন্তরে পুনরুদ্ধার করে।

'Sign' চিহ্নটি ফোরোটোট করবেন না, এটি লাইনের ধারাবাহিকতা সম্পর্কে ব্যাশ ইন্টারপ্রেটারকে অবহিত করবে। আমি আশা করি এটি আপনাকে বা যে কাউকে সহায়তা করবে। তবে আমার মতে এই স্ক্রিপ্টটি পুরোপুরি এডাব্লুকে স্ক্রিপ্টে রূপান্তর করা উচিত, না হলে রিজেক্স, বা পার্ল ব্যবহার করে পাইথনে আবার লেখা উচিত, তবে বছরের পর বছর ধরে পার্লের জনপ্রিয়তা হ্রাস পায়। অজগর দিয়ে করাই ভাল।

সাবস্কেল ছাড়াই সংশোধিত সংস্করণ:

#!/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
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.