Match-Zero-or-More-Operator beim Shell-Globbing

9

Ich stecke hier in einem ziemlich trivialen Problem fest: Wie kann ich das *Symbol in Bash auf Null oder mehr bringen , wie es in Tools wie z sed.

Zum Beispiel ak*sollte jede Datei übereinstimmen, deren Name vollständig aus einem agefolgt von null oder mehr ks besteht. Seine Ausdehnung würde a, ak, akk, und akkk, aber nicht akc.

Ich habe es bereits unsetopt sh_globin zsh und set -o noglobin bash versucht ; Sie erzeugten nicht das gewünschte Verhalten.

Kira
quelle
Muss es glob sein?
Heemayl
Ich bin mir eigentlich nicht sicher, ich wollte nur darauf hinweisen, was ich bereits versucht habe. Ich hatte den Titel geändert, um das widerzuspiegeln.
Kira
Das gesuchte Wort ist "Globbing" oder "Wildcards" (letzteres hängt möglicherweise vom Kontext ab).
Phk
Ähm, die Regex ak*in sedwürde total übereinstimmen akc(und auch nur a).
Thrig
@ Thrig, ^ak+$oder ^akk*$würde funktionieren.
Cas

Antworten:

10

Abgesehen davonksh93 hat keine der üblichen Shells reguläre Ausdrücke mit der gleichen Syntax wie sed, awk usw., die zum Abgleichen von Dateien verwendet werden können.

Ksh93, bash und zsh haben reguläre Ausdrücke mit einer anderen Syntax, die mit Globs abwärtskompatibel ist:

  • ?stimmt mit einem einzelnen Zeichen .überein (wie in der üblichen Regexp-Syntax)
  • […] Entspricht einem Zeichensatz größtenteils auf die gleiche Weise
  • *(FOO)entspricht einer beliebigen Anzahl von Vorkommen von FOO(wie in der üblichen Regexp-Syntax)(FOO)*
  • Entspricht in ähnlicher Weise einem oder mehreren Vorkommen und null oder einem Vorkommen+(FOO)?(FOO)
  • @(FOO|BAR)passt entweder FOOoderBAR
  • Übereinstimmungen gelten für die gesamte Zeichenfolge, nicht für einen Teilstring. Wenn Sie einen Teilstring wünschen, setzen Sie ihn *am Anfang und am Ende

Diese Syntax muss mit shopt -s extglobin bash und mit setopt ksh_globin zsh aktiviert werden . Also würdest du in Bash schreiben

shopt -s extglob
ls a*(k)

Siehe auch Warum funktioniert mein regulärer Ausdruck in X, aber nicht in Y?

Ksh93, zsh und bash können mit dem =~Operator des [[ … ]]Konstrukts reguläre Ausdrücke mit erweiterten regulären Ausdrücken (im Grunde die Syntax von awk) für Zeichenfolgen abgleichen . Dies ist zwar nicht praktisch, um Dateien aufzulisten, aber wenn Sie es wirklich wollen, können Sie es tun.

shopt -s dotglob  # <<< include dot files, for bash
setopt globdots   # <<< include dot files, for zsh
FIGNORE='@(.|..)' # <<< include dot files, for ksh
for x in *; do
  if [[ $x =~ ^ak*$ ]]; then
    
  fi
done
Gilles 'SO - hör auf böse zu sein'
quelle
Eine einfache Antwort wie ls a*(k)würde Ihnen bereits eine akzeptierte Antwort geben. Vielen Dank, dass Sie sich die Zeit genommen haben, diese gründliche Antwort zu geben.
Kira
1
Sind Sie sicher, dass Ihre dritte Kugel korrekt ist? a(k)gibt Syntaxfehler, daher nehme ich an, dass (foo) allein nicht die gesamte Syntax für diesen Teil ist.
Kira
1
@Kira Das hat mich auch verwirrt. Wenn man jedoch die Quelle der Antwort betrachtet , scheint es, dass Gilles *(foo)eher meinte als (foo). Ich habe eine Bearbeitung vorgeschlagen , um die Formatierung zu korrigieren.
Blacklight Shining
ksh93Globs können reguläre Ausdrücke verwenden: echo ~(E:ak*)für ERE.
Stéphane Chazelas
5

ls ak{k,}zeigt Dateien an, die mit beginnen, akgefolgt von einem anderen koder nichts.

