2010-06-28 13 views
5

W skrypcie BASH próbuję wykryć, czy plik istnieje. Nazwa pliku jest zmienna, ale polecenie -e wydaje się nie być w stanie wykryć pliku. Poniższy kod zawsze wyprowadza „~/misc/zadania/drupal_backup.sh nie istnieje”Skrypt bash -e nie wykrywa nazwy pliku w zmiennej

filename="~/misc/tasks/drupal_backup.sh" 

if [ -e "$filename" ]; then 
    echo "$filename exists" 
else 
    echo "$filename does not exist" 
fi 

Z drugiej strony, następujący kod poprawnie wykrywa pliku:

if [ -e ~/misc/tasks/drupal_backup.sh ]; then 
    echo "$filename exists" 
else 
    echo "$filename does not exist" 
fi 

Dlaczego byłoby to ? Jak mogę go wykryć, gdy plik jest w zmiennej?

+1

Uważam '~' za wygodną linię komend, której nie należy używać w skryptach. –

Odpowiedz

7

To jest interesujące. Zastępowanie $HOME dla działa tak samo, jak usunięcie cudzysłowów z zadania.

Jeśli umieścisz set -x u góry tego skryptu, zobaczysz, że wersja z cudzysłowami ustawia nazwę pliku na ~/..., co jest podane dla -e. Jeśli usuniesz cytaty, nazwa pliku zostanie ustawiona na rozwiniętą /home/somebody/.... W pierwszym przypadku widzisz:

+ [ -e ~/... ] 

i to się nie podoba. W drugim przypadku, widzisz:

+ [ -e /home/somebody/... ] 

i robi pracy.

Jeśli zrobisz to bez zmiennej, widzisz:

+ [ -e /home/somebody/... ] 

i tak, to działa.


Po odrobinie dochodzenia, odkryłem, że jest to faktycznie kolejność bash wykonuje swoje ekspansje. Na stronie atakujących człowieka:

Kolejność ekspansji jest: rozwijanie nawiasów, tyldy, parametr, zmienna i wyrażeń arytmetycznych i polecenia podstawienie (wykonane w lewej do prawej mody), podział na słowa, a ścieżka ekspansja.

Dlatego to nie działa, zmienna jest podstawiona po tyldy ekspansji. Innymi słowy, w punkcie, w którym bash chce rozwinąć ~, nie ma żadnego. Dopiero po rozszerzeniu zmiennym słowo zostanie zmienione na ~/... i po tym nie będzie rozwijania tyldy.

Jedną rzeczą, którą mógł zrobić, to zmienić swoje oświadczenie if do:

if [[ -e $(eval echo $filename) ]]; then 

Ten oceni $ filename argumentu dwukrotnie. Po raz pierwszy (z eval), nie będzie ~ podczas fazy rozszerzania tyldy, ale $filename zostanie zmieniony na ~/... podczas fazy zmiennej ekspansji.

Następnie, podczas drugiej oceny (wykonanej jako część samego if), w fazie rozszerzania tyldy pojawi się ~.

Przetestowałem to na moim pliku .profile i wydaje się, że działa, sugeruję, abyś potwierdził w swoim konkretnym przypadku.

+0

Dzięki! Po prostu zastąpienie ~ z $ HOME działało idealnie. – thornate

+1

Należy zauważyć, że nigdy nie powinieneś używać 'eval'. '~' jest miłym skrótem dla trybu interaktywnego, ale z tego powodu powinno być avioded w skryptach. Wiele skryptów startowych zawiera coś takiego jak '[-r ~/.profile] &&. ~/.profile', i nie wiem, czy to działa, jeśli katalog domowy zawiera spacje. Lepiej jest użyć $ HOME i podać wszystkie zmienne: 'filename =" $ HOME/misc/tasks/drupal_backup.sh "'. W tym przypadku cytowanie nie jest wymagane, ale nie jest szkodliwe i łatwiej po prostu zacytować wszystko, chyba że wiesz dokładnie, gdzie możesz pominąć cytaty. – Philipp

+0

@Phillipp, jeśli masz zamiar wydać oświadczenie typu "nigdy nie powinieneś używać ewaluacji" w merytokraci, możesz pomyśleć o wsparciu uzasadnienia :-) – paxdiablo

Powiązane problemy