Phạm vi biến bash


104

Vui lòng giải thích cho tôi tại sao echocâu lệnh cuối cùng lại trống? Tôi mong đợi điều đó XCODEđược tăng lên trong vòng lặp while thành giá trị 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

Tôi đã thử sử dụng câu lệnh sau thay vì ++XCODEphương thức

XCODE=`expr $XCODE + 1`

và nó cũng sẽ không in ra bên ngoài câu lệnh while. Tôi nghĩ rằng tôi đang thiếu một cái gì đó về phạm vi biến ở đây, nhưng trang người đàn ông ol 'không hiển thị nó cho tôi.


Bạn khởi tạo XCODE ở đâu để có thể tăng dần?
Paul Tomblin

Tôi đã cố ném "XCODE = 0" ở đầu mã, bên ngoài câu lệnh while
Matt P

Không có điểm mấu chốt, nó hoạt động với tôi. #! / bin / bash cho tôi trong 1 2 3 4 5; do echo $ ((++ XCODE)) done echo "fin:" $ XCODE Tôi nghĩ rằng vấn đề của bạn không liên quan gì đến phạm vi biến và mọi thứ liên quan đến những gì đang xảy ra trong thời gian này.
Paul Tomblin

Đồng ý .. có vẻ như nó phải làm với vòng lặp "trong khi đọc"?
Matt P

Có một Câu hỏi thường gặp về Bash về điều này: mywiki.wooledge.org/BashFAQ/024
Fabio nói Hãy phục hồi Monica vào

Câu trả lời:


117

Bởi vì bạn đang đi vào vòng lặp while, một trình bao con được tạo ra để chạy vòng lặp while.

Bây giờ tiến trình con này có bản sao môi trường của riêng nó và không thể chuyển bất kỳ biến nào trở lại cha mẹ của nó (như trong bất kỳ quy trình unix nào).

Do đó, bạn sẽ cần phải cơ cấu lại để không bị vướng vào vòng lặp. Ngoài ra, bạn có thể chạy trong một hàm, ví dụ, và echogiá trị bạn muốn được trả về từ quy trình con.

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


2
điều này chỉ giải đáp rất nhiều vấn đề dường như ngẫu nhiên mà tôi đang gặp phải với kịch bản bash.
Daniel Agans

Câu trả lời hoàn hảo này làm tôi rất buồn và giải thích một hành vi thực sự kỳ lạ trong hệ thống CI của chúng tôi.
KayCee

108

Vấn đề là các quy trình đặt cùng với một đường ống được thực thi trong các trang con (và do đó có môi trường riêng của chúng). Bất cứ điều gì xảy ra bên trong whilekhông ảnh hưởng đến bất cứ điều gì bên ngoài đường ống.

Ví dụ cụ thể của bạn có thể được giải quyết bằng cách viết lại đường ống thành

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

hoặc có lẽ

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

32
Đối với những người đang nhìn vào điều này, bối rối không biết toàn bộ cú pháp <() là gì (như tôi đã từng), nó được gọi là "Thay thế quy trình" và cách sử dụng cụ thể được nêu chi tiết ở trên có thể được xem tại đây: mywiki.wooledge.org/ProcessSubstitution
Ross Aiken

2
Thay thế quy trình là thứ mà mọi người nên sử dụng thường xuyên! Nó là siêu hữu ích. Tôi làm điều gì đó như vimdiff <(grep WARN log.1 | sort | uniq) <(grep WARN log.2 | sort | uniq)mọi ngày. Hãy xem xét rằng bạn có thể sử dụng nhiều tệp cùng một lúc và coi chúng như các tệp ... KHẢ NĂNG!
Bruno Bronosky

8

Điều này cũng sẽ hoạt động (vì echo và while ở trong cùng một vỏ con):

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

3

Thêm một lựa chọn:

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

CHỈNH SỬA: Ở đây, xsel là một yêu cầu (cài đặt nó). Ngoài ra, bạn có thể sử dụng xclip: xclip -i -selection clipboard thay vì xsel -i -p


Tôi gặp lỗi: ./scraper.sh: dòng 111: xsel: không tìm thấy lệnh ./scraper.sh: dòng 114: xsel: không tìm thấy lệnh
3kstc

@ 3kstc rõ ràng là cài đặt xsel. Ngoài ra, bạn có thể sử dụng xclip, nhưng cách sử dụng của nó hơi khác một chút. Điểm chính ở đây: Thứ nhất bạn đặt đầu ra vào một clipboard (3 trong số chúng trong linux), thứ hai - bạn lấy nó từ đó và gửi đến 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

xem những thay đổi đó có giúp ích gì không


Khi thực hiện việc này, bây giờ tôi nhận được "0" để in cho câu lệnh echo cuối cùng. tuy nhiên tôi mong đợi giá trị là 1 chứ không phải 0. Ngoài ra, tại sao việc sử dụng xuất khẩu? Tôi giả sử rằng buộc nó vào môi trường?
Matt P

0

Một tùy chọn khác là xuất kết quả thành một tệp từ vỏ con và sau đó đọc nó trong vỏ mẹ. cái gì đó như

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

0

Tôi đã gặp phải vấn đề này khi tôi đang làm một chuyến du hành nhỏ của riêng mình:

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" )

Vấn đề là tôi tạo một vỏ con với () chứa biến SUM của tôi và while, nhưng tôi đặt vào nguyên () thay vì vào chính nó, điều này tránh mắc lỗi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.