0

I know each part of this command.

sudo mv home/* *
  • sudo: super-user do, execute with root privileges.
  • mv: move a file or directory.
  • home/*: argument of mv command. It indicates the content of home directory. The asterisk * is a wildcard that expands to the list of files in the current directory in lexicographic order.

The next argument is the destiny folder. However, I specify an asterisk as destiny directory, and if I execute the command the folder disappear completely. So, what does the * in this case?

3
  • 4
    The behaviour is unpredictable and depends upon the content of your current folder. * expands to all the files in the current directory (excluding . and ..) - you can see this by running echo *. See this page documenting globs mywiki.wooledge.org/glob#extglob and the slightly more cryptic section of the reference manual
    – Att Righ
    Commented May 22, 2018 at 13:55
  • Thanks for your comment. Regrettably, the command echo * only prints * in my computer. I tried with echo -e * too.
    – JCMiguel
    Commented May 22, 2018 at 14:19
  • 1
    Umm... does your directory contain any files? '' expands to either '' or all the files in your directory according to whether there are files in your directory.
    – Att Righ
    Commented May 22, 2018 at 14:20

3 Answers 3

3

home/* and * are shell globs, they are first expanded by the shell (so here running as yourself, not with superuser privileges), and the resulting words are then passed as argument to the command.

So if home contains ., .., .hidden. u1, u2 and the current directory contains ., .., .hidden, -i, x, y, the arguments that mv will receive are mv, home/u1, home/u2, -i, x, y.

Some implementations of mv like GNU mv have that misfeature that arguments starting with - are still recognised as options even if they occur after non-option arguments, so above that -i would be taken as a -i option to mv (sudo doesn't have that misfeature for obvious reasons).

y would be taken by mv as the destination directory, and that's where the files would disappear to if y was of type directory (or symlink to directory).

mv will not see a * or home/* argument (unless there are files actually named like that there of course). However note that some shells like most Bourne-like ones have that misfeature that if the glob doesn't match any file, it is passed as is (unexpanded) to the command.

So, with those, if the current directory only contains hidden files, then mv would receive * as the last argument. mv doesn't treat * specially (it's the shell's role to understand globs), so will treat it as the literal name of the target directory. Now, if there's no non-hidden file, that directory * won't exist either, so mv home/u1 home/u2 * would just report an error about the target directory not existing. If there was only u1 in home though, mv home/u1 * would make mv rename home/u1 to * in the current directory.

That misfeature was introduced by the Bourne shell in 1979. In earlier Unix versions (and in csh which didn't change it), the command is cancelled if none of the globs match. globs were then expanded by a /etc/glob helper (which gave their name to globs). If at least one glob matched any file though, the non-matching globs were removed. So in csh/tcsh (or zsh -o cshnullglob) a mv home/* * where the current directory has no non-hidden file would become mv home/u1 home/u2 which would move u1 into u2.

Modern shells like zsh or fish (or bash -o failglob) have fixed it by cancelling the command if any of the globs don't match.

4
  • Thanks for your answer! I don't understand why the asterisk in mv will not see the .hidden directory. What is the reason for it?
    – JCMiguel
    Commented Oct 24, 2018 at 13:10
  • 1
    @JCMiguel, file names starting with . are hidden files by convention. ls won't list them unless you pass the -A, -f or -a options, and shell globs don't them expand by default unless the leading dot it literal (as in echo .*). Several shells have options to change that default behaviour. Commented Oct 24, 2018 at 13:13
  • Thanks for the explanation. So, if I need moving the hidden files, I must write a mv .* y command, in order to move all them, for example, to a folder called y. Is it correct?
    – JCMiguel
    Commented Oct 24, 2018 at 13:26
  • @JCMiguel, there are several Q&As here covering it. Try a search for yash dotglob for the more comprehensive ones. Commented Oct 24, 2018 at 13:27
1

It all depends on what the shell globbing patterns expand to.

It does one of three things:

  1. If the * at the end of the command expands to a list of names, and the last item in that list is the name of a directory, then the command moves everything that home/* expands to, and everything that * expands to (but not that last directory name) into that directory. The files and directories matching home/* will be found alongside the home directory in the destination directory, if home/* expands to anything, and if the destination directory didn't already contain a home directory.

  2. If the * at the end of the command expands to a list of names, and the last item in that list is the name of something that is not a directory, then the command will produce an error.

  3. If * does not expand at all (because the current directory is empty), then an error will be produced.

If using mv from GNU coreutils and the last * matches one or several names that starts with a dash, this may change what the command does depending on what options mv interpret the name as.

1
  • In certain circumstances an unexpanded * will return the asterisk as a literal character. In others it will be removed entirely. (See nullglob and failglob in bash.) So mv home/* * can end up being equivalent to mv home/one home/two home/three \*. That would error out with *: not a directory. But if the home/* glob produced a single element it would get renamed as a literal asterisk. If that element was a directory then future mv /path/to/* * would move those matching elements to the newly created directory called *. Commented May 22, 2018 at 15:31
0

When I ran this myself, it ended up creating a folder called * in my destination folder, and also got rid of the source files I was grabbing from. After a few more tests the expected behavior of '* is not a directory' returned and a new directory named * was not created.

I believe that's more in line with the question being asked, since he indicates his files disappeared and echo * returns *.

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