linux - IFS 和命令替换

标签 linux shell ksh ifs 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: not found [No such file or directory]”。我已经知道这是因为我正在设置 IFS。如果我评论 IFS,并将 csv 文件更改为

a b c

有效!我想象 shell 使用默认 IFS 来分隔命令/usr/bin/java ,然后稍后应用 -version 参数。由于我更改了 IFS,它将整个字符串作为一个命令 - 或者这就是我认为正在发生的事情。

但这是我的要求:我有一个带有自定义分隔符的 csv 文件,并且命令中有参数,用空格分隔。我怎样才能正确地做到这一点?

最佳答案

IFS 指示如何在未加引号的替换中拆分变量的值。它适用于 $row$myscript

如果你想使用 IFS 进行分割,这在普通 sh 中很方便,那么你需要更改 IFS 的值或安排需要相同的值值(value)。在这种特殊情况下,您可以通过将 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"

关于linux - IFS 和命令替换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52233262/

相关文章:

linux - 通过串口连接两个Linux系统

c++ - Linux 程序在运行时找不到共享库

java/shellscript代码来查明一个jar文件是否已经在当前机器上运行

linux - 如何通过ssh获取在ksh中提交的远程进程的pid

linux - 如何在 shell 中获取一年中的某一天?

c - 为什么忽略 SIGTRAP 对 asm 不起作用?

java - find 命令不适用于 java,但适用于系统 shell

Android模拟器命令行不会终止

linux - 用于计算文本文件中的 CR/LF 的 Bash 脚本

linux - 基于变量值的回显字符(linux)