-1

我想打印 中找到的最长和最短的用户名/etc/passwd。如果我运行下面的代码,它适用于最短的 ( head -1),但不适用于 ( sort -n |tail -1 | awk '{print $2})。谁能帮我找出问题所在?

#!/bin/bash

grep -Eo '^([^:]+)' /etc/passwd | 
while read NAME
do
  echo ${#NAME} ${NAME}
done |
sort -n |head -1 | awk '{print $2}' 
sort -n |tail -1 | awk '{print $2}'
4

5 回答 5

4

这里的问题是:

管道以第一个排序结束 -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 $2}' 

    grep -Eo '^([^:]+)' /etc/passwd | 
    while read NAME
    do
      echo ${#NAME} ${NAME}
    done |
    sort -n |tail -1 | awk '{print $2}

'

于 2017-06-22T20:44:30.477 回答
2

所有你需要的是:

$ awk -F: '
    NR==1 { min=max=$1 }
    length($1) > length(max) { max=$1 }
    length($1) < length(min) { min=$1 }
    END { print min ORS max }
' /etc/passwd

不需要显式循环或管道或多个命令。

于 2017-06-22T21:13:08.613 回答
1

虽然您已经有了很好的答案,但您也可以使用 POSIX shell 来完成您的目标,而无需使用任何管道,使用shell 本身提供的参数扩展字符串长度(请参阅: POSIX shell 规范)。例如,您可以执行以下操作:

#!/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"

示例使用/输出

$ sh cketcpw.sh
shortest ( 2): at
longest  (17): systemd-bus-proxy

使用 pipe/head/tail/awk 或 shell 本身都可以。有替代品很好。

(注意:如果您有多个长度相同的用户,这只会选择第一个,如果您想保存所有名称并使用-le-ge进行比较,可以使用临时文件。)

于 2017-06-22T21:32:30.983 回答
1

问题是您只有两条管道,而您确实需要一条。所以你有grep | while read do ... done | sort | head | awkand sort | tail | awk:第一个sort有一个输入(即while循环) - 第二个sort没有。所以脚本挂起是因为你的第二类没有输入:或者更确切地说它有,但它是 STDIN。

有多种解决方法:

  • 将循环的输出保存while到临时文件并将其用作两个sort命令的输入
  • 重复你的while循环
  • 使用 awk 来做头部和尾部

前两个涉及对密码文件进行两次迭代,这可能没问题 - 取决于您最终想要做什么。但是使用一个小的 awk 脚本,这可以通过BEGINandEND块为您提供第一行和最后一行。

于 2017-06-22T20:22:48.483 回答
0

如果您想要来自同一输入的头部和尾部,您可能需要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, $0 }' | sort -n | cut -d" " -f2- | sed -e 1b -e '$!d'
于 2017-06-22T20:31:19.287 回答