W twoim skrypcie występują różne możliwe punkty awarii. Przede wszystkim rm *.old*
użyje globowania, aby utworzyć listę wszystkich pasujących plików, które mogą poradzić sobie z nazwami plików zawierającymi białe znaki. Jednak twój skrypt przypisuje zmienną do każdego wyniku globu i robi to bez cytowania. To się zepsuje, jeśli nazwy plików zawierają spacje. Na przykład:
$ ls
'file name with spaces.old.txt' file.old.txt
$ rm *.old.* ## works: both files are deleted
$ touch "file.old.txt" "file name with spaces.old.txt"
$ for i in ./*; do oldfile=$i; rm -v $oldfile; done
rm: cannot remove './file': No such file or directory
rm: cannot remove 'name': No such file or directory
rm: cannot remove 'with': No such file or directory
rm: cannot remove 'spaces.old.txt': No such file or directory
removed './file.old.txt'
Jak widać, pętla nie powiodła się dla pliku ze spacjami w nazwie. Aby zrobić to poprawnie, musisz zacytować zmienną:
$ for i in ./*; do oldfile="$i"; rm -v "$oldfile"; done
removed './file name with spaces.old.txt'
removed './file.old.txt'
Ten sam problem dotyczy prawie każdego użycia $i
skryptu. Powinieneś zawsze cytować zmienne .
Kolejnym możliwym problemem jest to, że wydaje się oczekiwać, że *.old.*
pasuje do plików z rozszerzeniem .old
. Tak nie jest. Dopasowuje „0 lub więcej znaków” ( *
), a następnie a .
, następnie „stary”, następnie inny, .
a następnie „ponownie 0 lub więcej znaków”. Oznacza to, że będzie to nie pasują do czegoś podobnego file.old
, ale tylko coś w rodzaju `file.old.foo:
$ ls
file.old file.old.foo
$ for i in *; do if [[ "$i" == *.old.* ]]; then echo $i; fi; done
file.old.foo
Więc nie ma przeciwnika file.old
. W każdym razie skrypt jest o wiele bardziej złożony niż jest to konieczne. Spróbuj zamiast tego:
#!/bin/bash
for i in *; do
if [[ -f "$i" ]]; then
if [[ "$i" == *.old ]]; then
rm -v "$i" || echo "rm failed for $i"
else
echo "$i doesn't have an .old extension"
fi
cp -v "$i" "$i".old
else
echo "$i is not a file"
fi
done
Zauważ, że dodałem -v
do instrukcji echo rm
i cp which does the same thing as what you were doing with your
.
Nie jest to idealne, ponieważ na przykład, gdy znajdziesz, file.old
że zostanie on usunięty, a później skrypt spróbuje go skopiować i nie powiedzie się, ponieważ plik już nie istnieje. Jednak nie wyjaśniłeś, co skrypt naprawdę próbuje zrobić, więc nie mogę tego naprawić, chyba że powiesz nam, co naprawdę próbujesz osiągnąć.
Jeśli chcesz: i) usunąć wszystkie pliki z .old
rozszerzeniem oraz ii) dodać .old
rozszerzenie do wszystkich istniejących plików, które go nie mają, wszystko czego naprawdę potrzebujesz to:
#!/bin/bash
for i in *.old; do
if [[ -f "$i" ]]; then
rm -v "$i" || echo "rm failed for $i"
else
echo "$i is not a file"
fi
done
## All the ,old files have been removed at this point
## copy the rest
for i in *; do
if [[ -f "$i" ]]; then
## the -v makes cp report copied files
cp -v "$i" "$i".old
fi
done
rm *.old.*
który usuwa te pliki ale nie plik kopii zapasowej file.old. Próbuję to zrobić w moim skrypcie. DziękiJedyne przypadki,
rm $oldfile
może nie są, gdy nazwa pliku zawiera dowolny znakIFS
(spacja, tabulacjami) lub dowolny znak glob (*
,?
,[]
).Jeśli
IFS
obecny jest jakikolwiek znak, powłoka wykona dzielenie słów i na podstawie obecności interpretacji nazw ścieżek znaków globujących na rozwinięciu zmiennej.Na przykład, jeśli nazwa pliku to
foo bar.old.
, zmiennaoldfile
zawierałabyfoo bar.old.
.Kiedy to zrobisz:
shell na początku dzieli ekspansję
oldfile
przestrzeni na dwa słowa,foo
ibar.old.
. Zatem polecenie staje się:co oczywiście doprowadziłoby do nieoczekiwanego rezultatu. Nawiasem mówiąc, jeśli masz żadnych nazw plików operatorów (
*
,?
,[]
) w rozbudowie, a następnie ścieżka ekspansji byłoby zrobić też.Aby uzyskać pożądany wynik, musisz podać zmienne:
Teraz nie byłoby dzielenia słów ani rozwijania nazw ścieżek, dlatego powinieneś uzyskać pożądany wynik, tj. Pożądany plik zostałby usunięty. Jeśli jakaś nazwa pliku zaczyna się od
-
, to:Możesz zapytać, dlaczego nie musimy cytować zmiennych, gdy są używane w środku
[[
, ponieważ[[
jest tobash
słowo kluczowe i obsługuje wewnętrzną ekspansję zmiennych, zachowując dosłowne rozwinięcie.Teraz kilka punktów:
Powinieneś przekierować STDERR (
exec 2>errorfile
) przedrm
poleceniem, w przeciwnym razie[[ -s errorfile ]]
test dałby fałszywe alarmyUżyłeś
[ -s $errorfile ]
, używasz rozszerzenia zmiennej$errorfile
, które byłoby NUL, ponieważerrorfile
zmienna nie jest nigdzie zdefiniowana. Być może chodziło ci po prostu[ -s errorfile ]
o przekierowanie STDERRJeśli zmienna
errorfile
jest zdefiniowana, podczas używania[ -s $errorfile ]
ponownieIFS
dusiłaby wyżej wymienione przypadki i globowanie, ponieważ w przeciwieństwie do tego[[
,[
nie jest obsługiwana wewnętrznie przezbash
W dalszej części skryptu próbujesz
cp
usunąć już pobrany plik (ponownie bez cytowania zmiennej), nie ma to żadnego sensu, powinieneś sprawdzić ten uchwyt i wprowadzić niezbędne poprawki w zależności od celu.źródło