2016-09-15 2 views
1

Что мне нужно сделать, так это получить список пользователей от etc/group, а затем отсортировать его и затем подсчитать уникальные записи.Сортировка и подсчет уникальных записей в массиве строк

Прямо сейчас мне удалось получить имена пользователей. Но я подозреваю, что это неправильно.

#!/bin/bash 
usernames=(); 

while IFS=: read -r Groups Tmp1 Tmp2 Username 
do 
    if [ $Username!="" ]; 
    then 
    usernames+=($Username); 
    fi; 
done < /etc/group 

Тогда я тоже пытался разобраться, но выход ОЧЕНЬ странно:

Сортировка:

IFS=$'\n' sorted=($(sort <<<"${usernames[*]}")) 
unset IFS 

Выход:

echo ${usernames[@]} 
echo "" 
echo ${sorted[@]} 

Результат:

root root root root root root _teamsserver root root _taskgated root root,_jabber,_postfix,_cyrus,_calendar,_dovecot _calendar,_jabber,_postfix _devicemgr,_teamsserver _eppc root _teamsserver _devicemgr _softwareupdate _locationd _teamsserver _devicemgr,_calendar,_teamsserver,_xserverdocs _teamsserver,_devicemgr _warmd 

_calendar,_jabber,_postfix _devicemgr _devicemgr,_calendar,_teamsserver,_xserverdocs _devicemgr,_teamsserver _eppc _locationd _softwareupdate _taskgated _teamsserver _teamsserver _teamsserver _teamsserver,_devicemgr _warmd root root root root root root root root root root root,_jabber,_postfix,_cyrus,_calendar,_dovecot 

У меня нет опыта работы с bash и абсолютно невозможно заставить его работать.

Что мне нужно, самое простое решение, чтобы получить список имен пользователей из/etc/group только с уникальными записями и напечатать количество повторений каждого из них.

Для бывшего, если у меня есть этот /etc/group файл:

nobody:*:-2: 
nogroup:*:-1: 
wheel:*:0:root 
daemon:*:1:root 
kmem:*:2:root 
sys:*:3:root 
tty:*:4:root 
operator:*:5:root 
mail:*:6:_teamsserver 

Я хочу, чтобы получить это:

root 6 
_teamsserver 1 
+0

Пожалуйста, добавьте проверяемый образец i/p и ожидаемый о/р. Ваш файл '/ etc/group' и ваш выходной формат. – Inian

+0

попробуйте 'cut -d: -f1/etc/group | сортировать | uniq -c' – Sundeep

+0

Просто добавил желаемый пример – s1ddok

ответ

3

Каждое поле «имя пользователя» на самом деле необязательно пустой разделенных запятыми список имен пользователей , Чтобы разделить имена пользователей, вам нужно разбить записи на запятые.

Если я начал с вашего цикла, я бы, вероятно, использовать:

sorted=($(while IFS=: read -r Groups Tmp1 Tmp2 Usernames 
      do 
       if [ -n "$Usernames" ]; 
       then 
        echo "$Usernames" 
       fi 
      done < /etc/group | 
      tr ',' '\n' | 
      sort -u 
     )) 

echo "${sorted[@]}" 

Это обходит промежуточный usernames массив. Если вы действительно хотите, чтобы, затем сохранить исходный контур и трубу на вход sort через команду tr перед тем sort:

IFS=$'\n' sorted=($(tr ',' '\n' <<<"${usernames[*]}" | sort -u)) 

Это создает массив, sorted, содержащий список уникальных имен в отсортированном порядке.

Если все, что вы хотите, это количество уникальных имен, я бы, наверное, сделал все это в awk. В самом деле, у меня возникнет соблазн использовать awk вместо цикла while.

Если вы хотите подсчет вхождения каждого уникального имени, то вместо sort -u вы должны использовать sort | uniq -c. Параметры и варианты статистики - это легион - ключевым моментом является то, что вам нужно разбить последнее поле файла /etc/group на запятую. Если по какой-то причине у вас есть пробелы в этом списке, вам также, возможно, придется избавиться от них. tr ', ' '\n' сделал бы это.

Использование awk, вы могли бы сделать:

awk -F: '{ n = split($4, a, ","); for (u = 1; u <= n; u++) count[a[u]]++i } 
     END { for (u in count) print u, count[u] }' /etc/group 

Он делит четвертое поле в массив a, подсчитывает число вхождений каждого имени в count массиве. В конце он печатает записи из массива count. На моем Mac это дало:

