1

Say I am organizing a dir. It looked like this:

.
├── file_a.adoc
├── file_a.c
├── file_a.py
├── file_b.adoc
├── file_b.c
├── file_b.py
├── file_c.adoc
├── file_c.c
└── file_c.py

and now I'm at this point:

.
├── a
│   ├── file_a.adoc
│   ├── file_a.c
│   └── file_a.py
├── b
│   ├── file_b.adoc
│   ├── file_b.c
│   └── file_b.py
├── c
├── file_c.adoc
├── file_c.c
└── file_c.py

I want a command that can move all remaining non-directory files into dir c.

In this example I can use

mv *_c* c

but in more realistic scenarios, you might not have a good pattern to latch onto, or might not care to determine one.

3 Answers 3

2

One way is the literal translation of "move everything that is a file into c":

for f in *; do if [ -f "$f" ]; then mv "$f" c; fi; done

Or avoiding the if-then verbosity:

for f in *; do [ -f "$f" ] && mv "$f" c; done

better, without it failing in a set -e shell:

for f in *; do [ -f "$f" ] && mv "$f" c || true; done

My preference, failing if and only if one of the moves fails:

for f in *; do [ ! -f "$f" ] || mv "$f" c; done

Literally: for every f, either it is not a file, or it is moved to c, or we abort.

7
  • This is great, but is there a way to pipe some find output into the mv command to avoid the for and if start and end syntax? Something like mv `find . -f *` c.
    – mcp
    Jul 26, 2021 at 16:24
  • 1
    This would also move files that happened to be symbolic links to regular files. This may not be an issue in this particular instance, but the -f test is true for both regular files and for symbolic links to regular files.
    – Kusalananda
    Jul 26, 2021 at 16:52
  • @young_souvlaki You almost never want to use the output of find for any further processing.
    – Kusalananda
    Jul 26, 2021 at 16:55
  • @Kusalananda it would indeed move symbolic links as well, but not to regular files. The symlinks remain symlinks. And all relative symlinks (except those pointing at a file in .) would break.
    – zwets
    Jul 26, 2021 at 23:06
  • 1
    @young_souvlaki in theory you could do this with find . -type f -exec mv {} c \; but find recurses over subdirectories. Even if that were your intention, you'll still get in trouble as find will attempt to move the files it has just moved into c into c again.
    – zwets
    Jul 26, 2021 at 23:15
1

Using the zsh shell:

mv -- *(.) c

This expands the * globbing pattern, but the globbing qualifier (.) filters this to only include names of regular files. These are then given to the mv command and are all moved to the c directory.

From a non-zsh shell:

zsh -c 'mv -- *(.) c'

Instead of using the globbing qualifier (.) to only select names of regular files, you could use (^/) which literally means "non-directory". The difference being that *(^/) would also match names of symbolic links and other non-directory file types.

4
  • Does this answer assume all normal filenames have a . in them?
    – mcp
    Jul 26, 2021 at 16:21
  • @young_souvlaki It does not.
    – Kusalananda
    Jul 26, 2021 at 16:47
  • @young_souvlaki Would you want it to be restricted to filenames containing at least one dot, then use *.*(.) instead. This restricts it to only match regular files whose names contains a dot.
    – Kusalananda
    Jul 26, 2021 at 18:11
  • No it's good that this answer does not depend on the ..
    – mcp
    Jul 27, 2021 at 17:31
0
for i in a b c; do
  mkdir "$i" # use -p if you need it
  find . -type f -name "*_$i.*" -exec mv -t "$i" {} + # or, mv {} "$i" \;
done

You must log in to answer this question.

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