以下脚本我在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 的困难。