Cakupan variabel Bash


104

Tolong jelaskan kepada saya mengapa echopernyataan terakhir kosong? Saya berharap itu XCODEbertambah di while loop ke nilai 1:

#!/bin/bash
OUTPUT="name1 ip ip status" # normally output of another command with multi line output

if [ -z "$OUTPUT" ]
then
        echo "Status WARN: No messages from SMcli"
        exit $STATE_WARNING
else
        echo "$OUTPUT"|while read NAME IP1 IP2 STATUS
        do
                if [ "$STATUS" != "Optimal" ]
                then
                        echo "CRIT: $NAME - $STATUS"
                        echo $((++XCODE))
                else
                        echo "OK: $NAME - $STATUS"
                fi
        done
fi

echo $XCODE

Saya sudah mencoba menggunakan pernyataan berikut alih-alih ++XCODEmetode

XCODE=`expr $XCODE + 1`

dan itu juga tidak akan mencetak di luar pernyataan while. Saya rasa saya melewatkan sesuatu tentang variabel scope di sini, tapi halaman manual lama tidak menunjukkannya kepada saya.


Di mana Anda menginisialisasi XCODE menjadi sesuatu yang dapat ditingkatkan?
Paul Tomblin

Saya sudah mencoba untuk melempar "XCODE = 0" di bagian atas kode, di luar pernyataan while
Matt P

Tanpa cruft, itu berhasil untuk saya. #! / bin / bash untuk i in 1 2 3 4 5; lakukan echo $ ((++ XCODE)) dilakukan echo "fin:" $ XCODE Saya pikir masalah Anda tidak ada hubungannya dengan variabel pelingkupan dan segala sesuatu yang berkaitan dengan apa yang terjadi sementara itu.
Paul Tomblin

Setuju .. sepertinya ini ada hubungannya dengan loop "saat membaca"?
Matt P

Jawaban:


117

Karena Anda melakukan piping ke while loop, sub-shell dibuat untuk menjalankan while loop.

Sekarang proses anak ini memiliki salinan lingkungannya sendiri dan tidak dapat meneruskan variabel apa pun kembali ke induknya (seperti dalam proses unix apa pun).

Oleh karena itu, Anda perlu merestrukturisasi agar Anda tidak menyalurkan ke loop. Alternatifnya, Anda dapat menjalankan fungsi, misalnya, dan echonilai yang ingin Anda kembalikan dari sub-proses.

http://tldp.org/LDP/abs/html/subshells.html#SUBSHELL


2
ini hanya menjawab begitu banyak masalah yang tampaknya acak yang saya hadapi dengan skrip bash.
Daniel Agans

Jawaban sempurna ini sangat mengganggu saya dan menjelaskan perilaku yang sangat aneh dalam sistem CI kami.
KayCee

108

Masalahnya adalah proses yang disatukan dengan pipa dijalankan dalam subkulit (dan karenanya memiliki lingkungannya sendiri). Apapun yang terjadi di dalam whiletidak mempengaruhi apapun di luar pipa.

Contoh spesifik Anda dapat diselesaikan dengan menulis ulang pipa ke

while ... do ... done <<< "$OUTPUT"

atau mungkin

while ... do ... done < <(echo "$OUTPUT")

32
Bagi mereka yang melihat ini bingung tentang apa keseluruhan <() sintaks (seperti saya), itu disebut "Substitusi Proses", dan penggunaan spesifik yang dirinci di atas dapat dilihat di sini: mywiki.wooledge.org/ProcessSubstitution
Ross Aiken

2
Proses Substitusi adalah sesuatu yang harus digunakan setiap orang secara teratur! Ini sangat berguna. Saya melakukan sesuatu seperti vimdiff <(grep WARN log.1 | sort | uniq) <(grep WARN log.2 | sort | uniq)setiap hari. Pertimbangkan bahwa Anda dapat menggunakan beberapa sekaligus dan memperlakukannya seperti file ... KEMUNGKINAN!
Bruno Bronosky

8

Ini seharusnya bekerja juga (karena echo dan while berada di subkulit yang sama):

#!/bin/bash
cat /tmp/randomFile | (while read line
do
    LINE="$LINE $line"
done && echo $LINE )

3

Satu opsi lagi:

#!/bin/bash
cat /some/file | while read line
do
  var="abc"
  echo $var | xsel -i -p  # redirect stdin to the X primary selection
done
var=$(xsel -o -p)  # redirect back to stdout
echo $var

EDIT: Di sini, xsel adalah persyaratan (instal). Alternatifnya, Anda dapat menggunakan xclip: xclip -i -selection clipboard sebagai ganti xsel -i -p


Saya mendapatkan kesalahan: ./scraper.sh: baris 111: xsel: perintah tidak ditemukan ./scraper.sh: baris 114: xsel: perintah tidak ditemukan
3kstc

@ 3kstc jelas, instal xsel. Juga, Anda dapat menggunakan xclip, tetapi penggunaannya sedikit berbeda. Poin utama di sini: Pertama Anda memasukkan output ke clipboard (3 di antaranya di linux), ke-2 - Anda mengambilnya dari sana dan mengirim ke stdout.
Rammix

1
 #!/bin/bash
 OUTPUT="name1 ip ip status"
+export XCODE=0;
 if [ -z "$OUTPUT" ]
----

                     echo "CRIT: $NAME - $STATUS"
-                    echo $((++XCODE))
+                    export XCODE=$(( $XCODE + 1 ))
             else

echo $XCODE

lihat apakah perubahan itu membantu


Saat melakukan ini, sekarang saya mendapatkan "0" untuk dicetak untuk pernyataan gema terakhir. namun saya berharap nilainya menjadi 1 bukan nol. Juga, mengapa digunakan ekspor? Saya berasumsi bahwa memaksanya ke lingkungan?
Matt P

0

Pilihan lainnya adalah mengeluarkan hasil ke dalam file dari subkulit dan kemudian membacanya di shell induk. sesuatu seperti

#!/bin/bash
EXPORTFILE=/tmp/exportfile${RANDOM}
cat /tmp/randomFile | while read line
do
    LINE="$LINE $line"
    echo $LINE > $EXPORTFILE
done
LINE=$(cat $EXPORTFILE)

0

Saya menyiasatinya ketika saya membuat du kecil saya sendiri:

ls -l | sed '/total/d ; s/  */\t/g' | cut -f 5 | 
( SUM=0; while read SIZE; do SUM=$(($SUM+$SIZE)); done; echo "$(($SUM/1024/1024/1024))GB" )

Intinya adalah saya membuat subkulit dengan () yang berisi variabel SUM saya dan while, tetapi saya menyalurkan ke whole () daripada ke while itu sendiri, yang menghindari gotcha.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.