Linux 用户名 /etc/passwd 列表
Linux usernames /etc/passwd listing
我想打印在 /etc/passwd
中找到的最长和最短的用户名。如果我 运行 下面的代码适用于最短的 (head -1
),但 运行 不适用于 (sort -n |tail -1 | awk '{print }
)。谁能帮我找出问题所在?
#!/bin/bash
grep -Eo '^([^:]+)' /etc/passwd |
while read NAME
do
echo ${#NAME} ${NAME}
done |
sort -n |head -1 | awk '{print }'
sort -n |tail -1 | awk '{print }'
问题是当您真正需要一个时,您只有两个管道。所以你有 grep | while read do ... done | sort | head | awk
和 sort | tail | awk
:第一个 sort
有一个输入(即 while
循环)- 第二个 sort
没有。所以脚本挂起是因为你的第二个排序没有输入:或者更确切地说它有,但它是 STDIN。
有多种解决方法:
- 将
while
循环的输出保存到一个临时文件并将其用作两个 sort
命令的输入
- 重复你的 while 循环
- 使用 awk 做头部和尾部
前两个涉及遍历密码文件两次,这可能没问题 - 取决于您最终要尝试做什么。但是使用一个小的 awk 脚本,这可以通过 BEGIN
和 END
块为您提供第一行和最后一行。
如果您想要来自同一输入的头部和尾部,您可能需要类似 sed -e 1b -e '$!d'
的东西,在您使用 sed
对数据进行排序以获取顶线和底线之后。
因此您的脚本将是:
#!/bin/bash
grep -Eo '^([^:]+)' /etc/passwd |
while read NAME
do
echo ${#NAME} ${NAME}
done |
sort -n | sed -e 1b -e '$!d'
或者,更短的方法:
cut -d":" -f1 /etc/passwd | awk '{ print length, [=11=] }' | sort -n | cut -d" " -f2- | sed -e 1b -e '$!d'
这里的问题是:
管道以第一个 sort -n |head -1 | 结束awk '{print $2}' 命令。因此,通过管道提供第一个命令的输入并获得输出。
对于第二个命令,没有输入。因此,它等待来自键盘的 STDIN 输入,您可以通过键盘输入输入并按 ctrl+D 获得输出。
请运行像下面这样的代码以获得所需的输出:
#!/bin/bash
grep -Eo '^([^:]+)' /etc/passwd |
while read NAME
do
echo ${#NAME} ${NAME}
done |
sort -n |head -1 | awk '{print }'
grep -Eo '^([^:]+)' /etc/passwd |
while read NAME
do
echo ${#NAME} ${NAME}
done |
sort -n |tail -1 | awk '{print }
'
您只需要:
$ awk -F: '
NR==1 { min=max= }
length() > length(max) { max= }
length() < length(min) { min= }
END { print min ORS max }
' /etc/passwd
不需要显式循环或管道或多个命令。
虽然您已经有了很好的答案,但您也可以使用 POSIX shell 来实现您的目标,而无需使用 参数扩展 和字符串长度 由shell 本身提供(参见:POSIX shell specifiction)。例如,您可以执行以下操作:
#!/bin/sh
sl=32;ll=0;sn=;ln=; ## short len, long len, short name, long name
while read -r line; do ## read each line
u=${line%%:*} ## get user
len=${#u} ## get length
[ "$len" -lt "$sl" ] && { sl="$len"; sn="$u"; } ## if shorter, save len, name
[ "$len" -gt "$ll" ] && { ll="$len"; ln="$u"; } ## if longer, save len, name
done </etc/passwd
printf "shortest (%2d): %s\nlongest (%2d): %s\n" $sl "$sn" $ll "$ln"
示例Use/Output
$ sh cketcpw.sh
shortest ( 2): at
longest (17): systemd-bus-proxy
使用 pipe/head/tail/awk 或 shell 本身都可以。有替代品很好。
(注意:如果你有多个相同长度的用户,这只是选择第一个,如果你想保存所有名称并使用 -le
和 -ge
,你可以使用临时文件用于比较。)
我想打印在 /etc/passwd
中找到的最长和最短的用户名。如果我 运行 下面的代码适用于最短的 (head -1
),但 运行 不适用于 (sort -n |tail -1 | awk '{print }
)。谁能帮我找出问题所在?
#!/bin/bash
grep -Eo '^([^:]+)' /etc/passwd |
while read NAME
do
echo ${#NAME} ${NAME}
done |
sort -n |head -1 | awk '{print }'
sort -n |tail -1 | awk '{print }'
问题是当您真正需要一个时,您只有两个管道。所以你有 grep | while read do ... done | sort | head | awk
和 sort | tail | awk
:第一个 sort
有一个输入(即 while
循环)- 第二个 sort
没有。所以脚本挂起是因为你的第二个排序没有输入:或者更确切地说它有,但它是 STDIN。
有多种解决方法:
- 将
while
循环的输出保存到一个临时文件并将其用作两个sort
命令的输入 - 重复你的 while 循环
- 使用 awk 做头部和尾部
前两个涉及遍历密码文件两次,这可能没问题 - 取决于您最终要尝试做什么。但是使用一个小的 awk 脚本,这可以通过 BEGIN
和 END
块为您提供第一行和最后一行。
如果您想要来自同一输入的头部和尾部,您可能需要类似 sed -e 1b -e '$!d'
的东西,在您使用 sed
对数据进行排序以获取顶线和底线之后。
因此您的脚本将是:
#!/bin/bash
grep -Eo '^([^:]+)' /etc/passwd |
while read NAME
do
echo ${#NAME} ${NAME}
done |
sort -n | sed -e 1b -e '$!d'
或者,更短的方法:
cut -d":" -f1 /etc/passwd | awk '{ print length, [=11=] }' | sort -n | cut -d" " -f2- | sed -e 1b -e '$!d'
这里的问题是:
管道以第一个 sort -n |head -1 | 结束awk '{print $2}' 命令。因此,通过管道提供第一个命令的输入并获得输出。
对于第二个命令,没有输入。因此,它等待来自键盘的 STDIN 输入,您可以通过键盘输入输入并按 ctrl+D 获得输出。
请运行像下面这样的代码以获得所需的输出:
#!/bin/bash
grep -Eo '^([^:]+)' /etc/passwd |
while read NAME
do
echo ${#NAME} ${NAME}
done |
sort -n |head -1 | awk '{print }'
grep -Eo '^([^:]+)' /etc/passwd |
while read NAME
do
echo ${#NAME} ${NAME}
done |
sort -n |tail -1 | awk '{print }
'
您只需要:
$ awk -F: '
NR==1 { min=max= }
length() > length(max) { max= }
length() < length(min) { min= }
END { print min ORS max }
' /etc/passwd
不需要显式循环或管道或多个命令。
虽然您已经有了很好的答案,但您也可以使用 POSIX shell 来实现您的目标,而无需使用 参数扩展 和字符串长度 由shell 本身提供(参见:POSIX shell specifiction)。例如,您可以执行以下操作:
#!/bin/sh
sl=32;ll=0;sn=;ln=; ## short len, long len, short name, long name
while read -r line; do ## read each line
u=${line%%:*} ## get user
len=${#u} ## get length
[ "$len" -lt "$sl" ] && { sl="$len"; sn="$u"; } ## if shorter, save len, name
[ "$len" -gt "$ll" ] && { ll="$len"; ln="$u"; } ## if longer, save len, name
done </etc/passwd
printf "shortest (%2d): %s\nlongest (%2d): %s\n" $sl "$sn" $ll "$ln"
示例Use/Output
$ sh cketcpw.sh
shortest ( 2): at
longest (17): systemd-bus-proxy
使用 pipe/head/tail/awk 或 shell 本身都可以。有替代品很好。
(注意:如果你有多个相同长度的用户,这只是选择第一个,如果你想保存所有名称并使用 -le
和 -ge
,你可以使用临时文件用于比较。)