web-dev-qa-db-ja.com

「mv」を黙って失敗させる方法はありますか?

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

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

ただし、このケースに取り組んでいるスクリプトでは問題ありません。ログからそのメッセージを省略したいと思います。

何も動かされなかった場合でもmvが静かになるように伝える良い方法はありますか?

63
Jonik

これをお探しですか?

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

実際、私はmvをミュートするのは良い方法だとは思いません(興味があるかもしれない他のことについても報告するかもしれないことを思い出してください...例:~/bar)。グロブ式が結果を返さない場合にのみ、ミュートする必要があります。実際には、まったく実行しません。

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

あまり魅力的ではないように見え、bashでのみ機能します。

OR

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

bashが設定されたnullglobにいる場合を除きます。ただし、globパターンの3倍の繰り返しの代償を払うことになります。

14

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

— GNUのmvにはニースの「宛先優先」オプション(-t)があり、xargsは、入力がない場合(-r)にコマンドの実行をスキップできます。 -print0-0を使用することで、ファイル名にスペースやその他の「おもしろい」ものが含まれていても、混乱しないようにします。

13
poige

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

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

したがって、ここでは、foo*がどのファイルとも一致しない場合、コマンドを中止するのではなく(事前Bourneシェルやいくつかの最新のシェルのように)、シェルは逐語的なfoo*ファイルをmv、基本的にmvfoo*というファイルを移動するように依頼します。

そのファイルは存在しません。一致した場合、実際にはパターンと一致したため、mvはエラーを報告します。パターンがfoo[xy]であった場合、mvfooxおよびfooyファイルではなくfoo[xy]というファイルを誤って移動した可能性があります。

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

foo*に一致するファイルがない場合にエラーではないと見なし、その場合は何も移動しない場合は、最初にファイルのリストを作成することをお勧めします(次のようなエラーが発生しない方法で)一部のシェルのnullglobオプションを使用して)、次にmvを呼び出すだけで、リストは空ではありません。

mvのエラーを隠すallよりも(2> /dev/nullを追加するのと同じように)mvは他の理由で失敗します。おそらくその理由を知りたいと思うでしょう。

zshで

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

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

または、zargsを使用することもできます。これにより、foo*グロブがあまりにも多くのファイルに拡張される場合の問題も回避されます。

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には、1つのグロブに対してのみ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

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

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

foo[*]foo*の両方のグロブを使用することにより、一致するファイルがない場合と、たまたまfoo*と呼ばれる1つのファイルがある場合(set -- foo*はできませんでした)。

もっと読む:

6

私はあなたがbashを使用していると仮定しています。このエラーは、一致しないglobをそれ自体に展開するbashの動作に依存するためです。 (比較すると、zshは一致しないglobを拡張しようとするとエラーを発生させます。)

では、次の回避策についてはどうでしょうか?

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

これは、ls -d foo*が失敗してもmvを暗黙的に無視しますが、ls foo*は成功してもmvが失敗してもエラーを記録します。 (ls foo*は、存在しないfoo*以外の理由(例:権限の不足、FSの問題など)で失敗する可能性があるため、このソリューションではこのような状態は黙って無視されることに注意してください。)

3
a3nm

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

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

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

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

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

2

あなたはPerlで(ポータブルに)カンニングすることができます:

Perl -e 'system "mv foo* ~/bar/" if glob "foo*"'
2
Joseph R.

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 のドキュメントを参照してください。)

2
Ilmari Karonen

代わりに

mv foo* ~/bar/

できるよ

cp foo* ~/bar/
rm foo* 

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

1

このコードは、ファイルの場合、ソースの場所にいるときに私のために機能しました。

[ -f ${fileName} ] && mv ${source}/${fileName} ${destination}

ディレクトリの場合

[ -d ${source} ] && mv ${source} ${destination}
0
Vincent Singha
mv foo* ~/bar/ 2>/dev/null

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

command: echo $?

エコーの出力なら$? 0以外の場合は、出力が0の場合にコマンドが失敗したことを意味し、コマンドが成功したことを意味します。

0