root 11 
_warmd 1 
_locationd 1 
_jabber 2 
_taskgated 1 
_postfix 2 
_devicemgr 4 
_calendar 3 
_cyrus 1 
_teamsserver 6 
_dovecot 1 
_xserverdocs 1 
_eppc 1 
_softwareupdate 1 

Вы также можете отсортировать это по мере необходимости.

+0

Я пытаюсь запустить ваш код, но получаю эту ошибку: 'синтаксическая ошибка около неожиданного токена"; if [-n "$ Usernames"]; ' – s1ddok

+0

Был (основной) баг - это то, что происходит, когда вы не проверяете. Я обновил код оболочки с помощью проверенного кода (добавлено '$ (...)' вокруг содержимого назначения массива. Вам все равно нужно настроить команды для получения желаемого результата (например, используя 'sort | uniq -c 'для получения подсчетов каждого имени. –

+0

Окей! Кажется, именно то, что мне нужно. – s1ddok

2

Вы могли бы попробовать что-то вроде этого:

awk -F ':' '{ if(length($4)) { gsub(",", "\n", $4); print $4 } }' /etc/group | \ 
    sort | uniq -c 

Команда awk принимает все непустые 4-го поля (с ':' в качестве разделителя) и заменить '' с «\ п ' в случае, если одна группа имеет несколько пользователей.

Затем мы сортируем и учитываем уникальные явления.

Edit:

Без awk:

cut -d: -f4 /etc/group | tr ',' '\n' | grep -v '^$' | sort | uniq -c 
+0

Если вы разделили четвертое поле на массив и затем используете этот массив для генерации подсчета каждого имени пользователя внутри awk, который вы затем распечатываете в конце. –

+0

Извините, я не могу использовать AWK, мне нужно чистое решение bash – s1ddok

+0

Извините, я не понял, с требованиями в вопросе, upvoted для awk – s1ddok

0

Слегка модифицированный пример ввода включить , отделенные имена

$ cat abc.txt 
nobody:*:-2: 
nogroup:*:-1: 
wheel:*:0:root 
daemon:*:1:root 
kmem:*:2:root,test 
sys:*:3:root 
tty:*:4:root,t1,test 
operator:*:5:root 
mail:*:6:_teamsserver 

$ perl -F: -le 'foreach (split /,/,$F[3]){$h{$_}++ if /./} END{foreach (keys %h){print "$_ $h{$_}"}}' abc.txt 
t1 1 
_teamsserver 1 
root 6 
test 2 
  • -F: разделение входной линии на : и сохранить в @F Массив
  • foreach (split /,/,$F[3]) итерация над 4-сплит поля на ,
  • $h{$_}++ if /./ приращения хэш, если не пустая
  • END{foreach (keys %h){print "$_ $h{$_}"}} печать информации хэш в требуемом формате
+0

Извините, я не могу использовать perl. Мне нужно чистое решение bash – s1ddok

+1

@ s1ddok: Что означает «чистый Bash»? Perl - это команда; Awk - это команда; 'tr' - это команда; 'sort' - это команда; 'uniq' - это команда: ни один из них не является« чистым Bash », потому что Bash запускает другую команду. –

+0

@JonathanLeffler Я могу показаться новичком, так как я никогда не испытывал программирования bash, я пытаюсь сказать, что я не могу использовать какой-либо другой язык сценариев, отличный от команд по умолчанию – s1ddok

1
cut -d: -f4 /etc/group | tr , '\n' | grep '.' | \ 
sort | uniq -c | join -a 1 -o '1.2,1.1' - /dev/null 

Или:

cut -d: -f4 /etc/group | tr , '\n' | grep '.' | \ 
sort | uniq -c | awk '{ print $2 " " $1 }' 

Как это работает:

  1. cut вне поля # 4.
  2. tr изменяет запятые на линейные кормушки.
  3. grep удаляет пустые строки.
  4. sort, count uniq ue строки, печать с использованием спецификации OP.
+1

Почему 'rev'? Вы можете просто использовать 'cut -d: -f4', не требуя' rev' вообще. –

+0

Я не думаю, что понимаю, как это работает, но поддерживается – s1ddok

+0

@JonathanLeffler, вы правы, спасибо. (Поразите нечеткое понимание '/ etc/group'. Я не принимал ничего общего с количеством полей, только то, что нужно было * last *.) * Хорошо пересмотрен. * – agc

Смежные вопросы