在bash中打印两次的循环
我正在编写这个bash脚本,它应该打印出所有从未登录过的用户,并提供对他们进行排序的选项。我已经设法使所有的输入都能正常工作,但是,在打印输出时,我遇到了一些问题。循环如下:在bash中打印两次的循环,bash,shell,Bash,Shell,我正在编写这个bash脚本,它应该打印出所有从未登录过的用户,并提供对他们进行排序的选项。我已经设法使所有的输入都能正常工作,但是,在打印输出时,我遇到了一些问题。循环如下: for user in $(lastlog | grep -i 'never' | awk '{print $1}'); do grep $user /etc/passwd | awk -F ':' '{print $1, $3}' done 当然,这个循环不会对输出进行排序,但是
for user in $(lastlog | grep -i 'never' | awk '{print $1}'); do
grep $user /etc/passwd | awk -F ':' '{print $1, $3}'
done
当然,这个循环不会对输出进行排序,但是,从我对shell和shell脚本的有限理解来看,它应该只是在第一个“awk”{print$1}'之后加上一个“| sort”。我的问题是,这个循环的输出至少打印每个用户两次,在某些情况下,打印四次。这是为什么?我如何修复它?好吧,让我们试着调试它:
for user in $(lastlog | grep -i 'never' | awk '{print $1}'); do
echo "The user '$user' matches these lines:"
grep $user /etc/passwd | awk -F ':' '{print $1, $3}'
echo
done
这将产生:
The user 'daemon' matches these lines:
daemon 1
colord 112
The user 'bin' matches these lines:
root 0
daemon 1
bin 2
sys 3
sync 4
games 5
man 6
(...)
实际上,colord
的条目确实包含daemon
:
colord:x:112:120:colord colour management daemon,,,:/var/lib/colord:/bin/false
^-- Here
而且games
条目与bin
匹配:
games:x:5:60:games:/usr/games:/usr/sbin/nologin
^-- Here
因此,我们不想在任何地方匹配用户名字符串,而只想从行首到第一个冒号匹配它:
for user in $(lastlog | grep -i 'never' | awk '{print $1}'); do
echo "The user '$user' matches these lines:"
grep "^$user:" /etc/passwd | awk -F ':' '{print $1, $3}'
echo
done
现在每个条目只显示它应该显示的单个条目,所以你可以移除回声并继续
如果您对finesse和polish感兴趣,这里有一个可供选择的解决方案,它可以跨语言设置、奇怪的用户名、网络身份验证、大型列表等高效工作:
LC_ALL=C lastlog |
awk -F ' ' '/Never logged in/ {printf "%s\0", $1}' |
xargs -0 getent passwd |
awk -F : '{print $1,$3}' |
sort
那么,让我们试着调试它:
for user in $(lastlog | grep -i 'never' | awk '{print $1}'); do
echo "The user '$user' matches these lines:"
grep $user /etc/passwd | awk -F ':' '{print $1, $3}'
echo
done
这将产生:
The user 'daemon' matches these lines:
daemon 1
colord 112
The user 'bin' matches these lines:
root 0
daemon 1
bin 2
sys 3
sync 4
games 5
man 6
(...)
实际上,colord
的条目确实包含daemon
:
colord:x:112:120:colord colour management daemon,,,:/var/lib/colord:/bin/false
^-- Here
而且games
条目与bin
匹配:
games:x:5:60:games:/usr/games:/usr/sbin/nologin
^-- Here
因此,我们不想在任何地方匹配用户名字符串,而只想从行首到第一个冒号匹配它:
for user in $(lastlog | grep -i 'never' | awk '{print $1}'); do
echo "The user '$user' matches these lines:"
grep "^$user:" /etc/passwd | awk -F ':' '{print $1, $3}'
echo
done
现在每个条目只显示它应该显示的单个条目,所以你可以移除回声并继续
如果您对finesse和polish感兴趣,这里有一个可供选择的解决方案,它可以跨语言设置、奇怪的用户名、网络身份验证、大型列表等高效工作:
LC_ALL=C lastlog |
awk -F ' ' '/Never logged in/ {printf "%s\0", $1}' |
xargs -0 getent passwd |
awk -F : '{print $1,$3}' |
sort
您的grep将匹配多行(man将匹配manuel和norman等),将其锚定到行的开头并添加一条线索:
grep "^${user}:" /etc/passwd | awk -F ':' '{print $1, $3}'
更好的选择可能是完全忘记grepping/etc/passwd,使用id
命令获取用户id:
id=$(id -u "${user}" 2>/dev/null) && printf "%s %d\n" "${user}" "${id}"
如果id命令失败,则不会打印任何内容,或者可以将其修改为:
id=$(id -u "${user}" 2>/dev/null)
printf "%s %s\n" "${user}" "${id:-(User not found)}"
在gnu linux中,我很确定找到的用户id不存在是不可能的,因为lastlog将只报告现有用户,因此第二个示例可能没有意义。您的grep将匹配多行,(man将匹配manuel和norman等)将其锚定到行的开头并添加一条线索:
grep "^${user}:" /etc/passwd | awk -F ':' '{print $1, $3}'
更好的选择可能是完全忘记grepping/etc/passwd,使用id
命令获取用户id:
id=$(id -u "${user}" 2>/dev/null) && printf "%s %d\n" "${user}" "${id}"
如果id命令失败,则不会打印任何内容,或者可以将其修改为:
id=$(id -u "${user}" 2>/dev/null)
printf "%s %s\n" "${user}" "${id:-(User not found)}"
在gnu linux中,我很确定找到的用户id不存在是不可能的,因为lastlog只会报告现有用户,所以第二个示例可能毫无意义。想想名为
sh
的用户会发生什么。有多少用户将grep sh
匹配?可能它们都是,因为每个都在shell
字段中使用一些shell
你应该想想
awk -F ':' '$1 == "'"$user"'" {print $1, $3}' /etc/passwd
或使用用户的awk变量:
awk -F ':' -vuser="$user" '$1 == user {print $1, $3}' /etc/passwd
想想名为
sh
的用户会发生什么。有多少用户将grep sh
匹配?可能它们都是,因为每个都在shell
字段中使用一些shell
你应该想想
awk -F ':' '$1 == "'"$user"'" {print $1, $3}' /etc/passwd
或使用用户的awk变量:
awk -F ':' -vuser="$user" '$1 == user {print $1, $3}' /etc/passwd
grep | awk
确实是一种反模式(记住:awk内置了模式匹配),但这不意味着它会打印出所有与grep不匹配的内容吗?此外,当涉及到打印所有已登录的用户时,该代码也起作用in@Ohunter,awk'/never/{…}'
与grep never | awk'{…}'
相同,但没有额外进程和FIFO.BTW的开销,请参阅。特别是对于搜索/etc/passwd
,运行getentpasswd“$user”
更有意义,它也将与其他目录源(LDAP、NIS等)一起工作,但是如果要搜索/etc/passwd
,那么awk-v user=“$user”-F:'$1==user{print$3}
将只搜索精确的匹配项(不是子字符串),并且只在您实际要搜索的一个字段中。grep | awk
是反模式(请记住:awk内置了模式匹配)当然,但这不意味着它会打印出所有与grep不匹配的内容吗?而且,当涉及到打印所有已登录的用户时,代码也会起作用in@Ohunter,awk'/never/{…}'
的作用与grep never | awk'{…}的作用相同“
,但是没有额外的进程和FIFO.BTW的开销,请参阅。特别是对于搜索/etc/passwd
,运行getent passwd“$user”
,它也可以与其他目录源(LDAP、NIS等)一起工作,但是如果要搜索/etc/passwd
,那么awk-v user=“$user”-F:'$1==user{print$3}'
将只搜索精确匹配项(而不是子字符串),并且仅在您实际要搜索的一个字段中。因此,您是在暗示它与/etc/passwd的其他行相匹配并同时打印?但为什么它会打印出重复项的正确UID?您可以从输出中看到,它会显示两次守护进程1
。您的awk语句始终打印UID和d同一行的用户名,不能交叉引用,所以可以使用某种正则表达式和grep来过滤掉它?@Ohunter是的,这就是grep“^$user:”
选项可以。嗯,大多数情况下,如果用户名包含任何regex元字符,则会出现问题。您也可以解决这个问题,但此时您正在谈论在工作区之上堆积工作区,这都是因为您使用了错误的工具。只是