92

A command like mv foo* ~/bar/ produces this message in stderr if there are no files matching foo*.

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

However, in the script I'm working on that case would be completely fine, and I'd like to omit that message from our logs.

Is there any nice way to tell mv to be quiet even if nothing was moved?

6
  • 16
    mv foo* ~/bar/ 2> /dev/null ? Commented Aug 21, 2013 at 11:14
  • 1
    Well yeah. I guess I had something "nicer" in mind, like some switch for mv. :) But that will do, of course.
    – Jonik
    Commented Aug 21, 2013 at 11:34
  • 4
    I've seen some mv implementations support a -q option to quiet them down, but that is not part of the POSIX specification for mv. The mv in GNU coreutils, for instance, does not have such an option. Commented Aug 21, 2013 at 11:53
  • 1
    Well, I dont think the problem is how to keep "mv" be quiet, but how to do it more properly. Checking if there is any file/dir foo*, and user has writable permission, finally execute "mv", perhaps? Commented Aug 22, 2013 at 7:00
  • It might be a good enough reason to switch to some better scripting language, such as guile, lua, python. All of them are interfacing access(2), stat(2), rename(2). And you could patch sash for your needs. See also other shells, e.g. zsh or fish Commented Apr 4, 2020 at 7:18

12 Answers 12

84

Are you looking for this?

$ mv  file dir/
mv: cannot stat ‘file’: No such file or directory
$ mv  file dir/ 2>/dev/null
# <---- Silent ----->
5
  • 41
    note, return value still != 0, therefore any set -e (which should be used in every shell script) will fail. you can add a || true to deactivate the check for a single command.
    – reto
    Commented Aug 21, 2013 at 16:28
  • 14
    @reto set -e should not be used in every shell script, in many cases it makes error management even more complex than it is without.
    – Chris Down
    Commented Nov 4, 2013 at 9:12
  • 3
    @ChrisDown I see your point. It is usually a choice between two evils. But if in doubt, I prefer a failed successful run, than a successful failure. (which doesn't get noticed until it's getting visible to a customer/user). Shell scripts are a big mine field for beginners, it's so easy to miss a an error and your script goes haywire!
    – reto
    Commented Nov 4, 2013 at 10:25
  • @reto Much better to gracefully deal with errors than just dump out of the script. Commented May 14, 2021 at 18:31
  • 1
    @TrippKinetics if you use -e and forget about a corner case, then the script will just fail with a generic message. If you don't use it and forget about a corner case, then your script continous and you have a very strange bug half a year later. But just use the appropriate technique for your task at hand, there's no "wrong" way.
    – reto
    Commented May 17, 2021 at 11:28
18

Actually, I don't think muting mv is a good approach (remember it might report you also on other things which might be of interest ... eg. missing ~/bar). You want to mute it only in case your glob expression doesn't return results. In fact rather not execute it at all.

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

Doesn't look very appealing, and works only in bash.

OR

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

only except you are in bash with nullglob set. You pay a price of 3x repetition of glob pattern though.

1
  • 2
    Minor issue with the second form: it fails to move a file named foo* when it is the only one in the current directory which matches the glob foo*. This can be worked around with a glob expression that doesn't match itself literally, tough. E.g. [ 'fo[o]*' = "$(echo fo[o]*)" ] || mv fo[o]* ~/bar/.
    – fra-san
    Commented Jun 1, 2019 at 7:50
14

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

— GNU's mv has nice "destination first" option (-t) and xargs can skip running its command if there's no input at all (-r). Using of -print0 and -0 correspondingly makes sure there wouldn't be a mess when filenames contain spaces and other "funny" stuff.

3
  • 2
    Note that -maxdepth, -print0, -0 and -r are also GNU extensions (though some of them are found in other implementations nowadays). Commented Jun 1, 2019 at 6:47
  • 2
    Poige, a lot of our users don't use Linux. It is standard practice here, and very helpful, to point out what parts of an answer are not portable. The site is called "Unix & Linux", and many people use some form of BSD or Unix or AIX or macOS or embedded systems. Don't take it personally.
    – terdon
    Commented Jun 3, 2019 at 8:24
  • 1
    I'm not taking anything personally. I though have an opinion that you've removed as I see now. So I repeat: I find those comments "note it's GNU" pretty pointless. They're not worth adding at all, first of all since my answer has clear indication that it's based on GNU version of mv.
    – poige
    Commented Jun 3, 2019 at 11:52
12

It's important to realise that it's actually the shell that expands that foo* to the list of matching file names, so there's little mv could do itself.

The problem here is that when a glob doesn't match, some shells like bash (and most other Bourne-like shells, that buggy behaviour was actually introduced by the Bourne shell in the late 70s) pass the pattern verbatim to the command.

So here, when foo* doesn't match any file, instead of aborting the command (like pre-Bourne shells and several modern shells do), the shell passes a verbatim foo* file to mv, so basically asking mv to move the file called foo*.