$ touch ak akk akc
$ ls -l ak{k,}
-rw-rw-r-- 1 cas cas 0 Oct 27 10:30 ak
-rw-rw-r-- 1 cas cas 0 Oct 27 10:30 akk

Globs sind keine regulären Ausdrücke, aber sie bieten mehr als nur *und ?.

Wenn Sie reguläre Ausdrücke verwenden möchten, um passende Dateinamen zu finden, können Sie den folgenden findBefehl verwenden:

$ find . -maxdepth 1 -type f -regex './ak+$' 
./ak
./akk

Die -maxdepth 1Option beschränkt die Suche nur auf das aktuelle Verzeichnis (es werden keine Unterverzeichnisse durchsucht).

Wenn Sie Suchanfragen ohne Berücksichtigung der Groß- und Kleinschreibung wünschen, verwenden Sie -iregexstatt -regex.

Es gibt zahlreiche Methoden zur Verwendung der Dateien, die findin anderen Befehlen gefunden wurden. Zum Beispiel:

find . -maxdepth 1 -type f -regex './ak+$' -ls
find . -maxdepth 1 -type f -regex './ak+$' -exec ls -ld {} +
find . -maxdepth 1 -type f -regex './ak+$' -print0 | xargs -0r ls -ld
ls -ld $(find . -maxdepth 1 -type f -regex './ak+$')

Das letzte Beispiel ist anfällig für verschiedene Fehlermodi, darunter 1. Nichtbewältigung von Leerzeichen usw. in Dateinamen, 2. Begrenzung der Befehlszeilenlänge. nicht empfohlen.

cas
quelle
Ich habe genau das im Sinn, aber ich denke, OP möchte eine allgemeinere Lösung, die nicht an bestimmte Dateinamen gebunden ist.
heemayl
Ihre Antwort ist etwas zu spezifisch, ich möchte eine beliebige Anzahl von k's haben. Ich werde die Frage bearbeiten, um dies widerzuspiegeln.
Kira
@heemayl, wie bei regulären Ausdrücken, müssen Globs für die genauen Umstände wie erforderlich hergestellt werden. @kira, siehe man 7 glob- im Gegensatz zu Regex hat Glob keinen Modifikator für Null oder mehr oder 1 oder mehr.
Cas
2

In bashder Syntax können Sie verwenden , ist: ls a+(k) Das hängt von der bash shoptShell - Option extglobaktiviert ist. Unter Ubuntu 14.04 GNU / Linux scheint dies standardmäßig aktiviert zu sein.

So funktioniert es:

$ shopt extglob
extglob         on
$ ls
ak  akc  akd  akk  akkk  akkkk
$ ls a+(k)
ak  akk  akkk  akkkk
$ shopt -u extglob
$ shopt extglob
extglob         off
$ ls a+(k)
bash: syntax error near unexpected token `('
$

Aus dem Bash-Handbuch:

+ (Musterliste)

Entspricht einem oder mehreren Vorkommen der angegebenen Muster.

Eine Musterliste ist eine Liste von einem oder mehreren Mustern, die durch ein '|' getrennt sind.

Weitere Informationen finden Sie im Bash-Handbuch unter https://www.gnu.org/software/bash/manual/bash.html#Pattern-Matching .

RobertL
quelle
2

Einige Optionen je nach Shell:

$ touch a akk aka
$ ksh -c 'echo a*(k)'
a akk
$ zsh -o kshglob -o nobareglobqual -c 'echo a*(k)'
a akk

(Damit nobareglobqualdieser Trailing hier (k)nicht als Glob-Qualifier angesehen wird)

$ bash -O extglob -c 'echo a*(k)'
a akk

$ zsh -o extendedglob -c 'echo ak#'
a akk

zsh's #ist das Äquivalent von Regexp *.

ksh93 kann auch verschiedene Arten von regulären Ausdrücken in seinen Globs verwenden:

$ ksh93 -c 'echo ~(E:ak*)' # extended RE
a akk
$ ksh93 -c 'echo ~(P:ak*)' # perl-like RE
a akk
$ ksh93 -c 'echo ~(X:ak*)' # AT&T augmented RE
a akk
$ ksh93 -c 'echo @(~(E)ak*)' # alternative syntax
a akk
Stéphane Chazelas
quelle