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?
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?
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.
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.
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.
(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{}"
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.
find
's output. Don't use grep
to exclude file names when find
alone can do it.
– don_crissti
Apr 18 '17 at 10:13
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
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:05cd /foo/bar/; for f in *c
, or usefor f in /foo/bar/*.c; do name=${f##*/} ...
orname=$(basename "$f")
. – terdon Sep 06 '19 at 11:32