Escape [no grep

3

Eu li strings de stdin e quero exibir os usuários que correspondem às strings. O problema é, se o usuário insere o caractere '[', ou uma string que o contém.

grep -F não funciona porque a linha deve começar com a string (^ - que é um caractere simples com -F). Além disso, getent $user não será bom porque eu também preciso apenas do nome de usuário e não do id.

if [[ "$user" == *"["* ]]; then
    echo -e "Invalid username.\n"
    continue
fi

if ! getent passwd | grep "^$user:"; then
    echo -e "Invalid username.\n"
    continue
fi

Esta é a solução alternativa para '[', existe outro caminho? awk fará o trabalho mais provavelmente, mas eu não tenho conhecimento disso ainda, estou interessado em grep.

    
por Tigrex 27.04.2017 / 23:51

3 respostas

9

Ou escape ou coloque em uma classe de caracteres, algo ao longo destas linhas:

grep '\['

grep '[[]'

grep -e "${user//\[/\\[}"

A sintaxe ${var//c/d} = > na variável de shell $var , substituímos todos os caracteres c por d . Agora, no seu caso, o c é [ , mas acontece que [ é especial nessa sintaxe (ele faz globbing) e, portanto, precisamos escapar dele prefixando-o com uma barra invertida, ou seja, \[ .

Agora chegando à peça de substituição, o que precisamos é de \[ . Mas, novamente, tanto \ como [ são especiais nessa sintaxe de ${var//...} de substituição de parâmetro e, portanto, ambos precisam ser, sim, você adivinhou certo, com barra invertida levando à expressão: \\[ : "${var//\[/\\[}"

HTH

    
por 27.04.2017 / 23:54
5

[ não é o único caractere a escapar para regexps. Todos os operadores de RE estão incluindo . , que é comum em nomes de usuários ( r.ot como um regexp corresponde a root , por exemplo).

Além disso, sua abordagem ( getent passwd | grep "^$user:" ) também é inválida, pois não sinalizaria root:0 como inválido, por exemplo.

Aqui, seria melhor usar awk :

user_valid() {
  getent passwd | 
    U="$1" awk -F: '$1 == ENVIRON["U"] {found = 1; exit}
                    END {exit(1 - found)}'
}

Agora, nem todos os bancos de dados de usuários permitem enumerações como essas.

$ getent passwd | grep stephane
$ id -u stephane
10631
$ getent passwd stephane
stephane:*:10631:10631:Stephane Chazelas:/export/./home/stephane:/bin/zsh

No meu caso, esse usuário está em um banco de dados LDAP. enumeração está desativada (pode haver milhares de usuários), mas ainda posso consultar / resolver usuários individualmente.

Portanto, para validar os usuários, é melhor consultar o banco de dados do usuário diretamente para esse usuário. Por exemplo, usando o comando id (um comando padrão contrário a getent ):

user_valid() {
  case $1 in
    (*[!0-9]*) id -u -- "$1" > /dev/null 2>&1;;
    (*) false;;
  esac
}

(estamos cuidando dos usuários de todos os dígitos separadamente, pois algumas implementações de id fornecerão informações para o ID do usuário nesse caso. Nomes de usuário não podem ser numéricos na maioria dos sistemas (isso quebraria a maioria dos comandos que espere nomes de usuário ou ids de usuário como argumentos (como os id s acima, ps , find ...))).

    
por 28.04.2017 / 11:40
2

Escapar de caracteres especiais é um pouco complicado. Em vez disso, basta escolher o campo de nome de usuário da saída de getent primeiro e, depois, corresponder à linha restante completa:

LC_ALL=C
if ! [[ $user =~ ^[A-Za-z0-9._][A-Za-z._-]*$ ]] ; then
    echo "Username is invalid"
    continue
fi
getent passwd | cut -d: -f1 | grep -xF -e "$user"

-F para cadeias fixas, -x para correspondência de linha completa.

Se você não tiver usuários com nomes de usuário com apenas dígitos, você pode usar getent :

LC_ALL=C
if ! [[ $user =~ ^[A-Za-z0-9._][A-Za-z._-]*$ ]] ; then
    echo "Username is invalid"
    continue
elif [[ $user =~ ^[0-9]+$ ]] ; then
    echo "Cannot handle username of digits only, sorry :("
    continue
fi
if ! getent -- passwd "$user" > /dev/null ; then
    echo "$user doesn't exist"
    continue
fi

Ou, para evitar os problemas com getent ou outros, supondo que uma cadeia de dígitos deve ser um UID em vez de um nome de usuário, devemos chamar getpwnam() manualmente. Isso não faz outras suposições sobre quais nomes de usuários podem ser, além do que a implementação getpwnam() subjacente faz.

export user
if ! perl -e 'exit !defined getpwnam($ENV{user})' ; then 
    echo "$user doesn't exist"
fi

Ignorarei a gravação do wrapper C correspondente.

    
por 28.04.2017 / 09:41