Discussion:
* in pathname pattern
(zu alt für eine Antwort)
Marcel Logen
2025-02-17 16:50:54 UTC
Permalink
Folgende Situation:

| ***@o15:/tmp$ pwd
| /tmp

| ***@o15:/tmp$ mkdir dir

| ***@o15:/tmp$ cd dir

| ***@o15:/tmp/dir$ mkdir abc def ghi

| ***@o15:/tmp/dir$ ( for i in . abc def ghi ; do touch ${i}/sample.txt ; done ) && ls -l *
| -rw-r--r-- 1 user15 user15 0 Feb 17 17:38 sample.txt
|
| abc:
| total 0
| -rw-r--r-- 1 user15 user15 0 Feb 17 17:38 sample.txt
|
| def:
| total 0
| -rw-r--r-- 1 user15 user15 0 Feb 17 17:38 sample.txt
|
| ghi:
| total 0
| -rw-r--r-- 1 user15 user15 0 Feb 17 17:38 sample.txt

| ***@o15:/tmp/dir$ find . -name 'sample.txt'
| ./sample.txt
| ./ghi/sample.txt
| ./abc/sample.txt
| ./def/sample.txt

| ***@o15:/tmp/dir$ printf '%s\n' /tmp/dir/*/sample.txt
| /tmp/dir/abc/sample.txt
| /tmp/dir/def/sample.txt
| /tmp/dir/ghi/sample.txt

| ***@o15:/tmp/dir$ printf '%s\n' /tmp/dir/{*,}/sample.txt
| /tmp/dir/abc/sample.txt
| /tmp/dir/def/sample.txt
| /tmp/dir/ghi/sample.txt
| /tmp/dir//sample.txt
| ***@o15:/tmp/dir$

man bash
<https://manpages.debian.org/bookworm/bash/bash.1.en.html#Pathname_Expansion>

| ***@o15:/tmp/dir$ bash --version | head -n 1
| GNU bash, version 5.2.15(1)-release (x86_64-pc-linux-gnu)

man ksh
<https://man.openbsd.org/ksh#File_name_patterns>

| This page documents version @(#)PD KSH v5.2.14 99/07/13.2
| of the public domain Korn shell.

Laut den man pages steht der Stern für eine beliebige
Anzahl von Zeichen (IMHO eben auch für keines).

Warum 'findet' das pathname pattern (unter ksh und bash)

/tmp/dir/*/sample.txt

nicht das file "sample.txt" auf der oberen Ebene?

Marcel (Lines: 71)
--
╭─╮ ╭───────╮ ╭───────╮ ╭───╮ ╭─╮ ╭─────╮ ╭─────────╮ ..67..
─╯ ╰──╯ ╭────╯ ╰─╮ ╭──╯ │ ╰─╯ ╰─╯ ╭──╯ │ ╭──────╯ ╭─╮
╰────╮ ╭─╮ │ ╰──╮ ╭─╯ ..41..╰───╮ │ │ ╭─╮ ╭──╯ │ ╭
╰──╯ ╰──╯ ╰──╯ ..45..╰─╯ ╰───╯ ╰─╯ ╰──╯
Peter J. Holzer
2025-02-17 17:25:24 UTC
Permalink
[...]
Post by Marcel Logen
| ./sample.txt
| ./ghi/sample.txt
| ./abc/sample.txt
| ./def/sample.txt
| /tmp/dir/abc/sample.txt
| /tmp/dir/def/sample.txt
| /tmp/dir/ghi/sample.txt
[...]
Post by Marcel Logen
Laut den man pages steht der Stern für eine beliebige
Anzahl von Zeichen (IMHO eben auch für keines).
... in einem Filenamen.
Post by Marcel Logen
Warum 'findet' das pathname pattern (unter ksh und bash)
/tmp/dir/*/sample.txt
nicht das file "sample.txt" auf der oberen Ebene?
Weil da kein Subdirectory mit einem leeren Namen ist. Wenn Du ein
solches anlegen könntest (was aber nicht erlaubt ist), würde es auch
gefunden.

hp
Marcel Logen
2025-02-17 18:34:03 UTC
Permalink
Post by Peter J. Holzer
Post by Marcel Logen
Laut den man pages steht der Stern für eine beliebige
Anzahl von Zeichen (IMHO eben auch für keines).
... in einem Filenamen.
Nur bei Filenamen?

Dazu habe ich in den man pages nichts gefunden.
Post by Peter J. Holzer
Post by Marcel Logen
Warum 'findet' das pathname pattern (unter ksh und bash)
/tmp/dir/*/sample.txt
nicht das file "sample.txt" auf der oberen Ebene?
Weil da kein Subdirectory mit einem leeren Namen ist. Wenn Du ein
solches anlegen könntest (was aber nicht erlaubt ist), würde es auch
gefunden.
Klingt plausibel. Danke.

