Ważne jest, aby zdać sobie sprawę, że tak naprawdę to powłoka rozszerza foo*
ją na listę pasujących nazw plików, więc niewiele mv
można zrobić.
Problem polega na tym, że gdy glob nie pasuje, niektóre powłoki takie jak bash
(i większość innych powłok podobnych do Bourne'a, to zachowanie błędne zostało wprowadzone przez powłokę Bourne'a pod koniec lat 70.) przekazują wzorzec dosłownie do polecenia.
Więc tutaj, gdy foo*
nie pasuje do żadnego pliku, zamiast przerywać polecenie (jak robią to wcześniejsze powłoki Bourne'a i kilka nowoczesnych powłok), powłoka przekazuje foo*
plik dosłowny mv
, więc w zasadzie prosi mv
o przeniesienie pliku o nazwie foo*
.
Ten plik nie istnieje. Gdyby tak było, rzeczywiście pasowałby do wzorca, więc mv
zgłasza błąd. Gdyby foo[xy]
zamiast tego wzorzec , mv
mógł przypadkowo przenieść plik o nazwie foo[xy]
zamiast plików foox
i fooy
.
Teraz nawet w tych powłokach, które nie mają tego problemu (przed Bourne, csh, tcsh, fish, zsh, bash -O failglob), nadal pojawiał się błąd mv foo* ~/bar
, ale tym razem przez powłokę.
Jeśli chcesz uznać to za błąd, jeśli nie ma pasującego pliku, foo*
a w takim przypadku nie przenosić niczego, najpierw musisz zbudować listę plików (w sposób, który nie powoduje błędu, jak przy użyciu nullglob
opcji niektóre muszle), a następnie tylko wywołanie mv
jest takie, że lista nie jest pusta.
Byłoby to lepsze niż ukrywanie wszystkich błędów mv
(tak jak w przypadku dodawania 2> /dev/null
), jak gdyby mv
nie powiodło się z jakiegokolwiek innego powodu, prawdopodobnie nadal chcesz wiedzieć, dlaczego.
w Zsh
files=(foo*(N)) # where the N glob qualifier activates nullglob for that glob
(($#files == 0)) || mv -- $files ~/bar/
Lub użyj funkcji anonimowej, aby uniknąć używania zmiennej tymczasowej:
() { (($# == 0)) || mv -- "$@" ~/bar/; } foo*(N)
zsh
jest jedną z tych powłok, które nie zawierają błędu Bourne'a i zgłaszają błąd bez wykonania polecenia, gdy glob nie pasuje (a nullglob
opcja nie została włączona), więc tutaj możesz ukryć zsh
błąd i przywrócić stderr dla, mv
więc nadal będziesz widzieć mv
błędy, jeśli takie istnieją, ale nie błąd dotyczący niepasujących globów:
(mv 2>&3 foo* ~/bar/) 3>&2 2>&-
Lub możesz użyć tego, zargs
co uniknęłoby również problemów, gdyby foo*
glob rozszerzył się do zbyt dużych plików.
autoload zargs # best in ~/.zshrc
zargs -r -- foo* -- mv -t ~/bar # here assuming GNU mv for its -t option
W ksh93:
files=(~(N)foo*)
((${#files[#]} == 0)) || mv -- "${files[@]}" ~/bar/
W bash:
bash
nie ma składni, aby włączyć tylko nullglob
dla jednego globu, a failglob
opcja anuluje, nullglob
więc potrzebujesz takich rzeczy jak:
saved=$(shopt -p nullglob failglob) || true
shopt -s nullglob
shopt -u failglob
files=(foo*)
((${#files[@]} == 0)) || mv -- "${files[@]}" ~/bar/
eval "$saved"
lub ustaw opcje w podpowłoce, aby zapisać trzeba je wcześniej zapisać, a następnie przywrócić.
(
shopt -s nullglob
shopt -u failglob
files=(foo*)
((${#files[@]} == 0)) || mv -- "${files[@]}" ~/bar/
)
W yash
(
set -o nullglob
files=(foo*)
[ "${#files[@]}" -eq 0 ] || mv -- "${files[@]}" ~/bar/
)
W fish
W skorupce ryby zachowanie nullglob jest domyślnym set
poleceniem, więc jest to po prostu:
set files foo*
count $files > /dev/null; and mv -- $files ~/bar/
POSIXly
W nullglob
POSIX sh
nie ma opcji ani tablicy innej niż parametry pozycyjne. Istnieje jednak sztuczka, której można użyć do wykrycia, czy glob pasuje, czy nie:
set -- foo[*] foo*
if [ "$1$2" != 'foo[*]foo*' ]; then
shift
mv -- "$@" ~/bar/
fi
Używając zarówno a, jak foo[*]
i foo*
glob, możemy rozróżnić przypadek, w którym nie ma pasującego pliku, i ten, w którym jest jeden plik, który jest wywoływany foo*
(czego set -- foo*
nie można zrobić).
Więcej lektur:
mv foo* ~/bar/ 2> /dev/null
?