Não é possível analisar a saída separada por espaço do awk

4

Se eu entendi corretamente, o separador padrão para a saída de awk é space .

No entanto, o script a seguir não se comporta como esperado. Eu não consigo analisar a saída de awk em uma matriz:

#!/bin/bash
echo "------ with input string from awk ------"
ALL_TTY_OWNERS_STR=$(ls -l /dev | grep tty | awk '{print $3}')

read -r -a ALL_TTY_OWNERS_ARRAY <<< "$ALL_TTY_OWNERS_STR"

echo "${#ALL_TTY_OWNERS_ARRAY[@]}" # This says 1
echo "${ALL_TTY_OWNERS_ARRAY[0]}" # "root", as expected
echo "${ALL_TTY_OWNERS_ARRAY[1]}" # empty string, expected "root"
echo "${ALL_TTY_OWNERS_ARRAY[2]}" # empty string, expected "root"


echo "------ with my manually created input string ------"
ALL_TTY_OWNERS_STR="root root root" # only for testing

read -r -a ALL_TTY_OWNERS_ARRAY <<< "$ALL_TTY_OWNERS_STR"

echo "${#ALL_TTY_OWNERS_ARRAY[@]}" # 3, as expected
echo "${ALL_TTY_OWNERS_ARRAY[0]}" # "root", as expected
echo "${ALL_TTY_OWNERS_ARRAY[1]}" # "root", as expected
echo "${ALL_TTY_OWNERS_ARRAY[2]}" # "root", as expected

Por que não posso analisar a saída de awk com read como esperava que pudesse?

    
por Gauthier 07.09.2017 / 09:14

4 respostas

5

É sobre o separador campo .

Você precisa definir o separador record para colocar cada string em uma única. Use ORS param:

ls -l /dev | grep tty | awk 'BEGIN { ORS=" " }; {print $3}'

Sem você, a saída será:

root
root
root
etc...

E quando você define a variável ALL_TTY_OWNERS_STR você coloca apenas a primeira seqüência de saída no primeiro elemento da matriz. Devido a isso, você array irá conter apenas um elemento e isso é exatamente o que você está recebendo

    
por 07.09.2017 / 09:36
4

O separador de campo padrão para saída é o espaço. Mas o separador de registros é uma nova linha. ALL_TTY_OWNERS_STR contém um monte de root s separados por novas linhas:

$ printf "%q\n" "$ALL_TTY_OWNERS_STR"
$'root\nroot\nroot\n....

E read , por padrão, é lido até a primeira nova linha.

Se você quiser apenas todos os usuários em ALL_TTY_OWNERS_ARRAY , talvez seja mais simples fazer algo como:

ALL_TTY_OWNERS_ARRAY=( $(stat -c '%U' /dev/*tty*) )
    
por 07.09.2017 / 09:42
3

read -a array lê todas as palavras de um registro em array .

Se você quiser ler todas as palavras de toda a entrada, use um separador de registro que não ocorra na entrada, como com -d '' que usa o caractere NUL como o separador de registro ou -d : (como : não pode ocorrer em um nome de usuário).

IFS=$'\n' read -rd '' -a array < <(ls -Ll /dev | awk '/tty/{print $3}')

(aqui usando -L para que, para os links simbólicos, a propriedade dos dispositivos, ao contrário do symlink, seja retornada).

Ou use readarray para cada linha da entrada a ser armazenada na matriz.

readarray -t array < <(ls -Ll /dev | awk '/tty/{print $3}')

Ou você pode usar o operador split + glob:

set -o noglob # disable glob part
IFS=$'\n'     # split on newline
array=($(ls -Ll /dev | awk '/tty/{print $3}')) # invoke split+glob by leaving
                                               # the $(...) unquoted.

Observe que tty é procurado em toda a saída de ls -Ll , incluindo o nome do usuário e do grupo, o nome do arquivo (e o destino do symlink se você omitir o -L ). Se você quiser considerar apenas o nome do arquivo, você poderia fazer

ls -Lld /dev/*tty* | awk '{print $3}'

em vez disso.

Com zsh , você poderia fazer:

zmodload zsh/stat
stat -s -A array +uid /dev/*tty*

(que teria o benefício de também trabalhar para nomes de usuários contendo espaços em branco).

    
por 07.09.2017 / 10:25
0

Se você não citar a variável após <<< , as novas linhas se tornarão o espaço em branco que você espera e o resultado será tudo em uma linha:

read -r -a ALL_TTY_OWNERS_ARRAY <<< $ALL_TTY_OWNERS_STR
    
por 07.09.2017 / 10:15

Tags