Ich muß darüber aber noch mal nachdenken.

Ausgangspunkt für mich war übrigens
<https://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/www/faq/current.html?rev=1.1127&content-type=text/html>,
dort der Eintrag vom 27.01.2025.

Da wollte ich vor Ausführung des Befehls erst mal nachsehen,
was eigentlich 'Sache' ist.

Matcel
Marcel Logen
2025-02-17 19:08:35 UTC
Permalink
Post by Marcel Logen
Post by Peter J. Holzer
Post by Marcel Logen
Laut den man pages steht der Stern für eine beliebige
Anzahl von Zeichen (IMHO eben auch für keines).
... in einem Filenamen.
Nur bei Filenamen?
Dazu habe ich in den man pages nichts gefunden.
Das spielt hier auch offenbar keine Rolle.
Post by Marcel Logen
Post by Peter J. Holzer
Post by Marcel Logen
Warum 'findet' das pathname pattern (unter ksh und bash)
/tmp/dir/*/sample.txt
nicht das file "sample.txt" auf der oberen Ebene?
Weil da kein Subdirectory mit einem leeren Namen ist. Wenn Du ein
solches anlegen könntest (was aber nicht erlaubt ist), würde es auch
gefunden.
Klingt plausibel. Danke.
Ich muß darüber aber noch mal nachdenken.
Inzwischen habe ich mal das hier ausprobiert:

| ***@o15:/tmp/dir2$ ls -1F
| abc/
| ac/
| ***@o15:/tmp/dir2$

| ***@o15:/tmp/dir2$ printf '%s\n' /tmp/dir2/a*c/samp
| /tmp/dir2/abc/samp
| /tmp/dir2/ac/samp
| ***@o15:/tmp/dir2$

Das stützt m. E. Deine Erläuterung zum "Subdirectory mit leerem
Namen". Und es gibt keinen Unterschied zu Filenamen.

Marcel
Peter J. Holzer
2025-02-17 22:44:07 UTC
Permalink
Post by Marcel Logen
Post by Peter J. Holzer
Post by Marcel Logen
Laut den man pages steht der Stern für eine beliebige
Anzahl von Zeichen (IMHO eben auch für keines).
... in einem Filenamen.
Nur bei Filenamen?
Dazu habe ich in den man pages nichts gefunden.
Interessant. So wirklich eindeutig steht das anscheinend wirklich
nirgends. Die Bash-Manpage ist in der Hinsicht besonders schlecht, weil
die Bash so viele Optionen hat, die das Globbing beeinflussen. Die
Man-Page der Dash ist besser, aber die kann man auch falsch auslegen.
Und selbst der POSIX-Standard lässt Interpretationsspielraum:

| 2.14.2 Patterns Matching Multiple Characters
|
| 1. The <asterisk> ('*') is a pattern that shall match any string,
| including the null string.
[...]
| 2.14.3 Patterns Used for Filename Expansion
|
| 1. The <slash> character in a pathname shall be explicitly matched by
| using one or more <slash> characters in the pattern; it shall neither
| be matched by the <asterisk> or <question-mark> special characters nor
| by a bracket expression.


Wenn das Pattern /a/*/b lautet und der Pathname /a/b, dann könnte man
das so auslegen:

