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?
bash
shell-script
gornvix
źródło
źródło
for a in "$@"; do
(lubfor a; do
) w skrypcie, pozostawiając globbing zewnętrznej powłoce, aby nie pomijać cudzysłowów.Odpowiedzi:
Kiedy wywołujesz program
wtedy twoja powłoka wykonuje ekspansję i oblicza wszystkie wartości. Może więc skutecznie wywołać twój program jako
Teraz twój program tylko patrzy,
$1
więc działa tylkofile1.txt
.Cytując w wierszu poleceń, przekazujesz dosłowny ciąg
*.txt
do skryptu i to jest to, co jest przechowywane$1
. Twojafor
pętla następnie ją rozszerza.Zwykle używałbyś tego,
"$@"
a nie$1
w 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.
źródło
"$@"
(w przeciwieństwie do$@
lub$1
$2
$3
) spowoduje, że każda nazwa pliku będzie cytowana"file1.txt"
"file2.txt"
itp.file1.txt
Jest to bez znaczenia, ale jeśli takmy 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 nazwiemy
i jeden o nazwiefile.txt
. Zawsze podawaj dane wejściowe od użytkowników i globalną ekspansję, aby pewnego dnia nie byłeś bardzo nieszczęśliwy.Bez cudzysłowów powłoka rozwija się
*.txt
przed wywołaniem skryptu, więc$1
tylko pierwszy plik jest rozszerzany. Wszystkietxt
pliki 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
for
wykonanie rozwinięcia zgodnie z oczekiwaniami.źródło