Có cách nào để làm cho mv mv bị thất bại âm thầm không?


61

Một lệnh như mv foo* ~/bar/tạo ra thông báo này trong stderr nếu không có tệp nào khớp foo*.

mv: cannot stat `foo*': No such file or directory

Tuy nhiên, trong kịch bản tôi đang làm việc trong trường hợp đó sẽ hoàn toàn ổn và tôi muốn bỏ qua tin nhắn đó từ nhật ký của chúng tôi.

Có cách nào hay để bảo mvim lặng ngay cả khi không có gì được di chuyển không?


13
mv foo* ~/bar/ 2> /dev/null?
Thomas Nyman

1
À vâng. Tôi đoán rằng tôi đã có một cái gì đó "đẹp hơn" trong tâm trí, như một số chuyển đổi cho mv. :) Nhưng điều đó sẽ làm, tất nhiên.
Jonik

3
Tôi đã thấy một số mvtriển khai hỗ trợ một -qtùy chọn để làm dịu chúng, nhưng đó không phải là một phần của đặc tả POSIX cho mv. Các mvtrong coreutils GNU, ví dụ, không không có một tùy chọn như vậy.
Thomas Nyman

Chà, tôi không nghĩ vấn đề là làm thế nào để giữ "mv" im lặng, nhưng làm thế nào để làm điều đó đúng hơn. Kiểm tra xem có tập tin / dir foo * nào không, và người dùng có được phép ghi, cuối cùng có thể thực thi "mv" không?
Shâu Shắc

Câu trả lời:


46

Bạn đang tìm kiếm điều này?

$ mv  file dir/
mv: cannot stat file’: No such file or directory
$ mv  file dir/ 2>/dev/null
# <---- Silent ----->

20
lưu ý, giá trị trả về vẫn còn! = 0, do đó, bất kỳ set -e(nên được sử dụng trong mọi tập lệnh shell) sẽ không thành công. bạn có thể thêm một || trueđể hủy kích hoạt kiểm tra cho một lệnh duy nhất.
reto

10
@reto set -ekhông nên được sử dụng trong mọi tập lệnh shell, trong nhiều trường hợp, nó làm cho việc quản lý lỗi thậm chí còn phức tạp hơn so với không có.
Chris Xuống

1
@ChrisDown Tôi thấy quan điểm của bạn. Nó thường là một sự lựa chọn giữa hai tệ nạn. Nhưng nếu nghi ngờ, tôi thích một lần chạy thành công hơn là thất bại thành công. (điều này không được chú ý cho đến khi nó được hiển thị cho khách hàng / người dùng). Shell script là một lĩnh vực khai thác lớn cho người mới bắt đầu, thật dễ dàng để bỏ lỡ một lỗi và tập lệnh của bạn gặp trục trặc!
reto

14

Trên thực tế, tôi không nghĩ tắt tiếng mvlà một cách tiếp cận tốt (hãy nhớ rằng nó cũng có thể báo cáo cho bạn về những điều khác có thể quan tâm ... ví dụ: mất tích ~/bar). Bạn muốn tắt tiếng chỉ trong trường hợp biểu thức toàn cầu của bạn không trả về kết quả. Trong thực tế chứ không phải thực hiện nó cả.

[ -n "$(shopt -s nullglob; echo foo*)" ] && mv foo* ~/bar/

Trông không hấp dẫn lắm, và chỉ hoạt động trong bash.

HOẶC LÀ

[ 'foo*' = "$(echo foo*)" ] || mv foo* ~/bar/

chỉ ngoại trừ bạn đang ở bashvới nullglobthiết lập. Bạn phải trả giá gấp 3 lần so với mẫu toàn cầu.


Vấn đề nhỏ với hình thức thứ hai: nó không thể di chuyển một tệp có tên foo*khi nó là tệp duy nhất trong thư mục hiện tại phù hợp với toàn cầu foo*. Điều này có thể được giải quyết xung quanh với một biểu thức toàn cầu không khớp với chính nó theo nghĩa đen, khó khăn. Ví dụ [ 'fo[o]*' = "$(echo fo[o]*)" ] || mv fo[o]* ~/bar/.
fra-san

13

find . -maxdepth 1 -name 'foo*' -type f -print0 | xargs -0r mv -t ~/bar/

- GNU mvcó tùy chọn "đích đầu tiên" đẹp ( -t) và xargscó thể bỏ qua việc chạy lệnh của nó nếu không có đầu vào nào cả ( -r). Sử dụng -print0-0tương ứng đảm bảo sẽ không có sự lộn xộn khi tên tệp chứa khoảng trắng và các nội dung "buồn cười" khác.


2
Lưu ý rằng -maxdepth, -print0, -0-rcũng là phần mở rộng GNU (mặc dù một số trong số họ được tìm thấy trong triển khai khác hiện nay).
Stéphane Chazelas

1
Poige, rất nhiều người dùng của chúng tôi không sử dụng Linux. Đây là thực hành tiêu chuẩn ở đây, và rất hữu ích, để chỉ ra phần nào của câu trả lời không phải là di động. Trang web này được gọi là " Unix & Linux" và nhiều người sử dụng một số dạng BSD hoặc Unix hoặc AIX hoặc macOS hoặc các hệ thống nhúng. Đừng mang nó theo cá nhân.
terdon

Tôi không lấy bất cứ thứ gì cá nhân. Tôi mặc dù có ý kiến ​​rằng bạn đã loại bỏ như tôi thấy bây giờ. Vì vậy, tôi nhắc lại: Tôi thấy những bình luận "lưu ý đó là GNU" khá vô nghĩa. Chúng không đáng để thêm vào, trước hết vì câu trả lời của tôi có dấu hiệu rõ ràng rằng nó dựa trên phiên bản GNU mv.
poige

5

Đ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 mvcó 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 mvdi 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 đó mvbáo cáo lỗi. Nếu mẫu đã được foo[xy]thay thế, mvcó thể đã vô tình di chuyển một tệp được gọi foo[xy]thay vì fooxfooycá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 nullglobtùy chọn một số shell), và sau đó chỉ gọi mvlà 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ể mvkhô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)

zshlà 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à nullglobtùy chọn chưa được bật), vì vậy, ở đây, bạn có thể ẩn zshlỗi và khôi phục stderr mvvì vậy bạn vẫn sẽ thấy các mvlỗ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 zargscũ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:

bashkhông có cú pháp để chỉ kích hoạt nullglobmột toàn cầu và failglobtù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 setlệnh, vì vậy nó chỉ là:

set files foo*
count $files > /dev/null; and mv -- $files ~/bar/

POSIXly

Không có nullglobtùy chọn nào trong POSIX shvà 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:


3

Đây có thể không phải là tốt nhất nhưng bạn có thể sử dụng findlệnh để kiểm tra xem thư mục có trống hay không:

find "foo*" -type f -exec mv {} ~/bar/ \;

1
Điều này sẽ cung cấp một foo*con đường tìm kiếm theo nghĩa đen find.
Kusalananda

3

Tôi giả sử rằng bạn đang sử dụng bash, vì lỗi này phụ thuộc vào hành vi của bash để mở rộng các khối lượng chưa từng có cho chính họ. (Để so sánh, zsh gây ra lỗi khi cố gắng mở rộng một thế giới chưa từng có.)

Vì vậy, những gì về cách giải quyết sau đây?

ls -d foo* >/dev/null 2>&1 && mv foo* ~/bar/

Điều này sẽ âm thầm bỏ qua mvnếu ls -d foo*thất bại, trong khi vẫn đăng nhập lỗi nếu ls foo*thành công nhưng mvthất bại. (Coi chừng, ls foo*có thể thất bại vì những lý do khác ngoài việc foo*không tồn tại, ví dụ, không đủ quyền, vấn đề với FS, v.v., vì vậy những điều kiện như vậy sẽ bị âm thầm bỏ qua bởi giải pháp này.)


Tôi chủ yếu quan tâm đến bash, vâng (và tôi đã gắn thẻ câu hỏi là bash). +1 khi tính đến thực tế mvcó thể thất bại vì những lý do khác ngoài việc foo*không tồn tại.
Jonik

1
(Trên thực tế tôi có lẽ sẽ không được sử dụng này vì nó là loại tiết, và điểm của lslệnh sẽ không được rất rõ ràng cho độc giả trong tương lai, ít nhất là không có nhận xét.)
Jonik

ls -d foo*cũng có thể trở lại với trạng thái thoát khác không vì các lý do khác, như sau một ln -s /nowhere foobar(ít nhất là với một số lstriển khai).
Stéphane Chazelas

3

Bạn có thể làm ví dụ

mv 1>/dev/null 2>&1 foo* ~/bar/ hoặc là mv foo* ~/bar/ 1&>2

Để biết thêm chi tiết, xem: http://mywiki.wooledge.org/BashFAQ/055


mv foo* ~/bar/ 1&>2không im lặng lệnh, nó sẽ gửi những gì đã có trên thiết bị xuất chuẩn để cũng trên stderr.
Chris Xuống

và nếu bạn đang sử dụng mingw dưới các cửa sổ (như tôi) thay thế /dev/nullbằngNUL
Rian Sanderson

Lệnh này hoạt động như thế nào? Ampersands làm gì? Làm gì 12có nghĩa là gì?
Aaron Franke

@AaronFranke 1 là thiết bị xuất chuẩn và 2 là ống stderr theo mặc định khi một quy trình Linux được tạo. Biết thêm về đường ống trong các lệnh Linux. Bạn có thể bắt đầu tại đây - digitalocean.com/community/tutorials/ từ
Mithun B

2

Bạn có thể gian lận (có thể) với perl:

perl -e 'system "mv foo* ~/bar/" if glob "foo*"'

Hãy bình luận trước khi tải xuống.
Joseph R.

2

Nếu bạn sẽ sử dụng Perl, bạn cũng có thể sử dụng mọi cách:

#!/usr/bin/perl
use strict;
use warnings;
use File::Copy;

my $target = "$ENV{HOME}/bar/";

foreach my $file (<foo*>) {
    move $file, $target  or warn "Error moving $file to $target: $!\n";
}

hoặc như một lớp lót:

perl -MFile::Copy -E 'move $_, "$ENV{HOME}/bar/" or warn "$_: $!\n" for <foo*>'

(Để biết chi tiết về movelệnh, xem tài liệu cho Tệp :: Sao chép .)


-1

Thay thế

mv foo* ~/bar/

bạn có thể làm

cp foo* ~/bar/
rm foo* 

Đơn giản, dễ đọc :)


6
Sao chép, sau đó loại bỏ, mất nhiều thời gian hơn một động thái đơn giản. Nó cũng sẽ không hoạt động nếu một tệp lớn hơn dung lượng đĩa trống có sẵn.
Anthon

11
OP muốn một giải pháp im lặng. Cả cphay rmlà im lặng nếu foo*không tồn tại.
ron rothman

-1
mv foo* ~/bar/ 2>/dev/null

Cho dù lệnh Trên có thành công hay không, chúng ta có thể tìm thấy bằng trạng thái thoát của lệnh trước đó

command: echo $?

nếu đầu ra của echo $? khác 0 có nghĩa là lệnh không thành công nếu đầu ra là 0 có nghĩa là lệnh thành công

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.