【问题标题】:Variable not being recognized after "read"“读取”后无法识别变量
【发布时间】:2018-09-13 10:19:02
【问题描述】:

-- 编辑:已解决。查看答案。

背景: 我正在编写一个 shell,当有人调整数据库大小时,它将在我们的系统上执行一些额外的操作。 shell 是用 ksh 编写的(要求),操作系统是 Solaris 5.10 。 问题在于其中一项检查,它验证底层操作系统上有足够的可用空间。

问题: 检查读取根的 df -k 行,这是我在此步骤中检查的内容,并将其打印到文件中。然后我将内容“读取”到我在计算中使用的变量中。 不幸的是,当我尝试对其中一个变量进行算术运算时,我收到一个错误,表明它为空。我在该行之后放置了一个调试输出行,验证它是否为空......它失去了它的价值......

我已经尝试了所有可以在网上找到的方法,当我手动运行它们时它们可以工作,但不能在 shell 文件中。 (* 该文件确实有#!/usr/bin/ksh)

代码:

df -k | grep "rpool/ROOT" > dftest.out  
RPOOL_NAME=""; declare -i TOTAL_SIZE=0; USED_SPACE=0; AVAILABLE_SPACE=0; AVAILABLE_PERCENT=0; RSIGN=""  
read RPOOL_NAME TOTAL_SIZE USED_SPACE AVAILABLE_SPACE AVAILABLE_PERCENT RSIGN < dftest.out  
\rm dftest.out  
echo $RPOOL_NAME $TOTAL_SIZE $USED_SPACE $AVAILABLE_SPACE $AVAILABLE_PERCENT $RSIGN  
((TOTAL_SIZE=$TOTAL_SIZE/1024))  

这是结果:
DBResize.sh[11]: TOTAL_SIZE=/1024: 语法错误

我在这一点上拉头发,任何帮助将不胜感激。

【问题讨论】:

  • 你不必将$ 添加到(()) 构造来进行数学运算,所以它应该是TOTAL_SIZE=$(($TOTAL_SIZE/1024))
  • @AndrewHenle 两者都是变量赋值和算术运算的有效方式。
  • 你能看一下dftest.out的内容吗?您可以通过删除带有declare -iset -xecho "test 8 2 6 75% =" &gt; dftest.out 的行来开始调试。

标签: solaris ksh


【解决方案1】:

您发布的代码无法产生您发布的输出。最明显的是,错误在第 11 行发出信号,但您发布的代码少于 11 行。前面的几行可能很重要。当您寻求帮助时,请始终发布complete code

更具体地说,declare 命令在 ksh 中不存在,它是一个 bash 的东西。您可以使用typeset 获得相同的结果(declare 是等效于typeset 的bash,但并非所有选项都相同)。要么你是用 bash 执行这个脚本,要么有另一个关于 declare 的错误消息,或者你定义了一些额外的命令,包括 declare,这可能会改变这段代码的行为。

但是,这些都不会对您发布的特定问题产生影响。 read 创建的变量一直分配到子shell 结束,即直到代码到达)、管道结束(仅在 ksh 中管道的左侧)等。

关于declaretypeset 的使用,请注意您只是将TOTAL_SIZE 声明为整数。对于其他变量,您只是分配一个恰好由数字组成的值。您发布的代码无关紧要,但可能不是您的意思。

可能发生的一件事是grep 不匹配任何内容,因此read 读取一个空行。你应该检查错误。在脚本中使用set -e 在第一个错误时退出。 (在某些情况下set -e 不会捕获错误,但这是一个好的开始。)

可能发生的另一件事是df 将其输出拆分为多行,因为包含文件系统名称的第一列太大。要防止这种分裂,请传递选项-P

使用临时文件是脆弱的:代码可能在只读目录中执行,另一个进程可能想同时访问同一个文件……这里临时文件是没用的。只需通过管道直接输入read。在 ksh 中(与包括 bash 在内的大多数其他 sh 变体不同),管道的右侧在主 shell 中运行,因此在以下命令中仍然可以对管道右侧的变量进行赋值。

在这个特定的脚本中并不重要,但您可以在算术表达式中使用不带$ 的变量。使用 $ 替换一个可能会产生混淆结果的字符串,例如a='1+2'; $((a*3)) 扩展为 7。不使用 $ 使用数值(在 ksh 中,a='1+2'; $((a*3)) 扩展为 9;在某些 sh ​​实现中会出现错误,因为 a 的值不是数值)。

#!/usr/bin/ksh
set -e
typeset -i TOTAL_SIZE=0 USED_SPACE=0 AVAILABLE_SPACE=0 AVAILABLE_PERCENT=0
df -Pk | grep "rpool/ROOT" | read RPOOL_NAME TOTAL_SIZE USED_SPACE AVAILABLE_SPACE AVAILABLE_PERCENT RSIGN  
echo $RPOOL_NAME $TOTAL_SIZE $USED_SPACE $AVAILABLE_SPACE $AVAILABLE_PERCENT $RSIGN  
((TOTAL_SIZE=TOTAL_SIZE/1024))

【讨论】:

    【解决方案2】:

    奇怪......当我摆脱你的“声明”行时,你的原始代码似乎运行良好(至少在 Linux 上使用 ksh)

    代码:

    #!/bin/ksh
    
    df -k | grep "/home" > dftest.out
    read RPOOL_NAME TOTAL_SIZE USED_SPACE AVAILABLE_SPACE AVAILABLE_PERCENT RSIGN < dftest.out
    \rm dftest.out
    
    echo $RPOOL_NAME $TOTAL_SIZE $USED_SPACE $AVAILABLE_SPACE $AVAILABLE_PERCENT $RSIGN
    ((TOTAL_SIZE=$TOTAL_SIZE/1024))
    print $TOTAL_SIZE
    

    结果:

    32962416 5732492 25552588 19% /home
    5598
    

    简单的 df -k 返回的值是什么。变量似乎持续存在。

    【讨论】:

      【解决方案3】:

      对于那些感兴趣的人,我发现不可能像我使用它的方式那样使用“读取”。
      “读取”分配的变量值只是“不持久”。
      为了解决这个问题,我应用了使用标准“while read”格式的不太理想的解决方案,并在循环内将选定的变量回显到变量文件中。
      一旦创建了所述文件,我就“加载”了它。

      (伪代码:)

      LOOP START
      echo "VAR_A="$VAR_A"; VAR_B="$VAR_B";" > somefile.out
      LOOP END
      . somefile.out
      

      【讨论】:

      • 你的分析是错误的:你使用read是可能的。使您的代码工作的原因一定是其他一些更改。
      • 正如最初指出的那样,当我手动尝试它时,它确实有效。它只是没有作为完整代码的一部分工作。我想可能在其他地方存在问题,但这仅意味着上述解决方案适用于导致它的任何其他组件。也许 df -k 输出上的格式恰好在该服务器上搞砸了。
      猜你喜欢
      • 1970-01-01
      • 2021-02-27
      • 2013-05-15
      • 2014-06-10
      • 2017-01-04
      • 2013-08-13
      • 2018-11-12
      • 2013-02-18
      • 1970-01-01
      相关资源
      最近更新 更多