Bash内置读取命令与Korn shell的区别

时间:2015-12-11 18:44:39

标签: bash

我使用以下脚本在几百个Solaris(v9,10,11)和Red Hat Enterprise Linux(v5,6,7)服务器上检索有关已安装文件系统的信息以供分析。

# retrieves for all mounted file-systems: server, device, allocated, used, available, percent_used, mount_directory, permissions, owner_name, and group_name

server=$(uname -n)
df -h | awk '
    NF == 6 { print ($0); } 
    NF == 1 { device = $1; } 
    NF == 5 { print (device, " ", $0); }
  ' | while read device allocated used available percent mount
do
  ls -ld "${mount}" | read permissions links owner_name group_name size month day time directory
  echo "${server} ${device} ${allocated} ${used} ${available} ${percent} ${mount} ${permissions} ${owner_name} ${group_name}"
done

我使用PuTTY“plink”实用程序从Windoze执行此操作。

plink -m filesys.script server_name >>filesys.txt

所有工作都按预期工作,直到我的默认shell在所有服务器上从ksh更改为bash。现在,获取权限,owner_name和group_name的ls输出的第二个读取命令不起作用,也不会产生任何错误消息。因此,结果是输出中只有七个令牌(服务器到挂载),并且没有用于permissions,owner_name或group_name的任何内容。

我已经确认,如果我将脚本上传到带有shebang(#!/ bin / ksh)的Unix服务器,则脚本按预期工作。但是,我不想将此脚本推送到数百台服务器并将脚本维护在分布式机制中。我想保留中央Windoze工作站上的脚本,并使用plink的-m参数调用。将shabang放在文件顶部不会使用plink -m选项执行ksh。

正在播放的Bash shell版本是3.2和4.1。我还确定Windoze脚本文件已删除回车符。 awk实用程序用于处理设备名称太长而df将输出分成两行的情况。

同样,第一次读取(来自df / awk)工作正常,但第二次(ls输出)却没有。我通过在第二次读取后放置一个'set'来确认,那些环境变量不在环境中。

1 个答案:

答案 0 :(得分:2)

read(作为管道元素)发生在子shell中,因此即使它实际上 完美地执行,一旦该管道退出其结果也不可用于echo在一个单独的行上运行(作为最初生成管道的父进程的一部分)。这是POSIX完全允许的;管道的哪个组件(如果有的话)由生成的管道执行,该管道未由标准指定,因此是实现定义的。

您可以将echo置于与read相同的管道元素内来解决此问题:

server=$(uname -n)
df -h | awk '
    NF == 6 { print ($0); } 
    NF == 1 { device = $1; } 
    NF == 5 { print (device, " ", $0); }
  ' | while read device allocated used available percent mount
do
  # NOTE: parsing output from "ls" is unreliable
  ls -ld "${mount}" | {
    read permissions links owner_name group_name size month day time directory
    echo "${server} ${device} ${allocated} ${used} ${available} ${percent} ${mount} ${permissions} ${owner_name} ${group_name}"
  }
done

参考文献:

  • BashFAQ #24(我在一个管道中设置变量。为什么它们会在循环结束后消失?或者,为什么我不能管道数据来读取?)
  • ParsingLs(为什么你不应该解析ls(1)的输出)

如果您有GNU statfind,其中任何一个都允许您提供格式字符串来控制元数据输出,我强烈建议使用它们代替ls -l进行解析元数据。即使perl在某种程度上更好,只有一个普遍可用的实现在发布版之间具有统一的stat行为。