“Mv” nin sessizce başarısız olmasının bir yolu var mı?


61

Gibi bir komut mv foo* ~/bar/, eşleşen dosya yoksa, bu mesajı stderr'de üretir foo*.

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

Ancak, senaryoda bu dava üzerinde çalışıyorum tamamen iyi olurdu ve bu mesajı kayıtlarımızdan atlamak istiyorum.

mvHiçbir şey taşınmasa bile sessiz kalmanın güzel bir yolu var mı ?


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

1
İyi evet. Sanırım bazı anahtarlar gibi aklımda "daha güzel" bir şey vardı mv. :) Ama elbette yapacak.
Jonik

3
Bazı mvuygulamaların -qonları sessizleştirme seçeneğini desteklediğini gördüm , ancak bu POSIX teknik özelliklerinin bir parçası değil mv. mvGNU coreutils içerisinde, örneğin, yok değil böyle bir seçenek var.
Thomas Nyman

Peki, problemin "mv" nin nasıl sessiz tutulacağını, ama nasıl daha doğru şekilde yapılacağını sanmıyorum. Herhangi bir dosya / dir foo * olup olmadığını ve kullanıcının yazılabilir izninin olup olmadığını kontrol etmek, belki de "mv" yi çalıştırmak?
Shâu Shắc

Yanıtlar:


46

Bunu mu arıyorsunuz?

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

20
Not, yine de döndürme değeri! = 0, bu nedenle set -e( her kabuk komut dosyasında kullanılması gereken ) herhangi biri başarısız olur. || trueTek bir komutun kontrolünü devre dışı bırakmak için bir ekleyebilirsiniz .
Reto

10
@reto set -e, her kabuk betiğinde kullanılmamalıdır, çoğu durumda hata yönetimini olduğundan daha karmaşık hale getirir.
Chris Down,

1
@ChrisDown Amacınızı anlıyorum. Genellikle iki kötülük arasında bir seçimdir. Ancak, şüpheye düşersem, başarılı bir başarısızlık yerine başarılı bir başarısızlık yapmayı tercih ederim. (bir müşteri / kullanıcı tarafından görülene kadar fark edilmez). Shell scriptleri yeni başlayanlar için büyük bir mayın tarlasıdır, bir hatayı kaçırmak çok kolaydır ve senaryonuz haywire gider!
Reto

14

