2

GNU bash, version 5.0.17(1)-release (x86_64-pc-linux-gnu)
Ubuntu 20.04.3 LTS

So, I downloaded like 20 7z files and I wanted to move them somewhere, but NOT the previous 7z files I already had in Downloads. (The filenames had some spaces in them, too, btw.) So I did like

mv `find -mmin -700 -iname "*.7z" -printf "\"%f\" "` ~/FOLDER/

which should have found all 7z files modified in the last ~11 hrs or so, and moved them to ~/FOLDER/.

Instead of moving all the files, it gives me a heap of errors with fragments of the filenames, like

mv: cannot stat 'Pinball': No such file or directory

I've tried the find command on its own, and it gives me a reasonable-looking list of files, each surrounded by quotes, all on one line. I ALSO tried pasting the output of the find command into mv PASTED_HERE ~/FOLDER/, like I thought the backticks basically DID, and it worked fine. (I also tried $() instead of the backticks, but I got the same errors as with the backticks.) What's going wrong?

13
  • 1
    mv $(find …) … (or equivalent with backticks) is basically as flawed as for f in $(find …); do …, the Bash pitfall number 1. Jan 3, 2022 at 18:55
  • "Backticks giving different result than pasting result" -- yes, because they're a different thing. Pasting the output on the command line has it go through all of the shell's command line processing, including parsing shell operators, quotes and expansions. Those are not done on the output of expansions, and it's very well so, since otherwise it would be impossible to handle e.g. filenames that contain quotes or something that looks like shell operators. That's also the same as happens in e.g. Perl or any other programming language when e.g. the value of a variable is used.
    – ilkkachu
    Jan 3, 2022 at 19:20
  • @ilkkachu I see now that they are different things, yes. I'm still not convinced they SHOULD be, but that's sortof irrelevant, I guess. I'm not sure why you bring up "the output of expansions", and FTR it appears that the result of command substitutions are expanded.
    – Erhannis
    Jan 3, 2022 at 19:37
  • 1
    @Erhannis, yes, that's how the POSIX shell works. Unquoted expansions are subject to word splitting and globbing, both of which you should usually avoid by quoting the expansion. Or by switching to zsh or some real programming language and using NUL-separated lists of filenames instead of quotes or newline-terminators. No, not all of us love how it is, but that's what a history of keeping compatibility with the past gives you.
    – ilkkachu
    Jan 3, 2022 at 19:49
  • 1
    @Erhannis, not everything else, notably it also doesn't process expansions and operators. You could have a file called "hello $(bad command) && echo foo", and it would work fine with rm ./*, or indeed if the name was contained in a variable, and you did rm "$filename". Zsh doesn't do word splitting on variables by default, (err, but I think it does on command substitutions, for some reason) and it has better tools on controlling it too, and for using the output of e.g. find -print0.
    – ilkkachu
    Jan 3, 2022 at 19:58

1 Answer 1

3

The quotes are not part of the filenames, so you cannot include them in the names and expect it to work.

find -mmin -700 -iname '*.7z' -exec echo mv -v {} ~/FOLDER/ \;

Or (with GNU mv),

find -mmin -700 -iname '*.7z' -exec echo mv -vt ~/FOLDER/ {} +

In either case, remove echo when you're happy with the proposed result.


As a side comment, backticks are no longer considered best practice. Instead of ` ... ` use $( ... ), possibly double-quoted if you need the result to be treated as a single word.

5
  • So, #1, while this solves my problem, it doesn't answer my question - I still don't know why the backticks (nor $()) aren't working. I include quotes in my filenames all the time, when the filenames contain spaces; why wouldn't it work in a command substitution?
    – Erhannis
    Jan 3, 2022 at 18:55
  • And #2, there's something I'm missing, because your solution DOES work, but looks like it SHOULDN'T. With echos, I get like mv -v ./This is a filename.7z /home/USERNAME/FOLDER/, which isn't a command that actually works if you just type it in - you need the quotes to get one filename-with-spaces rather than three filenames. Why does it work?
    – Erhannis
    Jan 3, 2022 at 18:55
  • 1
    #2 the shell needs quotes on the command line to know how (not to) split words. So for mv 'one word' 'item' the quotes around one word tell the shell it's a single item of eight characters (including a space). The find substitution for {} avoids using the shell to tokenise the line so you don't need quotes to keep filenames with spaces intact. The echo shows what's being handled, not a copyable command line
    – roaima
    Jan 3, 2022 at 19:03
  • #1 the quotes aren't part of the filenames and so must not be included in the expansions used by mv
    – roaima
    Jan 3, 2022 at 19:04
  • Take a read of Why does my shell script choke on whitespace or other special characters? for more details
    – roaima
    Jan 3, 2022 at 19:06

You must log in to answer this question.

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