首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >命令脚本返回“太多”。

命令脚本返回“太多”。
EN

Stack Overflow用户
提问于 2016-05-24 02:52:01
回答 1查看 4K关注 0票数 0

下面的脚本是我在RHEL 5上构建的,它运行得很好。然而,现在我正在各种RHEL6和Ubuntu上进行测试,并且这个消息是一致的,“太多了”。任何关于发生什么的想法。这是我正在编写的脚本。

代码语言:javascript
复制
$ 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操作系统上测试这一点,并得到以下输出消息

代码语言:javascript
复制
# ( 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.

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

代码语言:javascript
复制
# ( 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
EN

回答 1

Stack Overflow用户

发布于 2016-05-24 05:37:38

我的猜测是,在测试命令行时,列表中只有一个用户id。这一猜测背后的理由是一个小小的离题。

简化脚本

使用bash创建一个向csh发送的复合命令,让我觉得要么是故意的混淆,要么是奇怪的过度思考。而不是不工作

代码语言:javascript
复制
( echo "foreach user ( `cut -d':' -f'1' /etc/passwd` )"
  echo "sudo passwd -S "\$"user" ; echo "end"
) | csh |
# ...

你可以这么做:

代码语言:javascript
复制
for user in $(cut -d: -f1 /etc/passwd); do
  sudo passwd -S $user
done

根本不需要csh,而且阅读起来也容易得多。再加上它能用。(它确实假定没有任何用户名包含空格或glob字符。)

更简单的是

代码语言:javascript
复制
sudo passwd -Sa

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

另外:

代码语言:javascript
复制
sed 's/ /,/g' | cut -d, -f1-3

可能会更易读

代码语言:javascript
复制
cut -f1-3 --output-delimiter=,

两个编辑

代码语言:javascript
复制
sed 's/LK/DISABLED/' | sed 's/PS/ENABLED/' 

可以放在一个命令中:

代码语言:javascript
复制
sed 's/LK/DISABLED/;s/PS/ENABLED/'

还可以使用sed插入前缀。

代码语言:javascript
复制
hostname=$(hostname)
sed "s/^/$hostname,/;s/LK/DISABLED/;s/PS/ENABLED/"

所以最终的结果是

代码语言:javascript
复制
hostname=$(hostname)
sudo passwd -Sa |
cut -d' ' --output-delimiter=, -f1-3 |
sed "s/^/$hostname,/;s/LK/DISABLED/;s/PS/ENABLED/"

在我看来更直截了当。或者,您可以使用awk而不是cut+sed

代码语言:javascript
复制
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语句的语法精确地是:

代码语言:javascript
复制
foreach <var> ( <list> )
  <commands>
end

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

现在,当您要求bash执行一个命令时,使用$(command [args...]) (或不建议使用的回勾符号`command [args...]`,您确实应该停止使用):(重点添加)

Bash通过执行命令并将命令替换为命令的标准输出来执行扩展,并删除所有尾随的换行符。

因此,如果命令返回一行,则将有一个尾尾换行符,并将其删除:

代码语言:javascript
复制
$ echo "foreach i ( $(seq 1) )"
foreach i ( 1 )

但是,如果有多行,您将得到内部换行符:

代码语言:javascript
复制
$ echo "foreach i ( $(seq 1 2) )"
foreach i ( 1
2 )

其中的第一个会很好。但如果你把第二条信息输入csh,它就会抱怨说“太多了”这条信息有点神秘。

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

代码语言:javascript
复制
$ echo foreach i \( $(seq 1 2) \)
foreach i ( 1 2 )

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

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/37403607

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档