Aslında, sessizliğin mviyi bir yaklaşım olduğunu düşünmüyorum (unutmayın, ilginizi çekebilecek diğer şeyler hakkında da rapor verebileceğini unutmayın ~/bar. Yalnızca ifade ifadenizin sonuç döndürmemesi durumunda sesini kapatmak istiyorsunuz. Aslında hiç yerine değil.

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

Çok çekici görünmüyor ve sadece işe yarıyor bash.

VEYA

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

Sadece bulunduğunuz dışında basholan nullglobsette. Yine de glob modelinin 3 katı tekrarını ödersiniz.


İkinci formla ilgili küçük sorun: foo*geçerli dizinde glob ile eşleşen tek dosya olduğunda adlı bir dosyayı taşıyamaz foo*. Bu, kelimenin tam anlamıyla kendisiyle eşleşmeyen sert bir ifadeyle çözümlenebilir. Örn [ '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'lar mvgüzel "önce hedef" "( -t) seçeneğine sahiptir ve xargshiçbir giriş yoksa komutunu çalıştırmayı atlayabilir ( -r). Kullanarak -print0ve -0buna uygun olarak, dosya isimleri boşluklar ve diğer "komik" şeyler içerdiğinde bir karışıklık olmayacağından emin olursunuz.


2
Not -maxdepth, -print0, -0ve -r(bazıları günümüzde diğer uygulamalarda bulunurlar rağmen) ayrıca GNU oluşumudur.
Stéphane Chazelas

1
Poige, kullanıcılarımızın çoğu Linux kullanmıyor. Buradaki standart bir uygulamadır ve bir cevabın hangi kısımlarının taşınabilir olmadığını belirtmek çok yararlıdır. Site " Unix ve Linux" olarak adlandırılır ve birçok kişi bir tür BSD veya Unix veya AIX veya macOS veya gömülü sistemler kullanır. Kişisel olarak alma.
terdon

Kişisel olarak hiçbir şey almıyorum. Şimdilik gördüğüm gibi çıkardığın bir fikrim var. Bu yüzden tekrar ediyorum: Bu yorumlar "not GNU olduğunu" oldukça anlamsız buluyorum. Cevabım GNU versiyonuna dayandığına dair net bir belirti içerdiğinden, her şeyden önce, hiç eklemeye değmezler mv.
poige

5

Bunun foo*, eşleşen dosya adları listesine genişleyen bir kabuk olduğunun farkına varmak önemlidir , bu nedenle çok az şey mvyapılabilir.

Buradaki sorun, bir glob eşleşmediğinde, bazı kabukları bash(ve diğer Bourne benzeri kabukları, buggy davranışının aslında Bourne kabuğunun 70'li yılların sonlarında getirdiği gibi) komutları vermesidir.

Yani burada, foo*herhangi bir dosyayla eşleşmediğinde, komutu iptal etmek yerine (Bourne öncesi kabukları ve birkaç modern kabuğun yaptığı gibi), kabuk sözde bir foo*dosyayı geçirir mv, bu nedenle temelde mvçağrılan dosyayı taşımak ister foo*.

Bu dosya mevcut değil. Olsaydı, aslında kalıpla eşleşirdi, bu yüzden mvbir hata bildirdi. Desen foo[xy]yerine, olsaydı , mvyanlışlıkla ve dosyaları foo[xy]yerine adlı bir dosyayı yanlışlıkla taşıyabilirdi .fooxfooy

Şimdi, bu problemi olmayan kabuklarda bile (Bourne öncesi, csh, tcsh, balık, zsh, bash -O failglob öncesi), yine de bir hata ile karşı karşıya kalırsınız mv foo* ~/bar, ama bu sefer kabuktan.

Eğer bir dosya eşleşmesi yoksa bir hata olmadığını düşünmek istiyorsan foo*ve bu durumda bir şeyi taşımazsan, ilk önce dosyaların listesini (bu nullglobseçeneği kullanarak hataya neden olmayacak şekilde) oluşturmak istersin . Bazı mermiler) ve sonra sadece arama mv, liste boş değildir.

Bu, başka bir nedenden ötürü başarısız olmuş gibi (ekleme gibi) tüm hataları gizlemekten daha iyidir , muhtemelen nedenini bilmek istersiniz.mv2> /dev/nullmv

zsh içinde

files=(foo*(N)) # where the N glob qualifier activates nullglob for that glob
(($#files == 0)) || mv -- $files ~/bar/

Veya geçici bir değişken kullanmamak için isimsiz bir işlev kullanın:

() { (($# == 0)) || mv -- "$@" ~/bar/; } foo*(N)

zshBourne böceğine sahip olmayan ve bir glob eşleşmediğinde (ve nullglobseçenek etkinleştirilmemişse) komutu çalıştırmadan hata bildiren kabuklardan biridir, bu yüzden burada zshhatayı gizleyip geri yükleyebilirsiniz. stderr bunun için mvhala mvvarsa hataları görürsünüz , fakat eşleşmeyen glob'larla ilgili hatayı görmezsiniz:

(mv 2>&3 foo* ~/bar/) 3>&2 2>&-

Veya zargseğer dünya foo*çok fazla erkek dosyaya da yayılırsa, problemleri de önleyebilecek olanları kullanabilirsiniz.

autoload zargs # best in ~/.zshrc
zargs -r -- foo* -- mv -t ~/bar # here assuming GNU mv for its -t option

Ksh93’de:

files=(~(N)foo*)
((${#files[#]} == 0)) || mv -- "${files[@]}" ~/bar/

Bash:

bashnullglobyalnızca bir glob için etkinleştirilecek sözdizimine sahip değildir ve failglobseçenek iptal eder, nullglobböylece aşağıdaki gibi şeylere ihtiyacınız olur:

saved=$(shopt -p nullglob failglob) || true
shopt -s nullglob
shopt -u failglob
files=(foo*)
((${#files[@]} == 0)) || mv -- "${files[@]}" ~/bar/
eval "$saved"

veya kaydetmek için alt kabuktaki seçenekleri ayarlayın, daha önce kaydetmeniz ve sonra geri yüklemeniz gerekir.

(
  shopt -s nullglob
  shopt -u failglob
  files=(foo*)
  ((${#files[@]} == 0)) || mv -- "${files[@]}" ~/bar/
)

İçinde yash

(
  set -o nullglob
  files=(foo*)
  [ "${#files[@]}" -eq 0 ] || mv -- "${files[@]}" ~/bar/
)

İçinde fish

Balık kabuğunda nullglob davranışı setkomut için varsayılandır , bu yüzden sadece:

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

POSIXly

Hiçbir var nullglobPOSIX'deki seçenek shve konumsal parametreler dışında hiçbir dizi. Bir kürenin eşleşip eşleşmediğini tespit etmek için kullanabileceğiniz bir numara var:

set -- foo[*] foo*
if [ "$1$2" != 'foo[*]foo*' ]; then
  shift
  mv -- "$@" ~/bar/
fi

A ikisini kullanarak foo[*]ve foo*topak, hiçbir eşleme dosyası ve adı olur bir dosya var bir tane var durumda ayırt edebilir foo*(a hangi set -- foo*yapamadı).

Daha fazla okuma:


3

Muhtemelen en iyisi değil ama findklasörün boş olup olmadığını kontrol etmek için komutu kullanabilirsiniz :

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

1
Bu, hazır bir foo*arama yolu sağlayacaktır find.
Kusalananda

3

Bash kullandığınızı farz ediyorum, çünkü bu hata, eşleşmeyen globları kendilerine genişletme davranışına bağlıdır. (Karşılaştırma yaparak, zsh, eşleşmeyen bir alanı genişletmeye çalışırken bir hata veriyor.)

Peki, aşağıdaki geçici çözümü ne durumda?

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

Bu sessizce göz ardı eder mveğer ls -d foo*varsa hala hatalar günlüğe kaydederek, başarısız ls foo*başarılı ama mvbaşarısız olur. (Dikkat edin, mevcut olmayan ls foo*diğer nedenlerden dolayı başarısız olabilir foo*, örneğin, yetersiz haklar, FS ile ilgili problem, vb., Bu nedenle bu koşullar sessizce göz ardı edilir.)


Ben çoğunlukla bash ile ilgileniyorum, evet (ve soruyu şu şekilde etiketledim bash). Varolmayan mvdışındaki nedenlerden dolayı başarısız olabilecek gerçeği dikkate almak için +1 foo*.
Jonik

1
(Uygulamada muhtemelen biraz ayrıntılı olduğu için lsbunu kullanmayacağım ve komutun amacı gelecekteki okuyucular için en azından bir yorum yapmadan çok net olmayacaktı.)
Jonik

ls -d foo*Diğer nedenlerle de sıfır olmayan bir çıkış durumu ile geri dönebilir ln -s /nowhere foobar(en azından bazı lsuygulamalarda olduğu gibi).
Stéphane Chazelas

3

Örneğin yapabilirsin

mv 1>/dev/null 2>&1 foo* ~/bar/ veya mv foo* ~/bar/ 1&>2

Daha fazla ayrıntı için bakınız: http://mywiki.wooledge.org/BashFAQ/055


mv foo* ~/bar/ 1&>2komutu susturmaz, stdout'ta olacak olanı stderr'de de gönderir.
Chris Down

(Benim gibi) windows altında mingw kullanıyorsanız ve yerini /dev/nullileNUL
Rian Sanderson

Bu komut nasıl çalışır? Ve işareti ne yapar? Ne demek 1ve ne 2demek?
Aaron Franke

@AaronFranke 1 bir stdout ve 2 bir Linux işlemi yaratıldığında varsayılan olarak stderr pipe'ıdır. Linux komutlarında pipo hakkında daha fazla bilgi edinin. Buradan başlayabilirsiniz - digitalocean.com/community/tutorials/…
Mithun B

2

(Portala) ile hile yapabilirsiniz perl:

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

Aşağı oy vermeden önce lütfen yorum yapın.
Joseph R.,

2

Perl kullanacaksanız, sonuna kadar gidebilirsiniz:

#!/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";
}

veya bir astar olarak:

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

( moveKomutun detayları için File :: Copy dokümantasyonuna bakınız .)


-1

Yerine

mv foo* ~/bar/

yapabilirsin

cp foo* ~/bar/
rm foo* 

Basit, okunabilir :)


6
Kopyalama, sonra kaldırma, basit bir hareketten daha uzun sürer. Bir dosya kullanılabilir boş disk alanından büyükse, çalışmaz.
Anthon

11
OP sessiz bir çözüm istiyor. Ne cpde rmeğer sessiz foo*yok.
ron rothman

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

Yukarıdaki komutun başarılı olup olmadığı önceki komutun çıkış durumuna göre bulabiliriz

command: echo $?

echo $ çıktısı varsa? 0 dışında, çıktı 0 ise komutun başarılı olmadığı anlamına gelir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.