"बैश-एक्स" इस स्क्रिप्ट को क्यों तोड़ता है?


13

मेरे पास एक स्क्रिप्ट है जो मापती है कि कब तक कुछ कमांड निष्पादित होती है।

इसे "वास्तविक" timeकमांड की आवश्यकता है, अर्थ, उदाहरण के लिए एक बाइनरी /usr/bin/time(जैसा कि बैश-बिल्ट-इन-में -fध्वज नहीं है )।

नीचे, एक सरल स्क्रिप्ट जिसे डीबग किया जा सकता है:

#!/bin/bash

TIMESEC=$(echo blah | ( /usr/bin/time -f %e grep blah >/dev/null ) 2>&1 | awk -F. '{print $1}')

echo ABC--$TIMESEC--DEF

if [ "$TIMESEC" -eq 0 ] ; then
   echo "we are here!"
fi

"Test.sh" के रूप में सहेजें और निष्पादित करें:

$ bash test.sh
ABC--0--DEF
we are here!

तो यह काम किया।

अब, कमांड लाइन को बैश करने के लिए "-x" जोड़कर इसे डीबग करने का प्रयास करें:

$ bash -x test.sh
++ echo blah
++ awk -F. '{print $1}'
+ TIMESEC='++ /usr/bin/time -f %e grep blah
0'
+ echo ABC--++ /usr/bin/time -f %e grep blah 0--DEF
ABC--++ /usr/bin/time -f %e grep blah 0--DEF
+ '[' '++ /usr/bin/time -f %e grep blah
0' -eq 0 ']'
test.sh: line 10: [: ++ /usr/bin/time -f %e grep blah
0: integer expression expected

जब हम "-x" का उपयोग कर रहे हैं तो यह स्क्रिप्ट क्यों टूटती है, और इसके बिना ठीक काम करता है?


1
हे। यह इस तरह दिखता है -xकि $()निर्माण को -xइसके परिणामस्वरूप मूल्य के हिस्से के रूप में शामिल किया जा रहा है । अगर यह "अपेक्षित" व्यवहार या बग नहीं जानता है, तो ... या शायद यह इसके ()अंदर का सबस्क्रिप्शन है जो वास्तव में -xआउटपुट दे रहा है ।
जेफ वाई

एक तरफ: सेटिंग BASH_XTRACEFDआपको set -xकहीं न कहीं परेशानी कम करने के लिए आउटपुट को पुनर्निर्देशित करने देती है।
चार्ल्स डफी

जवाबों:


21

समस्या यह रेखा है:

TIMESEC=$(echo blah | ( /usr/bin/time -f %e grep blah >/dev/null ) 2>&1 | awk -F. '{print $1}')

जहाँ आप मानक आउटपुट से मिलान करने के लिए मानक त्रुटि को पुनर्निर्देशित कर रहे हैं। बैश अपने ट्रेस-मैसेज को मानक त्रुटि के लिए लिख रहा है, और (उदाहरण के लिए) इसके echoशेल के साथ-साथ अन्य शेल निर्माण का उपयोग करते हुए बैश प्रक्रिया में करता है।

अगर आप इसे कुछ इस तरह से बदलते हैं

TIMESEC=$(echo blah | sh -c "( /usr/bin/time -f %e grep blah >/dev/null )" 2>&1 | awk -F. '{print $1}')

यह उस समस्या के आसपास काम करेगा, और शायद अनुरेखण और काम के बीच एक स्वीकार्य समझौता हो सकता है:

++ awk -F. '{print $1}'
++ sh -c '( /usr/bin/time -f %e grep blah >/dev/null )'
++ echo blah
+ TIMESEC=0                 
+ echo ABC--0--DEF
ABC--0--DEF
+ '[' 0 -eq 0 ']'
+ echo 'we are here!'
we are here!

7

आप केवल उपधारा छोड़ सकते हैं। जाहिरा तौर पर यह नेस्टेड गोले हैं जो एक दूसरे को परेशान करते हैं:

TIMESEC=$(
    echo blah |
    /usr/bin/time -f %e grep blah 2>&1 >/dev/null |
    awk -F. '{print $1}'
)

यदि तुम करो:


...| ( subshell ) 2>pipe | ...

... आप उस सब-पाइप के साथ लॉन्च किए गए उपधारा को संभालते हैं, जिसके भीतर उप-भाग की मेजबानी करने वाली पाइपलाइन का वह हिस्सा है । चूँकि बिना शेल के उप-डिबग आउटपुट के भी शेल पुनर्निर्देशित करता है (जैसा कि आप किसी अन्य प्रकार के {कंपाउंड कमांड के लिए भी करते हैं,; } >redirect जिसका उपयोग आप पाइप लाइन के अपने सेक्शन में कर सकते हैं) जिसमें आप मिक्सिंग स्ट्रीम को हवा देते हैं। इसे पुनर्निर्देशन के क्रम से करना होगा।

इसके बजाय, यदि आप पहले केवल उन कमांड के त्रुटि आउटपुट को रीडायरेक्ट करते हैं , जिन्हें आप गेज करने का प्रयास कर रहे हैं, और होस्ट शेल के आउटपुट को इसे स्ट्राइडर करने के लिए बनाते हैं, तो आप एक ही समस्या को हवा नहीं देते हैं।

इसलिए...


... | command 2>pipe 1>/dev/null | ...

... होस्ट शेल अपने स्टडर को लिखना जारी रखने के लिए स्वतंत्र है, जहां यह केवल कमांड के आउटपुट को पुनर्निर्देशित करता है, जिसे वह पाइप में कॉल करता है।


bash -x time.sh
+++ echo blah
+++ /usr/bin/time -f %e grep blah
+++ awk -F. '{print $1}'
++ TIMESEC=0
++ echo ABC--0--DEF
ABC--0--DEF
++ '[' 0 -eq 0 ']'
++ echo 'we are here!'
we are here!

उस बात के लिए...


TIMESEC=$(
    echo blah |
    /usr/bin/time -f %e grep blah 2>&1 >/dev/null
)
printf %s\\n "ABC--${TIMESEC%%.*}--DEF"
if [ "${TIMESEC%%.*}" -eq 0 ] ; then
   echo "we are here!"
fi
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.