Unix shell file copy flattening folder structure

后端 未结 5 1396
走了就别回头了
走了就别回头了 2021-01-30 08:52

On the UNIX bash shell (specifically Mac OS X Leopard) what would be the simplest way to copy every file having a specific extension from a folder hierarchy (including subdirect

相关标签:
5条回答
  • 2021-01-30 09:22

    If you really want to run just one command, why not cons one up and run it? Like so:

    $ find /foo  -name '*.txt' | xargs echo | sed -e 's/^/cp /' -e 's|$| /dest|' | bash -sx
    

    But that won't matter too much performance-wise unless you do this a lot or have a ton of files. Be careful of name collusions, however. I noticed in testing that GNU cp at least warns of collisions:

    cp: will not overwrite just-created `/dest/tubguide.tex' with `./texmf/tex/plain/tugboat/tubguide.tex'
    

    I think the cleanest is:

    $ find /foo  -name '*.txt' | xargs -i cp {} /dest
    

    Less syntax to remember than the -exec option.

    0 讨论(0)
  • 2021-01-30 09:23

    The answers above don't allow for name collisions as the asker didn't mind files being over-written.

    I do mind files being over-written so came up with a different approach. Replacing each / in the path with - keep the hierarchy in the names, and puts all the files in one flat folder.

    We use find to get the list of all files, then awk to create a mv command with the original filename and the modified filename then pass those to bash to be executed.

    find ./from -type f | awk '{ str=$0; sub(/\.\//, "", str); gsub(/\//, "-", str); print "mv " $0 " ./to/" str }' | bash
    

    where ./from and ./to are directories to mv from and to.

    0 讨论(0)
  • 2021-01-30 09:24

    In bash:

    find /foo -iname '*.txt' -exec cp \{\} /dest/ \;
    

    find will find all the files under the path /foo matching the wildcard *.txt, case insensitively (That's what -iname means). For each file, find will execute cp {} /dest/, with the found file in place of {}.

    0 讨论(0)
  • 2021-01-30 09:35

    The only problem with Magnus' solution is that it forks off a new "cp" process for every file, which is not terribly efficient especially if there is a large number of files.

    On Linux (or other systems with GNU coreutils) you can do:

    find . -name "*.xml" -print0 | xargs -0 echo cp -t a
    

    (The -0 allows it to work when your filenames have weird characters -- like spaces -- in them.)

    Unfortunately I think Macs come with BSD-style tools. Anyone know a "standard" equivalent to the "-t" switch?

    0 讨论(0)
  • 2021-01-30 09:42

    As far as the man page for cp on a FreeBSD box goes, there's no need for a -t switch. cp will assume the last argument on the command line to be the target directory if more than two names are passed.

    0 讨论(0)
提交回复
热议问题