命令脚本返回“太多('s。”

时间:2016-05-24 02:52:02

标签: bash

我在RHEL 5上构建的以下脚本运行正常。但是现在我正在测试各种RHEL6和Ubuntu并且消息是一致的“太多('s。)。任何关于发生了什么的想法。这是我正在处理的脚本

$ echo "HOST,UserName,IsDiabled,PassLastSet" > fileIWantToWriteTo;
$ ( echo "foreach user ( `cut -d':' -f'1' /etc/passwd` )";
    echo "sudo passwd -S "\$"user" ; echo "end"
  ) | csh | 
  sed 's/ /,/g' | cut -f1-3 -d',' | 
  sed 's/LK/DISABLED/' | 
  sed 's/PS/ENABLED/' | 
  awk 'BEGIN{"hostname" | getline hstnm ; }{print hstnm "," $0}' \
    >> fileIWantToWriteTo

现在我在各种Linux操作系统上测试它,我收到以下输出消息

# ( echo "foreach user ( `cut -d':' -f'1' /etc/passwd` )"
echo "sudo passwd -S "\$"user" ; echo "end" ) | 
csh | 
sed 's/ /,/g' | 
cut -f1-3 -d',' | 
sed 's/LK/DISABLED/' | 
sed 's/PS/ENABLED/' | 
awk 'BEGIN{"hostname" | getline hstnm ; }; {print hstnm "," $0}'
Too many ('s.

然而,当我测试时,我取得了成功。

# ( echo "foreach user ( `cut -d':' -f'1' /etc/passwd` )"
echo "sudo passwd -S "\$"user" ; echo "end" ) | 
csh | 
sed 's/ /,/g' | 
cut -f1-3 -d',' | 
sed 's/LK/DISABLED/' | 
sed 's/PS/ENABLED/' | 
awk 'BEGIN{"hostname" | getline hstnm ; }; {print hstnm "," $0}'
tophostd,root,ENABLED,2015-08-31 tophostd,bin,DISABLED,2013-08-19

1 个答案:

答案 0 :(得分:3)

我的猜测是,当您测试该命令行时,列表中只有一个用户ID。这个猜测背后的原因是一个小小的题外话。

简化脚本

使用bash创建一个复合命令以发送给csh,这可能是故意混淆或奇怪的过度思考。而不是不工作

( echo "foreach user ( `cut -d':' -f'1' /etc/passwd` )"
  echo "sudo passwd -S "\$"user" ; echo "end"
) | csh |
# ...

你可以这样做:

for user in $(cut -d: -f1 /etc/passwd); do
  sudo passwd -S $user
done

根本不需要csh,而且阅读起来也更容易。加上它有效。 (它假设没有用户名包含空格或全局字符。)

更简单的是

sudo passwd -Sa

生成所有帐户的状态信息。 (假设您的passwd实现了该选项。)

此外:

sed 's/ /,/g' | cut -d, -f1-3

可能更具可读性

cut -f1-3 --output-delimiter=,

和两个编辑

sed 's/LK/DISABLED/' | sed 's/PS/ENABLED/' 

可以放在一个命令中:

sed 's/LK/DISABLED/;s/PS/ENABLED/'

您也可以使用sed

插入前缀
hostname=$(hostname)
sed "s/^/$hostname,/;s/LK/DISABLED/;s/PS/ENABLED/"

以便最终结果为

hostname=$(hostname)
sudo passwd -Sa |
cut -d' ' --output-delimiter=, -f1-3 |
sed "s/^/$hostname,/;s/LK/DISABLED/;s/PS/ENABLED/"

在我看来更直接。或者,您可以使用awk代替cut + sed

sudo passwd -Sa |
awk -v hostname="$(hostname)" \
  '{ sub(/LK/, "DISABLED", $2);
     sub(/PS/, "ENABLED", $2);
     printf "%s,%s,%s,%s\n", hostname, $1, $2, $3;
   }'

但回到问题

无论如何,你的脚本失败的原因:

csh 非常特定于命令格式。它不能容忍意想不到的地方换行。 foreach语句的语法恰恰是:

foreach <var> ( <list> )
  <commands>
end

<list> 中不能有换行符(除非你用反斜杠转义它们)。

现在,当你要求bash执行一个命令时,使用$(command [args...])(或者你应该真正停止使用的弃用的反引号符号`command [args...]`):(强调添加)

  

Bash通过执行命令并使用命令的标准输出替换命令替换来执行扩展,并删除任何尾随换行符。

因此,如果该命令返回一行,则会有一个尾随换行符,它将被删除:

$ echo "foreach i ( $(seq 1) )"
foreach i ( 1 )

但是如果有多行,你最终会得到内部新行:

$ echo "foreach i ( $(seq 1 2) )"
foreach i ( 1
2 )

第一个将正常工作。但是,如果你将第二个提供给csh,它会抱怨一些有点神秘的信息“太多了(.s。”。

您可以通过对行进行分词来消除内部换行符,这意味着不使用引号。 (但你仍然需要引用括号。)

$ echo foreach i \( $(seq 1 2) \)
foreach i ( 1 2 )

您需要确保替换中没有glob元字符,因为未引用的替换也会扩展globs。总的来说,我会坚持使用本答案第一部分中概述的方法,并避免将命令组合传递给另一个shell的困难。