Dlaczego podczas wywoływania tego skryptu Bash konieczne są cudzysłowy dla argumentów plików?

13

Jestem całkiem nowy w skryptach Bash. Mam „skrypt testowy”, którego użyłem jako podstawę do bardziej zaawansowanego / przydatnego skryptu:

#!/bin/bash
files=$1
for a in $files
do
    echo "$a"
done

Kiedy wywołuję to bez cudzysłowów, po prostu wyszukuje jeden plik w katalogu:

testscript *.txt

Ale kiedy nazywam to cudzysłowami, działa poprawnie i wybiera wszystkie pliki tekstowe:

testscript '*.txt'

Co tu się dzieje?

gornvix
źródło
Aby być bardzo, bardzo jasnym, właściwym sposobem na rozwiązanie tego problemu jest uruchomienie for a in "$@"; do(lub for a; do) w skrypcie, pozostawiając globbing zewnętrznej powłoce, aby nie pomijać cudzysłowów.
Charles Duffy,
Warto to zobaczyć. guide.bash.academy
vascowhite

Odpowiedzi:

29

Kiedy wywołujesz program

testscript *.txt

wtedy twoja powłoka wykonuje ekspansję i oblicza wszystkie wartości. Może więc skutecznie wywołać twój program jako

testscript file1.txt file2.txt file3.txt file4.txt

Teraz twój program tylko patrzy, $1więc działa tylko file1.txt.

Cytując w wierszu poleceń, przekazujesz dosłowny ciąg *.txtdo skryptu i to jest to, co jest przechowywane $1. Twoja forpętla następnie ją rozszerza.

Zwykle używałbyś tego, "$@"a nie $1w takich skryptach.

Jest to „gotcha” dla osób pochodzących ze skryptów CMD, w których powłoka poleceń nie wykonuje globowania (jak wiadomo) i zawsze przekazuje ciąg literału.

Stephen Harris
źródło
6
Aby to wyjaśnić (dla osób innych niż autor powyższej odpowiedzi), użycie "$@"(w przeciwieństwie do $@lub $1 $2 $3) spowoduje, że każda nazwa pliku będzie cytowana "file1.txt" "file2.txt"itp. file1.txtJest to bez znaczenia, ale jeśli tak my file.txt, to cytowanie ma kluczowe znaczenie dla uniknięcia powłoki parsowanie w celu przekształcenia go w dwa nazwy plików, jeden o nazwie myi jeden o nazwie file.txt. Zawsze podawaj dane wejściowe od użytkowników i globalną ekspansję, aby pewnego dnia nie byłeś bardzo nieszczęśliwy.
Seth Robertson
2
I to nie jest tylko teoria - Mac OS X był kiedyś dostarczany ze skryptem aktualizacji, który nie podawał poprawnie argumentów i w niektórych okolicznościach powodował usuwanie dysków twardych ludzi.
puszysty
2
@fluffy, czy masz link na ten temat?
Wildcard
@Wildcard Niestety nie mogę znaleźć żadnych artykułów na ten temat, ale kiedy to się stało, była to ważna wiadomość w świecie technologii. Chcę powiedzieć, że było to w latach 2003/2004 lub później, kiedy Apple wciąż zaczynało być dystrybutorem UNIX.
puszysty
1
@wildcard Ah, znalazłem to! xlr8yourmac.com/OSX/itunes2_erased_drives.html - to był winowajca skrypt aktualizacji iTunes.
puszysty
7

Bez cudzysłowów powłoka rozwija się *.txtprzed wywołaniem skryptu, więc $1tylko pierwszy plik jest rozszerzany. Wszystkie txtpliki są w tym momencie argumentami twojego skryptu (zakładając, że nie ma ich zbyt wiele).

W przypadku cudzysłowów ciąg ten jest przekazywany bez rozwijania do skryptu, co pozwala na forwykonanie rozwinięcia zgodnie z oczekiwaniami.

Eric Renouf
źródło