「mv」を静かに失敗させる方法はありますか?


61

のようなコマンドmv foo* ~/bar/は、一致するファイルがない場合、stderrでこのメッセージを生成しますfoo*

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

ただし、このケースで作業しているスクリプトでは、まったく問題ないので、ログからそのメッセージを省略したいと思います。

mv何も動かされなかったとしても静かに言う良い方法はありますか?


13
mv foo* ~/bar/ 2> /dev/null
トーマスナイマン

1
まあ、そうだろう。のスイッチのように、「もっといい」ものを考えていたと思いmvます。:)しかし、それはもちろんです。
ジョニック

3
私はいくつかのmv実装-qがそれらを静かにするオプションをサポートするのを見ましたが、それはのためのPOSIX仕様の一部ではありませんmvmvGNUのcoreutilsの中で、例えば、ないないようなオプションがあります。
トーマスナイマン

まあ、私は問題が「mv」を静かに保つ方法であるとは思わないが、より適切にそれを行う方法。file / dir foo *があり、ユーザーが書き込み可能なアクセス権を持っているかどうかを確認し、最終的に「mv」を実行しますか?
シャウシュエック

回答:


46

これを探していますか?

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

20
戻り値はまだ!= 0であるため、any set -eすべてのシェルスクリプトで使用する必要があります)は失敗します。を追加し|| trueて、単一のコマンドのチェックを無効にすることができます。
-reto

10
@reto set -eはすべてのシェルスクリプトで使用されるべきではありません。多くの場合、エラー管理はそれが存在しない場合よりもさらに複雑になります。
クリスダウン

1
@ChrisDownあなたのポイントがわかります。通常、2つの悪の選択です。しかし、疑わしい場合は、成功した失敗よりも失敗した成功した実行を好みます。(顧客/ユーザーに表示されるまで気づかない)。シェルスクリプトは初心者にとって大きな地雷原です。エラーを見逃すのは簡単です。
レト

14

実際、ミューティングmvは良いアプローチだとは思いません(興味があるかもしれない他の事柄についても報告しているかもしれないことを思い出してください~/bar。glob式が結果を返さない場合にのみ、ミュートします。実際、まったく実行しないでください。

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

あまり魅力的ではなく、でのみ動作しbashます。

または

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

あなただけはしている除くbashnullglobセット。ただし、globパターンの3倍の繰り返しの価格を支払います。


2番目の形式の軽微な問題:foo*globに一致するのが現在のディレクトリにある唯一のファイルであるファイルの移動に失敗しますfoo*。これは、文字通り厳密には一致しないグロブ式で回避できます。例えば[ '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にmvは素敵な「destination first」オプション(-txargsがあり、入力がまったくない場合()にコマンドの実行をスキップできます-r。を使用して、-print0それに-0対応して、ファイル名にスペースやその他の「面白い」ものが含まれている場合に混乱しないようにします。


2
なお-maxdepth-print0-0-r(そのうちのいくつかは、今日、他の実装で発見されたが)また、GNU拡張です。
ステファンシャゼラス

1
Poige、多くのユーザーはLinuxを使用していません。ここでは標準的な方法であり、回答のどの部分が移植可能でないかを指摘することは非常に役立ちます。このサイトは「Unix&Linux」と呼ばれ、多くの人が何らかの形のBSDまたはUnixまたはAIXまたはmacOSまたは組み込みシステムを使用しています。個人的に受け取らないでください。
テルドン

私は何も個人的に取っていません。私はあなたが私が今見ているように削除したという意見があります。繰り返しますが、「GNUであることに注意してください」というコメントはかなり無意味であることがわかりました。私の答えは、それがGNUバージョンのに基づいていることを明確に示しているので、最初に追加する価値はありませんmv
poige

5

foo*一致するファイル名のリストにそれを展開するのは実際にはシェルであるため、mvそれ自体で実行できることはほとんどないことを認識することが重要です。

ここでの問題は、globが一致しない場合、一部のシェルbash(および他のほとんどのBourneのようなシェル、70年代後半にBourneシェルによって実際に導入されたバグ)がパターンに逐語的にコマンドを渡すことです。

ときだからここに、foo*代わりにコマンドを中止する任意のファイルを、(前のBourneシェルおよびいくつかの近代的なシェルが行うように)一致していない、シェルはそのまま渡しfoo*にファイルをmvので、基本的に求めて、mvという名前のファイルを移動することfoo*

そのファイルは存在しません。一致した場合、実際にパターンに一致したはずなのでmv、エラーを報告します。パターンがfoo[xy]代わりにあった場合、とファイルの代わりにmv呼び出されたファイルを誤って移動していた可能性があります。foo[xy]fooxfooy

さて、その問題のないシェル(Bourne、csh、tcsh、fish、zsh、bash -O failglob)でも、エラーが発生しますmv foo* ~/barが、今回はシェルによってエラーが発生します。

一致するファイルがない場合はエラーではなく、foo*その場合は何も移動しないと見なしたい場合は、最初にファイルのリストを作成します(nullglobオプションを使用するなどのエラーを引き起こさない方法で)いくつかのシェル)、そして唯一の呼び出しmvはリストが空でないことです。

他の理由で失敗したかのように(追加するように)すべてのエラーを非表示にするよりも、おそらく理由を知りたいと思うでしょう。mv2> /dev/nullmv

zshで

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

または、匿名関数を使用して、一時変数の使用を避けます。

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

zshBourneバグがないシェルの1つであり、globが一致しない場合(およびnullglobオプションが有効になっていない場合)にコマンドを実行せずにエラーを報告するため、ここで、zshエラーを非表示にして復元できますstderrの場合mvmvエラーがあれば表示されますが、一致しないグロブに関するエラーは表示されません。

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

またはzargsfoo*globがあまりにも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の場合:

bashnullglob1つのglobのみを有効にする構文はありません。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

nullglobPOSIXにはオプションがshなく、定位置パラメーター以外の配列はありません。ただし、グロブが一致したかどうかを検出するために使用できるトリックがあります。

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

a foo[*]foo*globの両方を使用することにより、一致するファイルがない場合と、たまたま呼び出されるfoo*set -- foo*できなかった)ファイルが1つある場合を区別できます。

