How to copy and add prefix to file names in one step?

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?

Asked By: BlackCat

||

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.

Answered By: glenn jackman

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.

Answered By: terdon

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
Answered By: Chenna V

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.

Answered By: Hudson Santos

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{}"
Answered By: TonyH
Categories: Answers Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.