Bash kombiniert Wildcard-Erweiterung mit Klammererweiterung

8

Ich versuche, eine Zeichenfolge mit einem Platzhalter und einer Sammlung von Erweiterungen in geschweiften Klammern zu erweitern. Nichts scheint zu funktionieren, wie das folgende Beispiel zeigt. die Variable firstListexpandiert in Ordnung, aber weder secondList, thirdListoder fourthListerweitert richtig. Ich habe auch verschiedene Versionen von ausprobiert, evalaber auch keine. Jede Hilfe wäre dankbar

#!/bin/bash
touch a.ext1
touch b.ext1
firstList='*.ext1'
ls  $firstList
touch a.ext2
touch b.ext2
secondList='*.{ext1,ext2}'
ls $secondList 
ls '$secondList'
ls "$secondList"
thirdList=*.{ext1,ext2}
ls $thirdList  
ls '$thirdList'
ls "$thirdList"
fourthList="*.{ext1,ext2}"
ls $fourthList
ls '$fourthList'
ls "$fourthList"
Leo Simon
quelle
fwiw eval ls $secondListfunktioniert hier gut ... was versuchst du zu erreichen?
don_crissti
Sie müssen die Reihenfolge der Bash-Erweiterungen überprüfen. Die Klammererweiterung erfolgt vor den Parametererweiterungen. Um den gewünschten Effekt zu erzielen, müssen Sie evaleine zweite Erweiterungsrunde durchführen.
Glenn Jackman
Betrüger davon? unix.stackexchange.com/q/117144/28489
Runium
@glennjackman Es gibt eine Lösung ohne Bewertung. Schauen Sie sich die zweite Lösung am Ende meiner Antwort an .

Antworten:

7

Die Shell wird* nur erweitert , wenn sie nicht in Anführungszeichen gesetzt ist. Bei Anführungszeichen wird die Erweiterung durch die Shell gestoppt.

Außerdem muss eine Klammererweiterung nicht zitiert werden, um von der Shell erweitert zu werden.

Diese Arbeit (verwenden wir Echo, um zu sehen, was die Shell tut):

$ echo *.{ext1,ext2}
a.ext1 b.ext1 a.ext2 b.ext2

Auch wenn es Dateien mit anderen Namen gibt:

$ touch {a,b}.{ext1,ext2} {c,d}.{ext3,ext4} none
ls
a.ext1  a.ext2  b.ext1  b.ext2  c.ext3  c.ext4  d.ext3  d.ext4  none

$ echo *.{ext1,ext2}
a.ext1 b.ext1 a.ext2 b.ext2

Warum funktioniert das?

Es ist wichtig, dass wir verstehen, warum das funktioniert. Es liegt an der Reihenfolge der Expansion. Zuerst die "Brace-Erweiterung" und später (die letzte) "Pathname-Erweiterung" (auch bekannt als Glob-Erweiterung).

Brace --> Parameter (variable) --> Pathname

Wir können "Pathname-Erweiterung" für einen Moment deaktivieren:

$ set -f
$ echo *.{ext1,ext2}
*.ext1 *.ext2

Die "Pfadnamenerweiterung" erhält zwei Argumente: *.ext1und *.ext2.

$ set +f
$ echo *.{ext1,ext2}
a.ext1 b.ext1 a.ext2 b.ext2

Das Problem ist, dass wir keine Variable für die Klammererweiterung verwenden können.
Es wurde schon oft erklärt , dass eine Variable in einer "Klammererweiterung" verwendet wird.

Um eine "Klammererweiterung" zu erweitern, die das Ergebnis einer "variablen Erweiterung" ist, müssen Sie die Befehlszeile erneut an die Shell mit senden eval.

$ list={ext1,ext2}
$ eval echo '*.'"$list"

Klammer -> Variable -> Glob || -> Klammer -> Variable -> Glob
........ hier zitiert -> ^^^^^^ || eval ^^^^^^^^^^^^^^^^^^^^^^^^^^

Werte der Dateinamen bringen kein Ausführungsproblem für eval:

$ touch 'a;date;.ext1'
eval echo '*.'"$list"
a;date;.ext1 a.ext1 b.ext1 a.ext2 b.ext2

Aber der Wert von $listkönnte unsicher sein. Der Wert von $listwird jedoch vom Skriptschreiber festgelegt. Der Skriptschreiber hat die Kontrolle über eval: Verwenden Sie nur keine extern festgelegten Werte für $list. Versuche dies:

#!/bin/bash
touch {a,b,c}.ext{1,2}
list=ext{1,2}
eval ls -l -- '*.'"$list"

Eine bessere Alternative.

Eine Alternative (ohne Auswertung) ist die Verwendung von Bash "Extended Patterns" :

#!/bin/bash
shopt -s extglob
list='@(ext1|ext2)'
ls -- *.$list

Hinweis: Bitte beachten Sie, dass beide Lösungen (Bewertung und Muster) (wie geschrieben) für Dateinamen mit Leerzeichen oder neuen Zeilen sicher sind. Wird aber für ein $listmit Leerzeichen fehlschlagen , weil $listnicht zitiert oder die Auswertung die Anführungszeichen entfernt.

Gemeinschaft
quelle
Ja, erweitertes Globbing
Glenn Jackman
2

Erwägen:

secondList='*.{ext1,ext2}'
ls $secondList 

Das Problem ist , dass Klammer Expansion erfolgt , bevor variable Expansion . Das bedeutet, dass oben keine Klammererweiterung durchgeführt wird.

Dies liegt daran, dass beim ersten Aufrufen der Befehlszeile durch bash keine geschweiften Klammern vorhanden sind. Nach dem secondListErweitern ist es zu spät.

Folgendes wird funktionieren:

$ s='*'
$ ls $s.{ext1,ext2}
a.ext1  a.ext2  b.ext1  b.ext2

Hier verfügt die Befehlszeile über geschweifte Klammern, sodass die Klammererweiterung als erster Schritt ausgeführt werden kann. Danach wird der Wert von $sin ( variable Erweiterung ) eingesetzt, und zuletzt wird die Pfadnamenerweiterung durchgeführt.

Dokumentation

man bash erklärt die Reihenfolge der Erweiterung:

Die Reihenfolge der Erweiterungen lautet: Klammererweiterung; Tilde-Erweiterung, Parameter- und Variablenerweiterung, arithmetische Erweiterung und Befehlssubstitution (von links nach rechts); Wortaufteilung; und Pfadnamenerweiterung.

John1024
quelle