Ich habe dieses kleine Skript in sh
(Mac OSX 10.6), um eine Reihe von Dateien zu durchsuchen. Google ist an dieser Stelle nicht mehr hilfreich:
files="*.jpg"
for f in $files
do
echo $f | grep -oEi '[0-9]+_([a-z]+)_[0-9a-z]*'
name=$?
echo $name
done
Bisher (für Sie Shell-Gurus offensichtlich) gilt $name
lediglich 0, 1 oder 2, je nachdem, ob grep
festgestellt wurde, dass der Dateiname mit der angegebenen Angelegenheit übereinstimmt. Ich möchte erfassen, was sich in den Parens befindet, ([a-z]+)
und dies in einer Variablen speichern .
Ich möchte verwenden , grep
nur, wenn möglich . Wenn nicht, bitte kein Python oder Perl usw. sed
oder ähnliches - ich bin neu in der Shell und möchte dies aus puristischer Sicht angreifen.
Als supercooler Bonu bin ich auch gespannt, wie ich einen String in der Shell verketten kann. Ist die Gruppe, die ich erfasst habe, die in $ name gespeicherte Zeichenfolge "somename", und ich wollte die Zeichenfolge ".jpg" am Ende hinzufügen, oder cat $name '.jpg'
?
Bitte erklären Sie, was los ist, wenn Sie Zeit haben.
grep
,sed
wäre es großartig, wenn es möglich ist, mit zu lösensed
.Antworten:
Wenn Sie Bash verwenden, müssen Sie nicht einmal Folgendes verwenden
grep
:Es ist besser, den regulären Ausdruck in eine Variable zu setzen. Einige Muster funktionieren nicht, wenn sie wörtlich enthalten sind.
Dies verwendet
=~
den Regex-Match-Operator von Bash. Die Ergebnisse der Übereinstimmung werden in einem aufgerufenen Array gespeichert$BASH_REMATCH
. Die erste Erfassungsgruppe wird in Index 1 gespeichert, die zweite (falls vorhanden) in Index 2 usw. Index Null ist die vollständige Übereinstimmung.Sie sollten sich bewusst sein, dass dieser Regex (und der verwendete
grep
) ohne Anker mit einem der folgenden Beispiele und mehr übereinstimmt, die möglicherweise nicht das sind, wonach Sie suchen:Um das zweite und vierte Beispiel zu eliminieren, machen Sie Ihren regulären Ausdruck wie folgt:
was sagt der Zeichenfolge muss beginnen mit einem oder mehreren Ziffern. Das Karat repräsentiert den Anfang der Saite. Wenn Sie am Ende der Regex ein Dollarzeichen hinzufügen, gehen Sie wie folgt vor:
dann wird auch das dritte Beispiel entfernt, da der Punkt nicht zu den Zeichen in der Regex gehört und das Dollarzeichen das Ende der Zeichenfolge darstellt. Beachten Sie, dass das vierte Beispiel auch diese Übereinstimmung nicht besteht.
Wenn Sie GNU haben
grep
(ungefähr 2,5 oder höher, denke ich, als der\K
Operator hinzugefügt wurde):Der
\K
Operator (Look-Behind mit variabler Länge) bewirkt, dass das vorhergehende Muster übereinstimmt, schließt die Übereinstimmung jedoch nicht in das Ergebnis ein. Das Äquivalent fester Länge ist(?<=)
- das Muster würde vor der schließenden Klammer stehen. Sie müssen verwenden ,\K
wenn quantifiers können Strings unterschiedlicher Länge (zB übereinstimmen+
,*
,{2,4}
).Der
(?=)
Operator stimmt mit Mustern fester oder variabler Länge überein und wird als "Vorausschau" bezeichnet. Die übereinstimmende Zeichenfolge ist auch nicht im Ergebnis enthalten.Um die Übereinstimmung zwischen Groß- und Kleinschreibung zu unterscheiden, wird der
(?i)
Operator verwendet. Es beeinflusst die Muster, die ihm folgen, so dass seine Position signifikant ist.Die Regex muss möglicherweise angepasst werden, je nachdem, ob der Dateiname andere Zeichen enthält. Sie werden feststellen, dass in diesem Fall ein Beispiel für die Verkettung einer Zeichenfolge zur gleichen Zeit gezeigt wird, zu der der Teilstring erfasst wird.
quelle
/K
Operator rockt.grep
. Es wurde auch vom OP akzeptiert und ziemlich positiv bewertet. Danke für die Ablehnung.Dies ist mit rein nicht wirklich möglich
grep
, zumindest nicht allgemein.Wenn Ihr Muster jedoch geeignet ist, können Sie es möglicherweise
grep
mehrmals innerhalb einer Pipeline verwenden, um zuerst Ihre Zeile auf ein bekanntes Format zu reduzieren und dann genau das gewünschte Bit zu extrahieren. (Obwohl Werkzeuge dies mögencut
undsed
weitaus besser sind).Nehmen wir zum Zwecke der Argumentation an, dass Ihr Muster etwas einfacher war:
[0-9]+_([a-z]+)_
Sie könnten dies wie folgt extrahieren:Die erste
grep
würde alle Zeilen entfernen, die nicht zu Ihrem Gesamtmuster passen, die zweitegrep
(die--only-matching
angegeben wurde) würde den Alpha-Teil des Namens anzeigen. Dies funktioniert nur, weil das Muster geeignet ist: "Alpha-Teil" ist spezifisch genug, um das herauszuholen, was Sie wollen.(Nebenbei: Persönlich würde ich
grep
+ verwendencut
, um das zu erreichen, wonach Sie suchen :echo $name | grep {pattern} | cut -d _ -f 2
. Dadurch wirdcut
die Zeile durch Aufteilen auf das Trennzeichen in Felder analysiert_
und nur Feld 2 zurückgegeben (Feldnummern beginnen bei 1)).Die Unix-Philosophie besteht darin, Tools zu haben, die eine Sache gut machen und sie kombinieren, um nicht triviale Aufgaben zu erfüllen. Ich würde also argumentieren, dass
grep
+sed
etc eine unixyere Art ist, Dinge zu tun :-)quelle
for f in $files; do name=
echo $ f | grep -oEi '[0-9] + _ ([az] +) _ [0-9a-z] *' | cut -d _ -f 2;
Aha!Mir ist klar, dass eine Antwort darauf bereits akzeptiert wurde, aber aus einem "streng puristischen Blickwinkel" scheint es das richtige Werkzeug für den Job zu sein
pcregrep
, das noch nicht erwähnt worden zu sein scheint. Versuchen Sie, die Zeilen zu ändern:Zu dem Folgendem:
um nur den Inhalt der Erfassungsgruppe zu erhalten 1.
Das
pcregrep
Tool verwendet dieselbe Syntax, die Sie bereits verwendet habengrep
, implementiert jedoch die Funktionen, die Sie benötigen.Der Parameter
-o
funktioniert genau wie diegrep
Version, wenn er leer ist, akzeptiert jedoch auch einen numerischen Parameter inpcregrep
, der angibt, welche Erfassungsgruppe Sie anzeigen möchten.Mit dieser Lösung ist ein Minimum an Änderungen im Skript erforderlich. Sie ersetzen einfach ein modulares Dienstprogramm durch ein anderes und optimieren die Parameter.
Interessanter Hinweis: Sie können mehrere -o-Argumente verwenden, um mehrere Erfassungsgruppen in der Reihenfolge zurückzugeben, in der sie in der Zeile angezeigt werden.
quelle
pcregrep
ist standardmäßig nicht verfügbar,Mac OS X
was das OP verwendetpcregrep
scheint die Ziffer nach dem-o
"Unbekannten Optionsbuchstaben '1' in" -o1 " nicht zu verstehen . Auch diese Funktion wird beim Betrachten nicht erwähntpcregrep --help
7.8 2008-09-05
.echo 'r123456 foo 2016-03-17' | pcregrep -o1 'r([0-9]+)' 123456
pcregrep
8.41 (installiert mitapt-get install pcregrep
onUbuntu 16.03
) erkennt den-Ei
Switch nicht. Ohne funktioniert es jedoch perfekt. Unter macOS,pcregrep
das überhomebrew
(ebenfalls 8.41) installiert wurde, wie oben in @anishpatel erwähnt, wird der-E
Switch zumindest unter High Sierra ebenfalls nicht erkannt.Ich glaube nicht nur in grep möglich
für sed:
Ich werde den Bonus allerdings ausprobieren:
quelle
sed
funktioniert diese Lösung nicht. Es druckt einfach alles in meinem Verzeichnis aus.Dies ist eine Lösung, die Gawk verwendet. Es ist etwas, das ich oft verwenden muss, also habe ich eine Funktion dafür erstellt
einfach zu benutzen
quelle
\s
. Wissen Sie, wie Sie das Problem beheben können?Ein Vorschlag für Sie - Sie können die Parametererweiterung verwenden, um den Teil des Namens ab dem letzten Unterstrich zu entfernen, und zwar zu Beginn:
Dann
name
wird der Wert habenabc
.Suchen Sie in den Apple- Entwicklerdokumenten nach "Parametererweiterung".
quelle
Wenn Sie Bash haben, können Sie Extended Globbing verwenden
oder
quelle