2014-01-16 30 views
3

我有我想設置這樣的一個變量:爲什麼shell不會設置一個管道變量?

#!/bin/sh 

found=0 
id=1 
echo "Hello" | 
while [ $id != 5 ] 
do 
     id=`expr $id + 1` 
     echo $id 
     found=1 
done 

echo "found = $found" // I expect this to be 1 

爲什麼,以及如何設置這個值? 我不得不使用這樣(管道),因爲在生產環境中的實際代碼是:

found=0 
id=1 
my_mount_name="/opt/insiteone/fuse-mount" 
echo "select file_system_id, mount_name from SystemTable" | mysql ifm -uroot -pinsite3 | 
while read file_system_id mount_name 
do 
    if [ "$id" == "$file_system_id" -a "$my_mount_name" == "$mount_name" ]; then 
     echo "Match found for file system ID and mount name" 
     found=1 
    fi 
done 
echo "found = $found" // I expect this to be 1, when a match, but does not 
+0

這是因爲變量是在子shell設置。爲了使它在同一個shell中工作,請測試其他方法。因爲事實上,爲什麼在'echo'後面輸入? – fedorqui

+0

當您運行它時,您是否看到消息「找到匹配的文件系統ID和裝載名稱」? –

+0

剛纔,我得到了一個可行的答案,但它被刪除了。事實上,它解決了這個問題。 – kingsmasher1

回答

4

管路在Subshel​​l。你可以做一些事情,使其工作,最簡單的就是:

found=0 
id=1 
my_mount_name="/opt/insiteone/fuse-mount" 
echo "select file_system_id, mount_name from SystemTable" | 
mysql ifm -uroot -pinsite3 | { 
while read file_system_id mount_name 
do 
    if [ "$id" == "$file_system_id" -a "$my_mount_name" == "$mount_name" ]; then 
     echo "Match found for file system ID and mount name" 
     found=1 
    fi 
done 
echo "found = $found"; } 
# Note the enclosing {}. Inside the black, the variable $found is set. 
# After this comment, it will be zero. 

這種技術可能需要封閉塊是相當大的,所以你可能要重構腳本的其餘部分,使這個可用。另一種選擇是使用fifo或將echo/mysql管道放入進程替換中。 (後者是不可移植的,但在bash這可能是適當的工作。)然而,在這種特殊情況下,它可能是更好的做這樣的事情:

found=0 
id=1 
my_mount_name="/opt/insiteone/fuse-mount" 

echo "select file_system_id, mount_name from SystemTable" | 
mysql ifm -uroot -pinsite3 | { 
while read file_system_id mount_name 
do 
    if [ "$id" == "$file_system_id" -a "$my_mount_name" == "$mount_name" ]; then 
     echo "Match found for file system ID and mount name" 
     exit 0 # Exit the subshell succesfully 
    fi 
done 
exit 1; } && found=1 
+0

+1,很好用'exit' –

+0

但'exit'只能返回8位左右。你也可以使用'do_it_all(){mysql ... |同時閱讀...;做...;回聲「找到匹配」;完成; }'和'found = \'do_it_all \''來獲得更長的信息(比如在這種情況下字符串'found found')。 – Alfe

+0

謝謝。你的第二個代碼很好用,並且很好的使用exit。儘管@ hek2mgl代碼在示例中可以正常工作,但實際代碼中卻沒有。 – kingsmasher1

3

你也可以傳遞變量到子shell的environemnt:

foo=1 
bar=2 

echo "sds" | foo="$foo" bar="$bar" while ... 
+0

好的答案;你是否在意添加解釋? – unxnut

+0

感謝undelete,它的工作infact,我很幸運,在刪除之前趕上你的答案:-) – kingsmasher1

+0

@unxnut你可以調用每個shell命令,像這樣「ENV_VAR = sds ENV_VAR = DSFSD ... COMMAND」 – hek2mgl

2

由於@fedorqui評論,bash將管道組件放在不同的子殼體中,所以當子shell退出時,對變量的更改就消失了。

有兩種策略來應對這樣的:

  1. 只使用變更後的變量在同一子shell

    echo "Hello" | 
    { 
        while [ $id != 5 ] 
        do 
         ((id++)) 
         echo $id 
         found=1 
        done 
        echo "found = $found" // I expect this to be 1 
    } 
    

    這可能是一個問題,如果你有很多的代碼依賴那些變量

  2. 用流程替換替換流水線。這意味着while循環沒有在子Shell中執行,它運行在當前shell:

    while [ $id != 5 ] 
    do 
        ((id++)) 
        echo $id 
        found=1 
    done < <(echo "Hello") 
    echo "found = $found" // I expect this to be 1 
    

    這可以從可讀性差苦,但你可以把儘可能多的新行內<(...),只要你想。與進程替換重寫

    你的生產代碼(和bash /條件的ksh語法):

    found=0 
    id=1 
    my_mount_name="/opt/insiteone/fuse-mount" 
    
    while read file_system_id mount_name; do 
        if [[ $id == $file_system_id ]] && [[ $my_mount_name == $mount_name ]]; then 
         echo "Match found for file system ID and mount name" 
         found=1 
        fi 
    done < <(
        echo "select file_system_id, mount_name from SystemTable" | 
        mysql ifm -uroot -pinsite3 
    ) 
    echo "found = $found" 
    
+0

過程替換隻能在Bash中工作,如果你將shebang設置爲'#!/ bin/sh',它不會。因此你的腳本是不可移植的。 – kingsmasher1

+1

ksh也有進程替換。根據我的經驗,可移植性的評分過高:您打算在腳本上運行多少個系統?它已經依賴於mysql –

+1

現有的腳本使用它,並且僅僅因爲添加了1個事物,我們不能更改shebang變量。 – kingsmasher1

相關問題