Điều quan trọng là phải nhận ra rằng đó thực sự là cái vỏ mở rộng nó foo*
vào danh sách các tên tệp phù hợp, vì vậy rất ít mv
có thể tự làm được.
Vấn đề ở đây là khi một quả địa cầu không khớp, một số vỏ như bash
(và hầu hết các vỏ giống như Bourne khác, hành vi lỗi đó thực sự được đưa ra bởi vỏ Bourne vào cuối những năm 70) truyền lại nguyên mẫu cho lệnh.
Vì vậy, ở đây, khi foo*
không khớp với bất kỳ tệp nào, thay vì hủy bỏ lệnh (như shell trước Bourne và một số shell hiện đại), shell sẽ chuyển một foo*
tệp nguyên văn sang mv
, vì vậy về cơ bản yêu cầu mv
di chuyển tệp được gọi foo*
.
Tập tin đó không tồn tại. Nếu có, nó thực sự đã khớp với mẫu, do đó mv
báo cáo lỗi. Nếu mẫu đã được foo[xy]
thay thế, mv
có thể đã vô tình di chuyển một tệp được gọi foo[xy]
thay vì foox
và fooy
các tệp.
Bây giờ, ngay cả trong các shell không có vấn đề đó (trước Bourne, csh, tcsh, fish, zsh, bash -O failglob), bạn vẫn sẽ gặp lỗi mv foo* ~/bar
, nhưng lần này là do shell.
Nếu bạn muốn coi đó không phải là lỗi nếu không có tệp nào khớp foo*
và trong trường hợp đó, không di chuyển bất cứ thứ gì, bạn sẽ muốn xây dựng danh sách các tệp trước (theo cách không gây ra lỗi như bằng cách sử dụng nullglob
tùy chọn một số shell), và sau đó chỉ gọi mv
là danh sách không trống.
Điều đó sẽ tốt hơn là che giấu tất cả các lỗi của mv
(như thêm vào 2> /dev/null
) như thể mv
không thành công vì bất kỳ lý do nào khác, có lẽ bạn vẫn muốn biết tại sao.
trong zsh
files=(foo*(N)) # where the N glob qualifier activates nullglob for that glob
(($#files == 0)) || mv -- $files ~/bar/
Hoặc sử dụng hàm ẩn danh để tránh sử dụng biến tạm thời:
() { (($# == 0)) || mv -- "$@" ~/bar/; } foo*(N)
zsh
là một trong những shell không có lỗi Bourne và báo lỗi mà không thực thi lệnh khi toàn cầu không khớp (và nullglob
tùy chọn chưa được bật), vì vậy, ở đây, bạn có thể ẩn zsh
lỗi và khôi phục stderr mv
vì vậy bạn vẫn sẽ thấy các mv
lỗi nếu có, nhưng không phải là lỗi về các khối không phù hợp:
(mv 2>&3 foo* ~/bar/) 3>&2 2>&-
Hoặc bạn có thể sử dụng zargs
cũng sẽ tránh được các vấn đề nếu toàn foo*
cầu sẽ mở rộng thành các tệp quá man.
autoload zargs # best in ~/.zshrc
zargs -r -- foo* -- mv -t ~/bar # here assuming GNU mv for its -t option
Trong ksh93:
files=(~(N)foo*)
((${#files[#]} == 0)) || mv -- "${files[@]}" ~/bar/
Trong bash:
bash
không có cú pháp để chỉ kích hoạt nullglob
một toàn cầu và failglob
tùy chọn hủy nullglob
để bạn cần những thứ như:
saved=$(shopt -p nullglob failglob) || true
shopt -s nullglob
shopt -u failglob
files=(foo*)
((${#files[@]} == 0)) || mv -- "${files[@]}" ~/bar/
eval "$saved"
hoặc đặt các tùy chọn trong một khung con để lưu phải lưu chúng trước và khôi phục chúng sau đó.
(
shopt -s nullglob
shopt -u failglob
files=(foo*)
((${#files[@]} == 0)) || mv -- "${files[@]}" ~/bar/
)
Trong yash
(
set -o nullglob
files=(foo*)
[ "${#files[@]}" -eq 0 ] || mv -- "${files[@]}" ~/bar/
)
Trong fish
Trong vỏ cá, hành vi nullglob là mặc định cho set
lệnh, vì vậy nó chỉ là:
set files foo*
count $files > /dev/null; and mv -- $files ~/bar/
POSIXly
Không có nullglob
tùy chọn nào trong POSIX sh
và không có mảng nào ngoài các tham số vị trí. Có một mẹo bạn có thể sử dụng để phát hiện xem một quả cầu có khớp hay không:
set -- foo[*] foo*
if [ "$1$2" != 'foo[*]foo*' ]; then
shift
mv -- "$@" ~/bar/
fi
Bằng cách sử dụng cả a foo[*]
và toàn foo*
cầu, chúng ta có thể phân biệt giữa trường hợp không có tệp phù hợp và tệp có một tệp được gọi là foo*
( set -- foo*
không thể làm được).
Đọc thêm:
mv foo* ~/bar/ 2> /dev/null
?