繁体   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