14

I want to copy and rename multiple c source files in a directory.

I can copy like this:

$ cp *.c $OTHERDIR

But I want to give a prefix to all the file names:

file.c --> old#file.c

How can I do this in 1 step?

terdon
  • 242,166
BlackCat
  • 395

5 Answers5

29

a for loop:

for f in *.c; do cp -- "$f" "$OTHERDIR/old#$f"; done

I often add the -v option to cp to allow me to watch the progress.

glenn jackman
  • 85,964
8

You can use shell globbing:

for f in *.c; do cp -- "$f" "$OTHERDIR/old#$f"; done

The for variable in GLOB format will expand the glob to all matching files/directories (excluding hidden-ones) and iterate over them, saving each in turn as $variable (in the example above, $f). So, the command I show will iterate over all non-hidden files, copying them and adding the prefix.

terdon
  • 242,166
  • If I do for f in /foo/bar/*.c, $f will contain the entire path, instead only the name of the file. How can I obtain only the name of the file? – alexandernst Aug 29 '19 at 16:05
  • 1
    @alexandernst either cd /foo/bar/; for f in *c, or use for f in /foo/bar/*.c; do name=${f##*/} ... or name=$(basename "$f"). – terdon Sep 06 '19 at 11:32
2

Here is a one-liner using xargs, tr and globbing. Might have some value.

echo *.c | tr ' ' '\n' | xargs -n1 -I{} cp "{}" "PREFIX{}"

This returns all files matching *.c as a space-separated string. Next, tr turns the extra spaces into newlines (N.B. did not test file names with spaces**). Then, xargs gets populated with each file name, and runs cp with the appropriate name and prefix.

*.c can be modified for other, useful globs. Other prefixes in the xargs and cp part can be used as well.

**for those with spaces in their filenames:

(requires find that supports -print0) Similar to above, we can use find to output a null-seperated list of files, and tweak xargs with a flag to separate on null

find . -name '*.c' -print0 | xargs -0 -n1 -I{} cp "{}" "PREFIX{}"
TonyH
  • 131
0

Still another way:

for f in $(find dir/ -type f | grep -v 'bk_'); do cp -- "$f" "$(dirname $f)/bk_$(pwgen -s 4 1)_$(basename $f)"; done

Each time you run this way, it will turn dir/ from:

dir/foo/foo.c
dir/bar/bar.c
dir/baz/baz.c

Into:

dir/foo/foo.c
dir/foo/bk_R3pP_foo.c
dir/bar/bar.c
dir/bar/bk_o1JD_bar.c
dir/baz/baz.c
dir/baz/bk_0QqF_baz.c

And then:

dir/foo/foo.c
dir/foo/bk_R3pP_foo.c
dir/foo/bk_dV06_foo.c
dir/bar/bar.c
dir/bar/bk_o1JD_bar.c
dir/bar/bk_B68i_bar.c
dir/baz/baz.c
dir/baz/bk_0QqF_baz.c
dir/baz/bk_p6P3_baz.c

And then...:

dir/foo/foo.c
dir/foo/bk_R3pP_foo.c
dir/foo/bk_dV06_foo.c
dir/foo/bk_2Ar8_foo.c
dir/bar/bar.c
dir/bar/bk_o1JD_bar.c
dir/bar/bk_B68i_bar.c
dir/bar/bk_wX7Z_bar.c
dir/baz/baz.c
dir/baz/bk_0QqF_baz.c
dir/baz/bk_p6P3_baz.c
dir/baz/bk_fPd3_baz.c

.. and so on.

-1

Here's how one would do it recursively if needed,

for f in `find . -name \*.c`; do name="${f//\.\//__}"; fileName="${name//\//_}" ; echo "Copying $f as $OTHERDIR/old#$fileName"; cp -- "$f" "$OTHERDIR/$fileName"; done
Chenna V
  • 159