簡體   English   中英

IFS和命令替換

[英]IFS and command substitution

我正在編寫一個shell腳本來讀取輸入的csv文件並相應地運行Java程序。

#!/usr/bin/ksh
CSV_FILE=${1}
myScript="/usr/bin/java -version"
while read row
do
     $myScript
     IFS=$"|"
     for column in $row
     do 
        $myScript
     done
done < $CSV_FILE

CSV文件:

a|b|c

有趣的是,for循環外的$ myScript可以工作,但是for循環內的$ myScript表示“ / usr / bin / java -version:找不到[沒有這樣的文件或目錄]”。 我知道這是因為我正在設置IFS。 如果我注釋IFS,然后將csv文件更改為

a b c

有用 ! 我想象外殼使用默認的IFS來分隔命令/ usr / bin / java,然后在以后應用-version參數。 自從我更改了IFS之后,它就將整個字符串作為單個命令-或我認為正在發生的事情。

但這是我的要求:我有一個帶有自定義定界符的csv文件,並且命令中包含參數,並用空格分隔。 如何正確執行此操作?

IFS指示如何在無引號的替換中拆分變量的值。 它適用於$row$myscript

如果要使用IFS進行拆分(這在普通sh中很方便),則需要更改IFS的值或安排需要相同的值。 在這種特殊情況下,通過將myScript定義為myScript="/usr/bin/java|-version" ,您可以輕松安排需要相同的值。 或者,您可以及時更改IFS的值。 在這兩種情況下,請注意,不帶引號的替換不僅會使用IFS拆分值,還會將每個部分解釋為通配符模式,並用匹配文件名列表(如果有)替換它。 這意味着,如果您的CSV文件包含以下行

foo|*|bar

那么該行將不是foo*bar而是foo (當前目錄中每個文件名bar 要像這樣處理數據,您需要使用set -f關閉。 還請記住,當一行以反斜杠結尾時, read會讀取連續行,並去除前導和尾隨IFS字符。 使用IFS= read -r關閉這兩種行為。

myScript="/usr/bin/java -version"
set -f
while IFS= read -r row
do
    $myScript
    IFS='|'
    for column in $row
    do 
        IFS=' '
        $myScript
    done
done

但是,有更好的方法可以完全避免IFS拆分。 不要將命令存儲在以空格分隔的字符串中:在復雜情況下,該命令將失敗,例如需要包含空格的參數的命令。 有三種可靠的方法來存儲命令:

  • 將命令存儲在函數中。 這是最自然的方法。 運行命令就是代碼; 您可以在函數中定義代碼。 您可以將函數的參數統稱為"$@"

     myScript () { /usr/bin/java -version "$@" } … myScript extra_argument_1 extra_argument_2 
  • 將可執行命令名稱及其參數存儲在數組中。

     myScript=(/usr/bin/java -version) … "${myScript[@]}" extra_argument_1 extra_argument_2 
  • 存儲一個shell命令 ,即要由shell解析的命令 要評估字符串中的外殼代碼,請使用eval 像其他變量擴展一樣,請務必引用該參數,以避免過早的通配符擴展。 這種方法更復雜,因為它需要仔細的引用。 僅當必須將命令存儲在字符串中時,它才真正有用,例如,因為它作為腳本的參數進入。 請注意,您不能以這種方式明智地傳遞額外的參數。

     myScript='/usr/bin/java -version' … eval "$myScript" 

另外,由於使用的是ksh而不是普通的sh,因此不需要使用IFS來拆分輸入行。 使用read -A代替直接拆分成一個數組。

#!/usr/bin/ksh
CSV_FILE=${1}
myScript=(/usr/bin/java -version)
while IFS='|' read -r -A columns
do
    "${myScript[@]}"
    for column in "${columns[@]}"
    do 
        "${myScript[@]}"
    done
done <"$CSV_FILE"

IFS告訴外殼程序哪些字符分隔“單詞”,即命令的不同組成部分。 因此,當您從IFS刪除空格字符並運行foo bar ,腳本會看到單個參數 “ foo bar”,而不是“ foo”和“ bar”。

最簡單的選擇是避免更改IFS並使用read -d <delimiter>進行拆分,如下所示:

#!/usr/bin/ksh
CSV_FILE=${1}
myScript="/usr/bin/java -version"
while read -A -d '|' columns
do
     $myScript
     for column in "${columns[@]}"
     do 
        echo next is "$column"
        $myScript
     done
done < $CSV_FILE

IFS應該放在“ while”后面

#!/usr/bin/ksh
CSV_FILE=${1}
myScript="/usr/bin/java -version"
while IFS="|" read row
do
 $myScript
 for column in $row
 do 
    $myScript
 done
done < $CSV_FILE

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM