Escape [ in grep

Escape [ in grep

Ich lese Zeichenfolgen von stdin und möchte die Benutzer anzeigen, die mit den Zeichenfolgen übereinstimmen. Das Problem besteht, wenn der Benutzer das Zeichen „[“ oder eine Zeichenfolge eingibt, die dieses enthält.

grep -Ffunktioniert nicht, da die Zeile mit der Zeichenfolge (^ - was ein einfaches Zeichen mit -F ist) beginnen muss. getent $userWäre auch nicht gut, da ich nur den Benutzernamen und nicht die ID brauche.

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

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

Dies ist die Problemumgehung für „[“. Gibt es eine andere Möglichkeit? awkWird höchstwahrscheinlich funktionieren, aber ich weiß noch nichts darüber. Mich interessiert grep.

Antwort1

Entweder maskieren Sie es oder fügen es in eine Zeichenklasse ein, etwa so:

grep '\['

grep '[[]'

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

Die Syntax ${var//c/d}=> in der Shell-Variable $varersetzen wir alle Zeichen cdurch d. In Ihrem Fall cist das , [aber es ist nun mal so, dass [in dieser Syntax etwas Besonderes ist (es führt Globbing durch) und daher müssen wir es maskieren, indem wir ihm einen Backslash voranstellen, also \[.

Kommen wir nun zum Ersetzungsteil. Was wir brauchen, ist ein \[in. Aber nochmals, sowohl \als auch [sind in dieser Syntax der Parameterersetzung etwas Besonderes ${var//...}und daher müssen beide, ja, Sie haben es richtig erraten, mit Backslashs versehen werden, was zu folgendem Ausdruck führt: \\\[: "${var//\[/\\\[}"

HTH

Antwort2

[ist nicht das einzige Zeichen, das bei regulären Ausdrücken maskiert wird. Alle RE-Operatoren sind inklusive, .was bei Benutzernamen üblich ist ( z. B. r.otwenn ein regulärer Ausdruck übereinstimmt root).

Darüber hinaus ist Ihr Ansatz ( ) auch insofern ungültig, als er beispielsweise getent passwd | grep "^$user:"nicht als ungültig gekennzeichnet würde .root:0

Hier wäre es besser, Folgendes zu verwenden awk:

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

Allerdings erlauben nicht alle Benutzerdatenbanken eine solche Aufzählung.

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

In meinem Fall befindet sich dieser Benutzer in einer LDAP-Datenbank.Aufzählungist deaktiviert (es könnte Tausende von Benutzern geben), aber ich kann Benutzer immer noch einzeln abfragen/auflösen.

Um Benutzer zu validieren, ist es hier besser, die Benutzerdatenbank direkt nach diesem Benutzer abzufragen. Verwenden Sie beispielsweise den folgenden idBefehl (im Gegensatz zu ein Standardbefehl getent):

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

(Wir kümmern uns separat um Benutzer, die nur aus Ziffern bestehen, da einige idImplementierungen Ihnen in diesem Fall Informationen zur Benutzer-ID geben würden. Benutzernamen können in den meisten Systemen nicht numerisch sein (das würde die meisten Befehle beschädigen, die entweder Benutzernamen oder Benutzer-IDs als Argumente erwarten (wie die idoben stehenden s, ps, find...))).

Antwort3

Das Maskieren von Sonderzeichen ist etwas mühsam. Stattdessen können Sie getentzunächst einfach das Feld username aus der Ausgabe von auswählen und dann mit der gesamten verbleibenden Zeile abgleichen:

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"

-Ffür feste Zeichenfolgen, -xfür vollständige Zeilenübereinstimmung.

Wenn Sie keine Benutzer haben, deren Benutzernamen nur aus Ziffern bestehen, können Sie Folgendes verwenden 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

Oder, um die Probleme mit getentoder anderen zu vermeiden, die annehmen, dass eine Ziffernfolge eine UID statt eines Benutzernamens sein muss, sollten wir anrufengetpwnam()manuell. Dabei werden keine anderen Annahmen über die möglichen Benutzernamen getroffen als die, die die zugrunde liegende getpwnam()Implementierung vornimmt.

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

Ich werde das Schreiben des entsprechenden C-Wrappers überspringen.

verwandte Informationen