Важливо усвідомити, що насправді оболонка розширює foo*
їх до списку відповідних імен файлів, тому мало що mv
може зробити сама.
Проблема тут полягає в тому, що коли глобус не відповідає, деякі снаряди на зразок bash
(і більшість інших оболонок, подібних до Борна, таку баггічну поведінку насправді впроваджувала оболонка Борна в кінці 70-х років) передають шаблон дослівно в команду.
Тож тут, коли foo*
не відповідає жодному файлу, замість того, щоб скасувати команду (як, наприклад, оболонки перед Борном та декілька сучасних оболонок), оболонка передає дослівний foo*
файл mv
, тому в основному просить mv
перемістити названий файл foo*
.
Цей файл не існує. Якби це було, воно фактично відповідало б шаблону, тому mv
повідомляє про помилку. Якби шаблон був foo[xy]
замість цього, ви mv
могли б випадково перемістити файл, названий foo[xy]
замість файлів foox
та fooy
файлів.
Тепер, навіть у тих оболонках, які не мають цієї проблеми (до Борна, csh, tcsh, риби, zsh, bash -O failglob), ви все одно отримаєте помилку mv foo* ~/bar
, але цього разу за оболонкою.
Якщо ви хочете вважати це не помилкою, якщо файлу немає, foo*
і в цьому випадку нічого не переміщувати, спершу слід створити список файлів (таким чином, що не викликає помилок, наприклад, використовуючи nullglob
параметр деякі оболонки), і тоді лише дзвінок mv
- це список не порожній.
Це було б краще, ніж приховувати всі помилки mv
(як додавання 2> /dev/null
), як ніби mv
не вдається з будь-якої іншої причини, ви, мабуть, все ще хочете знати, чому.
в зш
files=(foo*(N)) # where the N glob qualifier activates nullglob for that glob
(($#files == 0)) || mv -- $files ~/bar/
Або використовуйте анонімну функцію, щоб уникнути використання тимчасової змінної:
() { (($# == 0)) || mv -- "$@" ~/bar/; } foo*(N)
zsh
є однією з тих оболонок, у яких немає помилки Bourne і повідомляють про помилку, не виконуючи команду, коли глобус не відповідає (а nullglob
параметр не ввімкнено), тож тут ви можете приховати zsh
помилку та відновити stderr for, mv
тому ви все одно побачите mv
помилки, якщо такі є, але не помилку щодо невідповідних глобусів:
(mv 2>&3 foo* ~/bar/) 3>&2 2>&-
Або ви можете використовувати, zargs
що також уникне проблем, якщо foo*
глобус розшириться на занадто man файли.
autoload zargs # best in ~/.zshrc
zargs -r -- foo* -- mv -t ~/bar # here assuming GNU mv for its -t option
У ksh93:
files=(~(N)foo*)
((${#files[#]} == 0)) || mv -- "${files[@]}" ~/bar/
В bash:
bash
не має синтаксису, щоб увімкнути nullglob
лише один глобус, а failglob
параметр скасовується, nullglob
тому вам знадобляться такі речі, як:
saved=$(shopt -p nullglob failglob) || true
shopt -s nullglob
shopt -u failglob
files=(foo*)
((${#files[@]} == 0)) || mv -- "${files[@]}" ~/bar/
eval "$saved"
або встановіть параметри в підрозділі, щоб зберегти їх потрібно зберегти раніше і відновити після цього.
(
shopt -s nullglob
shopt -u failglob
files=(foo*)
((${#files[@]} == 0)) || mv -- "${files[@]}" ~/bar/
)
В yash
(
set -o nullglob
files=(foo*)
[ "${#files[@]}" -eq 0 ] || mv -- "${files[@]}" ~/bar/
)
В fish
У рибній оболонці поведінка nullglob є типовою для set
команди, тому це просто:
set files foo*
count $files > /dev/null; and mv -- $files ~/bar/
POSIXly
У nullglob
POSIX sh
немає жодної опції та жодного масиву, крім параметрів позиції. Існує хитрість, яку ви можете використовувати, щоб визначити, чи відповідає глобус чи ні:
set -- foo[*] foo*
if [ "$1$2" != 'foo[*]foo*' ]; then
shift
mv -- "$@" ~/bar/
fi
Використовуючи і a, foo[*]
і foo*
glob, ми можемо розмежовувати випадок, коли немає відповідного файлу, і той, де є один файл, який, можливо, викликається foo*
(що set -- foo*
не може зробити).
Більше читання:
mv foo* ~/bar/ 2> /dev/null
?