it-swarm.com.de

Wie ordne ich einer Variablen in einem Bash-Skript einen Glob-Ausdruck zu?

Wenn die folgenden zwei Codezeilen in einem Bash-Skript ausgeführt werden, beklagt "ls", dass die Dateien nicht vorhanden sind:

dirs=/content/{dev01,dev02}
ls -l $dirs

Wenn ich das Skript mit der Option -x ausführe, scheint es, als würde die Variable in einfache Anführungszeichen gesetzt (was ein Verschieben verhindern würde):

+ dirs=/content/{dev01,dev01}
+ ls -l '/content/{dev01,dev01}'
ls: /content/{dev01,dev01}: No such file or directory

Wenn ich den Befehl "ls" in meiner interaktiven Shell ausführe (ohne Anführungszeichen), werden die beiden Verzeichnisse zurückgegeben.

Ich habe das Bash-Referenzhandbuch (Version 3.2) gelesen und sehe keinen Grund dafür, dass das Verschieben von Dateinamen nicht stattfindet (ich übergebe -f nicht an die Shell), oder etwas, das ich einstellen kann, um dies sicherzustellen Globbing passiert.

41
kdgregory

Ich denke, es ist die Reihenfolge der Erweiterungen:

Die Reihenfolge der Erweiterungen lautet: brace expansion, Tilde-Erweiterung, Parameter, variable und arithmetische Erweiterung und Befehlssubstitution (erfolgt in einer Links-nach-Rechts-Richtung), Word Aufteilung und pathname expansion.

Wenn Ihre Variable ersetzt wird, findet also keine Klammererweiterung mehr statt. Das funktioniert für mich:

eval ls $dirs

Seien Sie sehr vorsichtig mit dem Eval. Es wird das Zeug wörtlich ausführen. Wenn dirs f{m,k}t*; some_command enthält, wird some_command ausgeführt, nachdem ls beendet ist. Die Zeichenfolge, die Sie in der aktuellen Shell an eval übergeben, wird ausgeführt. Es wird /content/dev01 /content/dev02 an ls übergeben, unabhängig davon, ob sie existieren oder nicht. Wenn Sie * nach dem Zeugs setzen, wird daraus eine Pfadnamenerweiterung, und es werden nicht vorhandene Pfade weggelassen:

dirs=/content/{dev01,dev02}*

Ich bin mir nicht zu 100% sicher, aber es macht für mich Sinn.

Hier ist eine exzellente Diskussion darüber, was Sie zu tun versuchen.

Die kurze Antwort lautet, dass Sie ein Array wünschen:

dirs=(/content/{dev01,dev01})

Aber was Sie mit den Ergebnissen tun, kann komplexer werden, als Sie meinen.

21
feoh

Dies ist kein Dateinamensglobbing, sondern eine Klammererweiterung. Der Unterschied ist geringfügig, aber er besteht - beim Dateinamensglobbing würden Sie nur vorhandene Dateien als Ergebnis erhalten, während Sie in der Klammererweiterung jede Art von Zeichenfolge generieren können .

http://www.gnu.org/software/bash/manual/bashref.html#Brace-Expansion

http://www.gnu.org/software/bash/manual/bashref.html#Filename-Expansion

Nun, das hat bei mir funktioniert:

#!/bin/sh
dirs=`echo ./{dev01,dev02}`
ls $dirs
8
Yoni Roit

Für Leute (wie ich), die dies über Google finden, sind die Antworten von @Peter und @feoh die allgemeine Lösung für "Wie man Variablen in einem Bash-Skript gliedert". 

list_of_results=(pattern)

speichert vorhandene Dateinamen, die mit pattern übereinstimmen, im Array list_of_results. Jedes Element von list_of_results enthält einen Dateinamen, Leerzeichen und alle.

Sie können auf jedes Ergebnis als "${list_of_results[<index>]}" für <index> ab 0 zugreifen. Sie können die gesamte Liste in korrekter Anführungszeichen als "${list_of_results[@]}" abrufen. 

4
cxw

Ich vermute, dass Sie ein Array benötigen, aber das wird Sie auf neuere Bases beschränken. Es ist sicherer als Eval.

dirs=( /"content with spaces"/{dev01,dev02} )

dirs=( /content/{dev01,dev02} )
ls -l "${dirs[@]}"

/content/{dev01,dev02}

wird erweitern auf:

"/content/dev01" "/content/dev02"

Die Existenz dieser Verzeichnisse ist für die Erweiterung irrelevant.

Es ist unvorhersehbar, wenn Sie einer Klammererweiterung eine Variable zuweisen.

dirs=/content/{dev01,dev02}

kann sich in verwandeln

"/content/dev01"

oder

"/content/dev01 /content/dev02"

oder

"/content/dev01" "/content/dev02"

oder

"/content/{dev01,dev02}"

Wenn Sie die Klammern in irgendeiner Weise zitieren, werden sie nicht erweitert, sodass das Ergebnis die Klammern enthält und meistens bedeutungslos ist.

2
Peter

Da Sie Dateien globieren möchten, sollten Sie keine Klammererweiterungen verwenden. Die Verwendung der Klammererweiterung ist in diesem Fall ein antipattern und definitiv das falsche Werkzeug für den Job.

Was Sie wollen, ist Erweitertes Globbing :

shopt -s extglob # likely already set in interactive shells

dirs=/content/@(dev01|dev02)
ls $dirs
1
gniourf_gniourf
ls `echo $dirs` 

arbeitet unter cygwin. 

0
Zsolt Botykai

Das Problem, mit dem sich niemand befasst hat, ist, dass die Variablenzuordnung den Unterschied macht.

dirs=/content/{dev01,dev02}

erweitert sich anders als

echo /content/{dev01,dev02}

Die Frage ist, wie die Ergebnisse der Erweiterung dirs zugewiesen werden.

Siehe: So verwenden Sie die Bash-Substitution in einer Variablendeklaration

0
Sam Liddicott