Warum kann -regex nicht mit einer neuen Zeile übereinstimmen?

7

Warum scheitert das?

touch "$(printf "a\nb")"; find . -regex './.\n.'

Ich habe auch diese ausprobiert, von denen keine funktioniert:

find . -regextype posix-extended -regex '.\n.'
find . -regextype posix-awk -regex '.\n.'
find . -regextype posix-basic -regex '.\n.'
find . -regextype posix-egrep -regex '.\n.'

Der einzige Weg, wie es zu funktionieren scheint, ist (danke @MichaelMrozek)

find . -regex './.'$'\n''.'

Was gelinde gesagt umständlich ist. Warum scheinen die regulären Ausdrücke von find nicht in der Lage zu sein, damit umzugehen \n?


Update als Antwort auf die bisherigen Antworten:

OK, ich verstehe, dass dies \nnicht Teil von ERE ist und dass dies eines meiner Missverständnisse war, sondern den findAnspruch auf Unterstützung posix-awkund beides gawkund mawkÜbereinstimmung \nwie erwartet:

$ printf "f1l1\nhas newline:f2l1#f1l2 does not:f2l2#" | 
    mawk -F: 'BEGIN{RS="#"}; ($1~/\n/){print $1}' 
f1l1
has newline

Ich habe kein reines awkzum Testen, also awkpasst POSIX vielleicht nicht zusammen? Andernfalls werden findkeine posix-awkregulären Ausdrücke implementiert ?

terdon
quelle
Ist das auch find . -name $'*\n*'umständlich?
devnull
Überhaupt nicht, nur irrelevant. Ich frage mich, warum -regexfehlschlägt, nicht Wie finde ich Dateien, die Zeilenumbruch im Dateinamen enthalten? was du perfekt beantwortet hast :).
Terdon
Nach der Diskussion dieses mit einigen anderen Usern im Chat, scheint es wahrscheinlich , dass die awkRegex Sprache nicht kennt , \nsondern dass der awkDolmetscher tut und das ist , warum es übereinstimmt. Daher würde die Implementierung von awkregulären Ausdrücken findnicht bedeuten, dass dies \nübereinstimmen sollte. Danke euch allen!
Terdon

Antworten:

14

Weil GNU find keine \nEscape-Sequenz unterstützt. Der reguläre Ausdruck \nentspricht dem Zeichen n. GNU find kopiert die traditionelle Emacs-Syntax, die diese Funktion ebenfalls nicht bietet¹.

Während GNU find andere Regex-Syntax unterstützt, unterstützt keine Backslash-Letter oder Backslash-Octal, um Steuerzeichen zu kennzeichnen. Sie müssen das Steuerzeichen buchstäblich in das Argument aufnehmen.

Es gibt viele verschiedene Regex-Syntaxen. Weder reguläre reguläre POSIX- Ausdrücke (BRE) noch erweiterte reguläre Ausdrücke (ERE) enthalten \noder Backslash-Octal-Escapezeichen. Beide Definitionen lassen die Bedeutung eines Backslashs unberührt, wenn kein Sonderzeichen folgt. Die Dienstprogramme awk und sed unterstützen beide \n, um eine neue Zeile zu bedeuten; Dies ist spezifisch für diese Dienstprogramme (und alltäglich, aber wie Sie sehen, nicht universell).

Aus einem Shell-Skript können Sie schreiben

find . -regex $'./.\n.'     # ksh/bash/zsh only
find . -regex './.
.'
find . -name '*
*'

¹ Ganz logisch: Für die interaktive Verwendung können Sie jedes Zeichen mit C-qeingeben. für die Programmierung \nals Teil der String-Literal-Syntax vorhanden.

Gilles 'SO - hör auf böse zu sein'
quelle
Sind grundlegende reguläre Ausdrücke und erweiterte reguläre Ausdrücke nicht die Art, in der sie verwendet werden grep?
Melab
@Melab grepverwendet standardmäßig BRE oder ERE mit der Option -E.
Gilles 'SO - hör auf böse zu sein'
6

Sie können eine neue Zeile nicht mit '\ n' abgleichen , da sie in einem regulären Ausdruck keine besondere Bedeutung hat (z. B. Zeilenumbruch), aber Sie können das Zeilenende mit dem regulären Ausdruck $ abgleichen.

Babasbot
quelle
\nMit Sicherheit hat es eine Bedeutung in einem regulären Ausdruck, versuchen Sie es printf "aa\nbb" | perl -ne 'print if /\n/', die nur übereinstimmt aa\nund überspringt bbzum Beispiel die. Es scheint Unterschiede in der Implementierung zu geben, obwohl die Ursache grep -Pnicht dazu passt. Aber wie ist das $hier relevant? Ich möchte eine wörtliche Newline finden, $die auch ohne eine printf "aa" | grep 'a$'
übereinstimmt
1
@terdon \nhat keine besondere Bedeutung, auch nicht in regulären Perl-Ausdrücken. Es hat jedoch eine besondere Bedeutung in interpolierten Perl-Strings, von denen qr//es sich um einen Typ handelt. Suche nach \nin man perlre...
derobert
@derobert fair point, ich habe mich schlecht ausgedrückt. Ich meinte, das \npasst zu Zeilenumbrüchen in regulären Ausdrücken. Sie und babaslovesyou haben ganz recht, dass es als solches keine besondere Bedeutung hat, ich meine nur, dass es "passend" ist.
Terdon
2
@terdon Nun, außer Sie versuchen, das Zeichen 0x0A (Newline) zuzuordnen, und Sie versuchen es mit der Zeichenfolge 0x5C (Backslash) 0x6E (n). Da \ n keine besondere Bedeutung hat, versucht es, sich selbst anzupassen. Je nach RE-Engine wird das \ möglicherweise entfernt (ungültiges Escapezeichen) oder nicht, aber Sie versuchen, <NL> mit \ n oder n abzugleichen, und beide stimmen nicht überein.
Derobert
2
@terdon In Ihrem Perl-Beispiel passiert tatsächlich, dass die String-Analyse \nin <NL> umgewandelt wird, bevor sie an die Regexp-Engine weitergegeben wird. Dies ist eine Funktion der Perl-String-Analyse.
Derobert
1

Ich denke, weil die findVerwendung der fnmatchFunktion in der Standard-C-Bibliothek, wenn sie FNM_NOESCAPEnicht festgelegt ist, ein Backslash-Zeichen im Muster gefolgt von einem anderen Zeichen mit dem zweiten Zeichen in der Zeichenfolge übereinstimmt.

FNM_NOESCAPE

Don't treat the `\' character specially in patterns. Normally, `\' quotes
the following character, turning off its special meaning (if any) so that it 
matches only itself. When quoting is enabled, the pattern `\?' matches only 
the string `?', because the question mark in the pattern acts like an 
ordinary character. If you use FNM_NOESCAPE, then `\' is an ordinary character.

Ich überprüfe mit find (GNU findutils) 4.4.2und glibc 2.15, diese Option ist deaktiviert. Check line 42in fnmatch.h:

#define FNM_NOESCAPE    (1 << 1) /* Backslashes don't quote special chars.  */
cuonglm
quelle
fnmatchist für die *.txtArt der Muster, nicht für die .*\.txt$Art der regulären Ausdrücke.
Stéphane Chazelas