1. Der * matcht jeden String inlusive des Substrings, also ist das
Pattern äquivalent zu einer unendlichen Menge von Patterns, die unter
anderem /a//b, /a/a/b, /a/b/b, etc. enthält.

2. Das Pattern /a//b matcht den Pathname /a/b, weil jeder / im Pathname
»explicitly matched by one or more <slash> characters in the pattern«
ist.

3. Also matcht das Pattern /a/*/b den Pathname /a/b, so wie Du erwartet
hast.

Diese Auslegung ist falsch (so hat sich m.W. noch nie eine Shell
verhalten), aber nach dem Buchstaben der Norm IMHO möglich.

Die intendierte Auslegung ist, dass man das Pattern in Atome zerlegt,
die einzeln matchen müssen.

1. Das Pattern /a/*/b besteht aus den Atomen »/«, »a«, »/«, »*«, «/«,
«b«.
2. Egal, ob man jetzt von vorn oder von hinten anfängt und wieviel *
matcht, es geht sich nie aus:
/ ~ /
a ~ a
/ ~ /
* ~ ""
/ !~ b

/ ~ /
a ~ a
/ ~ /
* ~ b
/ !~ ""

b ~ b
/ ~ /
* ~ ""
/ != /a

...

Das Pattern /a//b würde hingegen matchen, weil »one or more <slash>
characters« ein Atom bilden, das einen einzelnen / matcht:
/ ~ /
a ~ a
// ~ /
b ~ b

hp
Thomas Dorner
2025-02-18 08:27:57 UTC
Permalink
Post by Peter J. Holzer
| 2.14.2 Patterns Matching Multiple Characters
|
| 1. The <asterisk> ('*') is a pattern that shall match any string,
| including the null string.
[...]
| 2.14.3 Patterns Used for Filename Expansion
|
| 1. The <slash> character in a pathname shall be explicitly matched by
| using one or more <slash> characters in the pattern; it shall neither
| be matched by the <asterisk> or <question-mark> special characters nor
| by a bracket expression.
Wenn das Pattern /a/*/b lautet und der Pathname /a/b, dann könnte man
1. Der * matcht jeden String inlusive des Substrings, also ist das
Pattern äquivalent zu einer unendlichen Menge von Patterns, die unter
anderem /a//b, /a/a/b, /a/b/b, etc. enthält.
2. Das Pattern /a//b matcht den Pathname /a/b, weil jeder / im Pathname
»explicitly matched by one or more <slash> characters in the pattern«
ist.
Das "/a//b" ist aber nicht mehr das Pattern, sondern das bereits einmal
interpretierte Pattern.

(Ansonsten könnte man ja noch auf die Idee kommen, daß "/a/\*/b" ->
"/a/*/b" -> "/a//b" -> "/a/b" matcht, was ich ziemlich gruselig finde.
;-)

