3

Is there something like a logical for the cli? I want to achieve this

mv -t newfolder *.(png|jpg)

so that alls jpg and png files are moved into newfolder. I know it could be done with

mv -t newfolder *.png && mv -t newfolder *.jpg

But there is somewhere sytactic sugar, isn't it?

4
  • 2
    Must that be regular expression? Brace expansion+pathname expansion is not enough? mv -t newfolder *.{png,jpg} Do you need it for a specific shell?
    – manatwork
    Feb 7, 2013 at 15:09
  • 1
    @manatwork there is a slight semantic difference with the braces: If there are no pngs or no jpgs, the command with braces will result in an error if nullglob is not set (mv: rename *.foo to newfolder/*.foo: No such file or directory), or fail entirely if failglob is set (bash: no match: *.foo).
    – Kevin
    Feb 7, 2013 at 15:39
  • Of course. Probably I was too brief as actually I started to type just a request for more details. I had no intention to present that code as equivalent.
    – manatwork
    Feb 7, 2013 at 15:57
  • In your particular example, you could also do mv -t newfolder *.png *.jpg since mv supports any number of files named on the command line (up to the command line length limit imposed by the shell, which will be a concern with regex filename matching as well).
    – user
    Feb 8, 2013 at 8:34

5 Answers 5

4

In Bash:

mv -t newfolder *.@(png|jpg)
  • ?(pattern) Matches zero or one occurrence of the given patterns
  • *(pattern) Matches zero or more occurrences of the given patterns
  • +(pattern) Matches one or more occurrences of the given patterns
  • @(pattern) Matches one of the given patterns
  • !(pattern) Matches anything except one of the given patterns

It requires extglob option:

$ shopt extglob
extglob         on

If it's off you can turn it on with

$ shopt -s extglob
2
mv -t newfolder *.(png|jpg)

Is the zsh syntax, but you need to enable it with:

setopt extended_glob

Strictly speaking, you should write it:

mv -t newfolder -- *.(png|jpg)

As otherwise, if some file names start with a - character, it won't work properly.


mv -t newfolder -- *.@(png|jpg)

is the ksh syntax. It can be recognised by zsh if you turn the kshglob option on (setopt kshglob) and by bash if you turn the extglob option on (shopt -s extglob).

However note that ksh and bash both suffer from one problem in that instance: if there's no png nor jpg file, *.@(png|jpg) will be passed untouched to mv, and if there does exist a file by that (admittedly unusual) name, it will be moved to newfolder.

In bash, you can avoid that problem by turning on the failglob option. In ksh (recent versions of ksh93 only), the best you can do is write ~(N)*.@(png|jpg) (which would be the equivalent of nullglob in bash or the (N) globbing qualifier in zsh), which would cause the pattern to expand to nothing if there's no matching file, and you would then get an error from mv.

1

Nykakin's answer explain a lot. Just to add something point -

There is nothing like regex in CLI. It always globbing that you see and use.

If that would have been pure regex then why not the file and directories starting with a dot [.] are displayed when you type ls *? In regex * matches everything right ? But that's not the case with glob.

3
  • bash and zsh support regex in the [[ builtin, but not with file globbing.
    – jordanm
    Feb 7, 2013 at 17:06
  • Also, a lone * in regex matches nothing, since it means "0 or more of the last character". .* is a greedy match of everything.
    – jordanm
    Feb 7, 2013 at 17:14
  • ksh93 supports regexp in globbing (see ~(E)) but like many things ksh, it's a bit broken by design. Feb 7, 2013 at 20:06
1

As others have stated, regex is not need to accomplish your goal. I will offer an example of how to accomplish this via regex if the situation did call for it. The following will work in bash:

files=();
for file in *; 
    [[ $file =~ \.(png|jpg)$ ]] && files+=("$file")
done

mv -t newfolder "${files[@]}"
0

I don't know of anything with regexs, or any nifty syntactic sugar to get the desired result. Here's a way to get the desired result, though, that uses find.

find . -maxdepth 1 -mindepth 1 -type f -name '*.png' -o -name '*.jpg' -exec mv -t newfolder {} +
5
  • You can use regexes with find: -regex '.*\(png\|jpg\)'
    – Nykakin
    Feb 7, 2013 at 17:22
  • 1
    Note: not all versions of find support -maxdepth or -regex.
    – jordanm
    Feb 7, 2013 at 18:32
  • -1 for over-engineered complexity. How is this shorter or better than just mv -- *.png *.jpg newfolder?
    – tripleee
    Feb 8, 2013 at 9:19
  • @triplee as mentioned above, your solution will fail unless both patterns match at least one file (depending on shell settings). If there are no pngs, for instance, it will fail. The find solution works either way.
    – user17591
    Feb 8, 2013 at 16:26
  • Add a nullglob to fix that, or just ignore the error. Anyhow, my point is really that this is a way too complicated solution to a very simple problem.
    – tripleee
    Feb 8, 2013 at 17:45

You must log in to answer this question.

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