Escape [ в grep

Escape [ в grep

Я читаю строки из stdin и хочу отобразить пользователей, которые соответствуют строкам. Проблема в том, что если пользователь вводит символ '[' или строку, содержащую его.

grep -Fне работает, потому что строка должна начинаться со строки (^ - что является простым символом с -F). Также getent $userэто не будет хорошо, потому что мне нужно только имя пользователя, а не идентификатор.

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

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

Это обходной путь для '[', есть ли другой способ? awkСкорее всего, он справится с задачей, но я пока о нем не знаю, меня интересует grep.

решение1

Либо экранируйте его, либо поместите в класс символов, что-то вроде этого:

grep '\['

grep '[[]'

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

Синтаксис ${var//c/d}=> в переменной оболочки $varмы заменяем все символы cна d. Теперь, в вашем случае is , cно [так уж получилось, что это [является особенным в этом синтаксисе (он выполняет подстановку), и поэтому нам нужно экранировать его, добавив к нему префикс в виде обратной косой черты, т. е. \[.

Теперь перейдем к части замены, нам нужно, чтобы \[там было a. Но опять же, оба \и [являются специальными в этом синтаксисе подстановки ${var//...}параметров, и поэтому оба должны быть, да, вы угадали правильно, с обратной косой чертой, что приводит к выражению: \\\[: "${var//\[/\\\[}"

НТН

решение2

[не единственный символ для экранирования в регулярных выражениях. Все операторы RE включают, .что часто встречается в именах пользователей ( например, r.otкогда регулярное выражение совпадает ).root

Кроме того, ваш подход ( getent passwd | grep "^$user:") также недействителен, поскольку он не будет помечен root:0как недействительный, например.

Здесь лучше использовать awk:

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

Однако не все пользовательские базы данных допускают такое перечисление.

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

В моем случае этот пользователь находится в базе данных LDAP.перечислениеотключено (пользователей могут быть тысячи), но я все равно могу запрашивать/разрешать проблемы пользователей по отдельности.

Так что здесь, для проверки пользователей, лучше запросить базу данных пользователей напрямую для этого пользователя. Например, используя команду id(стандартная команда, в отличие от getent):

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

(мы отдельно рассматриваем пользователей, имена которых состоят только из цифр, поскольку idв этом случае некоторые реализации предоставят вам информацию об идентификаторе пользователя. В большинстве систем имена пользователей не могут быть числовыми (это нарушит работу большинства команд, которые ожидают в качестве аргументов либо имена пользователей, либо идентификаторы пользователей (например, те, что idприведены выше, ps, find...))).

решение3

Экранирование специальных символов — это немного муторно. Вместо этого вы можете просто getentсначала выбрать поле имени пользователя из вывода, а затем сопоставить его с оставшейся строкой:

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для фиксированных строк, -xдля полного совпадения строки.

Если у вас нет пользователей с именами, состоящими только из цифр, вы можете использовать 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

Или, чтобы избежать проблем с getentпредположением, что строка цифр должна быть UID, а не именем пользователя, мы должны назватьgetpwnam()вручную. Это не делает других предположений о том, какими могут быть имена пользователей, кроме тех, которые getpwnam()делает базовая реализация.

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

Я пропущу написание соответствующей оболочки на языке C.

Связанный контент