
Я читаю строки из 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.