我正在编写一个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文件,并且该命令中包含以空格分隔的参数。如何正确执行此操作?
答案 0 :(得分:1)
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解析的内容。要评估字符串中的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"
答案 1 :(得分:0)
IFS
告诉外壳程序哪些字符分隔“单词”,即命令的不同组成部分。因此,当您从IFS
中删除空格字符并运行foo bar
时,脚本会看到单个参数“ foo bar”而不是“ foo”和“ bar”。>
答案 2 :(得分:0)
最简单的选择是避免更改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
答案 3 :(得分:-1)
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