もっと読む:


3

おそらく最善ではありませんが、findコマンドを使用して、フォルダーが空かどうかを確認できます。

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

1
これはにリテラルfoo*検索パスを提供しfindます。
クサラナンダ

3

このエラーは、bashの動作に依存して、一致しないグロブをそれ自体に展開するため、bashを使用していると想定しています。(比較すると、zshは一致しないグロブを展開しようとするとエラーを発生させます。)

では、次の回避策はどうでしょうか?

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

これはmvif ls -d foo*失敗を静かに無視しますが、ls foo*成功したがmv失敗した場合はエラーを記録します。(注意:存在しないls foo*以外の理由foo*、たとえば、不十分な権限、FSの問題などで失敗する可能性があるため、このソリューションではこのような条件は黙って無視されます。)


私はほとんどbashに興味があります(そして質問にとしてタグを付けましたbash)。存在しないmv以外の理由で失敗する可能性があるという事実を考慮して+1 foo*
ジョニック

1
(実際には、これは一種の冗長であるため、おそらくこれを使用することはないでしょう。lsコマンドの要点は、少なくともコメントなしでは、将来の読者にはあまり明確ではありません。)
Jonik

ls -d foo*ln -s /nowhere foobar(少なくともいくつかのls実装では)後など、他の理由でゼロ以外の終了ステータスで戻ることもあります。
ステファンシャゼラス

3

あなたは例えばすることができます

mv 1>/dev/null 2>&1 foo* ~/bar/ または mv foo* ~/bar/ 1&>2

詳細については、http//mywiki.wooledge.org/BashFAQ/055を参照してください。


mv foo* ~/bar/ 1&>2コマンドを黙らせるのではなく、stdoutにあったものをstderrに送信します。
クリスダウン

あなたは(私のような)は、Windows上でmingwのを使用している場合と置き換える/dev/nullNUL
リアン・サンダーソン

このコマンドはどのように機能しますか?アンパサンドは何をしますか?何をする1と、2意味ですか?
アーロンフランケ

@AaronFranke 1は、Linuxプロセスの作成時にデフォルトでstdout、2がstderrパイプです。Linuxコマンドのパイプの詳細を知ってください。あなたはここに始めることができます- digitalocean.com/community/tutorials/...
Mithun B

2

あなたは(ポータブルに)チートすることができますperl

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

投票する前にコメントしてください。
ジョセフR.

2

Perlを使用する場合は、すべての方法を実行することもできます。

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

またはワンライナーとして:

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

moveコマンドの詳細については、File :: Copyのドキュメントを参照してください。)


-1

代わりに

mv foo* ~/bar/

できるよ

cp foo* ~/bar/
rm foo* 

シンプルで読みやすい:)


6
コピーしてから削除するには、単純な移動よりも時間がかかります。また、ファイルが利用可能な空きディスク容量よりも大きい場合は機能しません。
アントン

11
OPはサイレントなソリューションを求めています。どちらcprm場合は沈黙しているfoo*が存在しません。
ロンロスマン14年

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

上記のコマンドが成功したかどうかは、前のコマンドの終了ステータスで確認できます

command: echo $?

echo $?の出力 0以外は、出力が0の場合、コマンドが成功しなかったことを意味し、コマンドが成功したことを意味します

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.