命令脚本返回"Too many ('s."



以下脚本我在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

我的猜测是,当您测试该命令行时,列表中只有一个用户 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,而且阅读起来也容易得多。另外,它有效。(它假定没有用户名包含空格或 glob 字符。

更简单的是

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,%sn", 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 元字符,因为不带引号的替换也会扩展 glob。总的来说,我会坚持本答案第一部分中概述的方法,并避免组装命令以传递给另一个 shell 的困难。

相关内容

  • 没有找到相关文章

最新更新