Viele Grüße, Thomas
--
Adresse gilt nur kurzzeitig!
Christian Weisgerber
2025-02-17 19:27:35 UTC
Permalink
Post by Marcel Logen
Warum 'findet' das pathname pattern (unter ksh und bash)
/tmp/dir/*/sample.txt
nicht das file "sample.txt" auf der oberen Ebene?
Weil die Pfadnamen-Expansion der Shell pfadkomponentenweise arbeitet.
Post by Marcel Logen
man bash
<https://manpages.debian.org/bookworm/bash/bash.1.en.html#Pathname_Expansion>
"When matching a pathname, the slash character must always be matched
explicitly by a slash in the pattern"
Post by Marcel Logen
man ksh
<https://man.openbsd.org/ksh#File_name_patterns>
"Note that none of the above pattern elements match either a period (‘.’)
at the start of a file name or a slash (‘/’),"
--
Christian "naddy" Weisgerber ***@mips.inka.de
Helmut Waitzmann
2025-02-18 21:51:21 UTC
Permalink
Marcel Logen <333200007110-***@ybtra.de>:


Es seien folgende Dateien im Dateisystem vorhanden:


/tmp/dir/sample.txt
/tmp/dir/abc/sample.txt
/tmp/dir/def/sample.txt
/tmp/dir/ghi/sample.txt
Post by Marcel Logen
Warum 'findet' das pathname pattern (unter ksh und bash)
/tmp/dir/*/sample.txt
nicht das file "sample.txt" auf der oberen Ebene?
Weil das Shell sich vermutlich nicht alle möglichen auf das
angegebene Muster passenden Pfadnamen ausdenkt und bei jedem
ausgedachten Pfadnamen anschließend prüft, ob er vielleicht im
Dateisystem vorhanden ist (Da hätte es angesichts der immensen
Menge an möglichen Pfadnamen viel zu tun!), sondern umgekehrt
komponentenweise das Dateisystem durchsucht und bei jedem im
Dateisystem gefundenen Pfadnamen prüft, ob er auf das angegebene
Muster passt.


Auf den Pfadnamen „/tmp/dir//sample.txt“ wird es aber beim
Durchsuchen des Dateisystems nicht stoßen.  Deshalb wird es ihn
auch nicht als Treffer erkennen, obwohl dieser Pfadname für das
Betriebssystem dasselbe wie der Pfadname „/tmp/dir/sample.txt“,
der im Dateisystem vorhanden ist, bedeutet.
Helmut Waitzmann
2025-02-19 14:19:23 UTC
Permalink
Post by Helmut Waitzmann
/tmp/dir/sample.txt
/tmp/dir/abc/sample.txt
/tmp/dir/def/sample.txt
/tmp/dir/ghi/sample.txt
Post by Marcel Logen
Warum 'findet' das pathname pattern (unter ksh und bash)
/tmp/dir/*/sample.txt
nicht das file "sample.txt" auf der oberen Ebene?
Weil das Shell sich vermutlich nicht alle möglichen auf das
angegebene Muster passenden Pfadnamen ausdenkt und bei jedem
ausgedachten Pfadnamen anschließend prüft, ob er vielleicht im
Dateisystem vorhanden ist (Da hätte es angesichts der immensen
Menge an möglichen Pfadnamen viel zu tun!), sondern umgekehrt
komponentenweise das Dateisystem durchsucht und bei jedem im
Dateisystem gefundenen Pfadnamen prüft, ob er auf das angegebene
Muster passt.
Auf den Pfadnamen „/tmp/dir//sample.txt“ wird es aber beim
Durchsuchen des Dateisystems nicht stoßen.  Deshalb wird es ihn
auch nicht als Treffer erkennen, obwohl dieser Pfadname für das
Betriebssystem dasselbe wie der Pfadname „/tmp/dir/sample.txt“,
der im Dateisystem vorhanden ist, bedeutet.
Denselben Effekt kann man auch bei „find“ beobachten.  Da sieht
man besser, dass Dateisystemdurchsuchung und
Pfadnamensmusterpassung zwei nach einander unabhängig ablaufende
Vorgänge sind:


(
cd -- dir &&
find ./ -path './*/sample.txt' -print
)

wird keinen Treffer für „.//sample.txt“ ausgeben, während


(
cd -- dir &&
find .// -path './*/sample.txt' -print
)


das tun wird. 


=> Das „find“‐Pfadnamenmuster „./*/sample.txt“ passt durchaus auf
den Pfad „.//sample.txt“.


Der Grund ist auch hier, dass „find“ bei einem Dateibaumparameter
„./“ nicht auf die Idee kommen wird, auch den Dateibaum „.//“
(der für das Betriebssystem dasselbe bedeutet) zu durchsuchen.
--
Hat man erst verstanden, wie Unix funktioniert, ist auch
das Shell-Handbuch kein Buch mit sieben Siegeln mehr.
Loading...