That file doesn't exist. If it did, it would actually have matched the pattern, so mv reports an error. If the pattern had been foo[xy] instead, mv could have accidentally move a file called foo[xy] instead of the foox and fooy files.

Now, even in those shells that don't have that problem (pre-Bourne, csh, tcsh, fish, zsh, bash -O failglob), you would still get an error upon mv foo* ~/bar, but this time by the shell.

If you want to consider it not an error if there's no file matching foo* and in that case, not move anything, you would want to build the list of files first (in a way that doesn't cause an error like by using the nullglob option of some shells), and then only call mv is the list is non-empty.

That would be better than hiding all the errors of mv (as adding 2> /dev/null would) as if mv fails for any other reason, you'd probably still want to know why.

in zsh

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

Or use an anonymous function to avoid using a temporary variable:

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

zsh is one of those shells that don't have the Bourne bug and do report an error without executing the command when a glob doesn't match (and the nullglob option has not been enabled), so here, you could hide zsh's error and restore stderr for mv so you would still see the mv errors if any, but not the error about the non-matching globs:

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

Or you could use zargs which would also avoid problems if the foo* glob would expand to too man files.

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

In ksh93:

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

In bash:

bash has no syntax to enable nullglob for one glob only, and the failglob option cancels nullglob so you'd need things like:

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

or set the options in a subshell to save have to save them before and restore them afterwards.

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

In yash

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

In fish

In the fish shell, the nullglob behaviour is the default for the set command, so it's just:

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

POSIXly

There's no nullglob option in POSIX sh and no array other than the positional parameters. There is a trick you can use though to detect whether a glob matched or not:

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

By using both a foo[*] and foo* glob, we can differentiate between the case where there's no matching file and the one where there's one file which happens to be called foo* (which a set -- foo* couldn't do).

More reading:

3

It's probably not the best but you can use find command to check if the folder is empty or not:

find "foo*" -type f -exec mv {} ~/bar/ \;
1
  • 1
    This would give a literal foo* search path to find.
    – Kusalananda
    Commented Jun 28, 2019 at 6:32
3

I'm assuming that you are using bash, because this error depends on bash's behavior to expand unmatched globs to themselves. (By comparison, zsh raises an error when trying to expand an unmatched glob.)

So, what about the following workaround?

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

This will silently ignore the mv if ls -d foo* fails, while still logging errors if ls foo* succeeds but the mv fails. (Beware, ls foo* could fail for other reasons than foo* not existing, e.g., insufficient rights, problem with the FS, etc., so such conditions would be silently ignored by this solution.)

3
  • I'm mostly interested in bash, yeah (and I did tag the question as bash). +1 for taking into account the fact that mv might fail for other reasons than foo* not existing.
    – Jonik
    Commented Aug 22, 2013 at 12:27
  • 1
    (In practice I probably won't be using this since it's kind of verbose, and the point of the ls command wouldn't be very clear for future readers, at least without a comment.)
    – Jonik
    Commented Aug 22, 2013 at 12:28
  • ls -d foo* could return with a non-zero exit status for other reasons as well, like after a ln -s /nowhere foobar (at least with some ls implementations). Commented Jun 1, 2019 at 8:39
2

You can cheat (portably) with perl:

perl -e 'system "mv foo* ~/bar/" if glob "foo*"'
1
  • Please comment before downvoting.
    – Joseph R.
    Commented Aug 21, 2013 at 18:40
2

If you're going to use Perl, you might as well go all the way:

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

or as a one-liner:

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

(For details of the move command, see the documentation for File::Copy.)

2

You can do for example

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

For more details see: http://mywiki.wooledge.org/BashFAQ/055

4
  • mv foo* ~/bar/ 1&>2 does not silence the command, it sends what would have been on stdout to also be on stderr.
    – Chris Down
    Commented Aug 21, 2013 at 12:15
  • and if you're using mingw under windows (like me) replace /dev/null with NUL Commented Mar 10, 2016 at 18:46
  • How does this command work? What do the ampersands do? What do 1 and 2 mean? Commented Dec 15, 2016 at 20:20
  • @AaronFranke 1 is stdout and 2 is stderr pipe by default when a Linux process get created. Know more on pipe in Linux commands. You can start here -- digitalocean.com/community/tutorials/…
    – Mithun B
    Commented May 31, 2019 at 10:21
1

This code worked for me for in case of a file, when you are in the source location.

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

In case of a directory

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

Whether Above command is successfull or not we can find by exit status of previous command

command: echo $?

if output of echo $? is other than 0 means command unsucessfull if output is 0 means command is successfull

-1

Instead

mv foo* ~/bar/

you can do

cp foo* ~/bar/
rm foo* 

Simple, readable :)

2
  • 6
    Copying, then removing, takes longer than a simple move. It also will not work if a file is larger than the available free disc space.
    – Anthon
    Commented Apr 14, 2014 at 7:14
  • 11
    The OP wants a solution that is silent. Neither cp nor rm are silent if foo* does not exist. Commented Oct 3, 2014 at 13:10

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .