“Bash -x” bu betiği neden kırıyor?


13

Bazı komutun ne kadar çalıştığını ölçen bir komut dosyası var.

"Gerçek" timekomutuna, yani bir ikili /usr/bin/timedosyaya (bash-built-in -fbayrağı olmadığı için ) ihtiyaç duyar .

Aşağıda, hata ayıklanabilecek basitleştirilmiş bir komut dosyası:

#!/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" olarak kaydedin ve yürütün:

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

Böylece işe yaradı.

Şimdi bash komut satırına "-x" ekleyerek hata ayıklamaya çalışalım:

$ 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" kullandığımızda bu komut dosyası neden bozuluyor ve onsuz düzgün çalışıyor?


1
Heh. Yapı, çıktıyı elde edilen değerin bir parçası olarak dahil -xediyor gibi görünüyor . Bunun "beklenen" bir davranış mı yoksa bir hata mı olduğunu bilmiyorum ... Ya da belki aslında çıktı veren iç kabuk olduğunu . $()-x()-x
Jeff Y

Kenara: Ayar, çıkışı daha az sorun olan bir yere BASH_XTRACEFDyönlendirmenizi sağlar set -x.
Charles Duffy

Yanıtlar:


21

Sorun şu:

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

Burada standart hatayı standart çıktıyla eşleşecek şekilde yönlendirirsiniz. bash, izleme mesajlarını standart hataya yazıyor ve (örneğin), echotüm bash işlemindeki diğer kabuk yapılarıyla birlikte dahili olarak kullanıyor .

Eğer böyle bir şeye değiştirirseniz

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

bu problemi çözecek ve belki de izleme ve çalışma arasında kabul edilebilir bir uzlaşma olacaktır:

++ 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

ayrıca alt kabuğu da bırakabilirsiniz. görünüşe göre birbirimizi üzen rüzgar iç içe kabuklar:

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

Yaparsan:


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

... boru hattının içinde alt kabuğu barındıran o bölümünü işlemek için başlatılan alt kabuk ile dolanırsınız. Kabuk içermeyen kabuk, alt kabuğun hata ayıklama çıktısını bile ( kullanmayı tercih edebileceğiniz herhangi bir başka {bileşik komut için de yapacağınız gibi ; } >redirect) boru hattının bölümüne yönlendirir. Yönlendirme sırası ile ilgilidir.

Bunun yerine, önce yalnızca ölçmeye çalıştığınız komutların hata çıktısını yeniden yönlendirirseniz ve ana bilgisayar kabuğunun çıktısının stderr yapmasına izin verirseniz, aynı sorunla karşılaşmazsınız.

ve bu yüzden...


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

... anasistem kabuğu stderr'ını istediği yere yazmaya devam etmekte ve sadece boruya çağırdığı komutların çıkışını yeniden yönlendirmekte serbesttir.


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!

Bu konuda ...


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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.