2012-02-10 8 views
22

mój kolega, Ryan przyszedł do mnie z błędu w swoim skrypcie bash, a ja zidentyfikowany problem z tym teście:Przypisywanie wyniki globbing do zmiennej w bash

$ mkdir ryan 
$ mkdir ryan/smells-bad 
$ FOO=ryan/smells-* 
$ echo $FOO 
ryan/smells-bad 
$ touch $FOO/rotten_eggs 
touch: cannot touch `ryan/smells-*/rotten_eggs': No such file or directory 

Z tego wnoszę, że globbing dzieje się podczas polecenia echa, a nie gdy tworzona jest zmienna FOO.

Mamy kilka obejścia, w porządku malejącym ungracefulness:

touch `echo $FOO`/rotten_eggs 

czyli

pushd 
cd $FOO 
touch rotten_eggs 
popd 

ale nie jest satysfakcjonujące. Czy brakuje mi jakiejś sztuczki?

Odpowiedz

29

Problem polega na tym, że glob rozwinie się tylko wtedy, gdy plik "rotten_eggs" istnieje, ponieważ jest zawarty w wzorze globu. Powinieneś użyć tablicy.

FOO=(ryan/smells-*) 
touch "${FOO[@]/%//rotten_eggs}" 

Tablica FOO zawiera wszystko, co odpowiada globowi. Rozszerzenie za pomocą% appends/rotten_eggs do każdego elementu.

+0

Dzięki, to ładnie to wyjaśnia. –

+0

To nie jest problem w ogóle. Problem polega na tym, że bash przypisuje glob do zmiennej przed rozwinięciem. Nawet jeśli plik istnieje, do zmiennej zostanie przypisany glob, a nie nazwa pliku; i że glob będzie rozszerzony na użycie –

+0

@SamLiddicott Przykład z OP działałby, gdyby istniało "rotten_eggs", więc był to mniej więcej problem. Czas rozwinięcia globu nie jest istotny w krótkim przykładzie OP. – jordanm

6

Rozważmy

for dir in $FOO; do 
    touch "$dir/rotten_eggs" 
done 

pamiętać, że będzie touch wiele plików jeśli wzorzec glob pasuje do więcej niż jednej ścieżki.

+0

To zadziała, ale wymaga, aby dotyk był uruchamiany wiele razy, a nie jeden raz. – jordanm

+1

Jest to prawdopodobnie czystsze, w zależności od sytuacji. –

3

zrobiłbym to tak:

for FOO in ryan/smells-*; do 
    touch "$FOO"/rotten_eggs 
done 

ten sposób $FOO zawiera rzeczywistą nazwę katalogu, a nie wzór glob. Jeśli jednak istnieje więcej niż jedna zgodność, to będzie zawierała tylko ostatnią po pętli, więc rozwiązanie macierzowe może być lepsze dla tego przypadku.

0

Kod zgodnie z przeznaczeniem z wyniku glob przypisany do zmiennej byłoby tak:

$ mkdir ryan 
$ mkdir ryan/smells-bad 
$ FOO=(ryan/smells-*) 
$ echo "${FOO[@]}" 
ryan/smells-bad 
$ echo "$FOO" 
ryan/smells-bad 
$ touch "$FOO/rotten_eggs" 
$ ls -l "$FOO" 
total 0 
-rw-r--r-- 1 ryan ryan 0 Mar 1 11:17 rotten_eggs 

$FOO jest rzeczywiście tablicą tutaj, ale $ FOO działa również zdobyć pierwszy element tablicy .

ale zobaczyć jak glob można dopasować więcej niż jeden plik (stąd tablica jest dobrym pomysłem)

$ mkdir ryan/clean 
$ FOO=(ryan/*) 
$ echo "$FOO" 
ryan/clean 
$ echo "${FOO[@]}" 
ryan/clean ryan/smells-bad 

W tych przypadkach wyniki glob jest przypisana do zmiennej jako pożądane, zamiast zmienna jest rozwijana jako glob w miejscu użycia.

Oczywiście oznacza to, że zmienna naprawdę powinny być zawsze stosowane w cudzysłowie "..." inaczej jeśli sam (ekspansja glob) nazwa pliku miała również * w nim, byłoby glob ponownie.

np.

$ touch ryan/'*ea*' 
$ FOO=(ryan/*ea*) 
$ echo "${FOO[@]}" 
ryan/clean ryan/*ea* 
$ echo ${FOO[@]} 
ryan/clean ryan/clean ryan/*ea*