как перенаправить вывод системного вызова CAT в переменную и проанализировать переменные данные

Моя основная задача - извлечь только имя пользователя из файла/etc/passwd и сохранить его в списке массивов, я могу отображать их один за другим, для этого я создал простой скрипт, как показано ниже

#!/bin/bash


cat "/etc/passwd" > OUTPUT

echo -e $OUTPUT
IFS=:
read -ra ADDR <<< "$OUTPUT"

for i in "${ADDR[@]}"; do
    echo -e $i
done

И выход

>    cat /etc/passwd

   root:$1$L15vXdMd$SVx9qKAzHtLcqGN8n2SIc.:0:0:root:/:/bin/sh
   sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/bin/false
   super:x:1111:1100:Ldap user:/opt/www/usr/super:/bin/sh
   admin:x:1107:1100:Ldap user:/opt/www/usr/admin:/opt/www/jv/bin/cliInit.sh
   guest:x:1101:1100:Ldap user:/opt/www/usr/guest:/opt/www/jv/bin/cliInit.sh
   postgres:x:1000:1000:Linux User,,,:/home/postgres:/bin/sh
   albert:x:1114:1100:Ldap user:/opt/wwwl/usr/albert:/opt/www/jv/bin/cliInit.sh
   ritesh112:x:1125:1001:RADIUS user:/opt/www/usr/ritesh112:/opt/dell/mc/bin/cliInit.sh

может быть, я делаю неправильный путь (поэтому я задаю вопрос) в приведенном выше скрипте. Согласно моей логике сначала перенаправьте /etc/passwd на любую переменную с помощью (cat "/etc/passwd" > OUTPUT не знаю это правильно или неправильно Также в качестве альтернативы пробовали read -ra ADDR <<< "cat /etc/passwd", но не работают ), затем разделите вывод IFS=: переменной bash и сохраните все имена пользователей в массиве ADDR.

Я ожидаю, что вывод скрипта будет следующим, за исключением имен пользователей sshd и postgres.

root
super
admin
guest
albert
ritesh112

Но это не работает ни у кого есть идея?


person Jayesh Bhoi    schedule 03.02.2014    source источник


Ответы (6)


Не сохраняйте вывод в переменной / массиве (в некоторых случаях вы можете получить всевозможные проблемы с буферизацией - хотя и не с / etc / passwd, и в этом просто нет необходимости), просто сделайте что-нибудь вроде анализа строки за строкой или используйте такой инструмент, как awk или cut как предлагается перреаль

e.g.

#!/bin/bash

while read -r line; do 
  echo "${line%%:*}"
done < /etc/passwd

${line%%:*} удалит самый длинный суффикс, совпадающий с :*, то есть все, что находится после первого : включительно.

Чтобы получить массив, вы можете просто изменить его на (также обратите внимание, что нельзя включать postgres и sshd в массив. Есть несколько способов сделать это, но здесь должно быть достаточно)

#!/bin/bash

while read -r line; do
  [[ "${line%%:*}" != postgres && "${line%%:*}" != sshd ]] && arr+=("${line%%:*}")
done < /etc/passwd

for i in "${arr[@]}"; do
  echo -e "$i"
done

Или сделайте что-то вроде подстановки команд (но опять же, в большинстве случаев это не лучшая практика)

IFS=$'\n'
arr=($(< /etc/passwd))
person Reinstate Monica Please    schedule 03.02.2014
comment
хорошо, это правда, но он анализирует usrname только для отображения, но то, что мне нужно позже, мне интересно использовать массив списка имен пользователей. - person Jayesh Bhoi; 03.02.2014
comment
ох ... отлично, но, пожалуйста, исключите sshd и postgres имя пользователя из списка массивов, чтобы моя работа была выполнена. - person Jayesh Bhoi; 03.02.2014

cat "/etc/passwd" > OUTPUT

Не создает переменную, а просто копирует /etc/passwd в файл с именем OUTPUT. Чтобы получить содержимое переменной, вы можете попробовать OUTPUT=$(cat /etc/passwd).

Но вы можете просто использовать вырезать:

cut -d: -f1 /etc/passwd
person perreal    schedule 03.02.2014

Вы можете попробовать что-то вроде:

declare -a array
i=0
cut -d: -f1 /etc/passwd|egrep -v "^(sshd|postgres)$"|while read; do
    array[$i]="$REPLY"
    i=$((i + 1))
done
echo "${array[@]}"

Или что-то вроде:

declare -a array
i=0
while read; do
    array[$i]="$REPLY"
    i=$((i + 1))
done <<< "$(cut -d: -f1 /etc/passwd|egrep -v '^(sshd|postgres)$')"
echo "${array[@]}"

Or :

declare -a array
i=0
while read; do
    array[$i]="$REPLY"
    i=$((i + 1))
done < <(cut -d: -f1 /etc/passwd|egrep -v '^(sshd|postgres)$')
echo "${array[@]}"

В противном случае вы также можете использовать awk вместо cut и egrep (оба):

awk -F":" '($1 != "sshd" && $1 != "postgres"){print $1}' /etc/passwd|while read; do
    #...
done
person Idriss Neumann    schedule 03.02.2014

Поскольку кажется, что никто другой не помещает результаты в массив, как вы просили, я предлагаю следующее:

declare -a array=($(awk -F: '!/^sshd|^postgres/{print $1}' /etc/passwd))
echo ${array[1]}
root
echo ${array[2]}
daemon
person Mark Setchell    schedule 03.02.2014
comment
echo ${array[1]} ничего не отображать! - person Jayesh Bhoi; 03.02.2014
comment
Скопируйте и попробуйте еще раз - person Mark Setchell; 03.02.2014

Немного поработав awk, вы можете достичь своей цели с помощью

i=0
awk -F:  '{ print $1 }' /etc/passwd | egrep -v '^(sshd|postgres)$' |while read; do
    array[i]=$REPLY
    i=$((i + 1))
done

#output 5th item in array 
echo ${array[4]}
#output array length
echo ${#array[*]}
person epsilon    schedule 03.02.2014
comment
спасибо за помощь ... он также отображает sshd и postgres, которые не требуются, а также как хранить список пользователей в массиве - person Jayesh Bhoi; 03.02.2014
comment
Никогда не используйте for i in $(cmd). Вы можете увидеть этот ответ для получения более подробной информации: stackoverflow.com/questions/19606864/ffmpeg-in-a-bash-pipe/ И в этом случае достаточно использовать cut вместо awk (нет необходимости использовать танк, чтобы убить муху). - person Idriss Neumann; 03.02.2014

@perreal уже объяснил, что не так с вашим скриптом. Вот что бы я сделал:

cut -d: -f1 /etc/passwd | egrep -v '^(sshd|postgres)$'

Объяснение:

cut печатает поля ввода. -d: говорит, что поля разделены :, -f1 выбирает первое поле.

egrep дополнительно фильтрует вывод cut. -v говорит «печатать все, что не соответствует», а регулярное выражение соответствует ровно двум именам пользователей. Так что он все равно будет печатать sshd2 или postgresadmin.

Чтобы поместить все в массив:

declare -a array=($(cut -d: -f1 /etc/passwd | egrep -v '^(sshd|postgres)$'))

(примечание: синтаксис BASH; другие оболочки работают немного иначе).

person Aaron Digulla    schedule 03.02.2014