Home > Software design >  How to copy a file to a new file with a new name in same directory but across multiple directories i
How to copy a file to a new file with a new name in same directory but across multiple directories i

Time:02-10

I am trying to copy an existing file (that is found in across directories) to a new file with a new name in Bash. For example, 'Preview.json' to 'Performance.json'. I have tried using

find * -type f -name 'Preview.json' -exec cp {} {}"QA" \;

But ended up with 'Preview.jsonQA'. (I am new to Bash.) I have tried moving the "QA" in front of the {} but I got errors because of an invalid path.

CodePudding user response:

In an -exec predicate, the symbol {} represents a path that is being considered, starting at one of the starting-point directories designated in the command. Example: start/dir2/Preview.json. You can form other file names by either prepending or appending characters, but whether that makes sense depends on the details. In your case, appending produces commands such as

cp start/dir2/Preview.json start/dir2/Preview.jsonQA

which is a plausible command in the event that start/dir2/Preview.json exists. But cp does not automatically create directories in the destination path, so the result of prepending characters ...

cp start/dir2/Preview.json QAstart/dir2/Preview.json

... is not as likely to be accepted -- it depends on directory QAstart/dir2 existing.

I think what you're actually looking for may be cp commands of the form ...

cp start/dir2/Preview.json start/dir2/QAPreview.json

... but find cannot do this by itself.

For more flexibility in handling the file names discovered by find, pipe its output into another command. If you want to pass them as command-line arguments to another command, then you can interpose the xargs command to achieve that. The command on the receiving end of the pipe can be a shell function or a compound command if you wish.

For example,

# Using ./* instead of * ensures that file names beginning with - will not
# be misinterpreted as options:
find ./* -type f -name 'Preview.json' |
  while IFS= read -r name; do  # Read one line and store it in variable $name
    # the destination name needs to be computed differently if the name
    # contains a / character (the usual case) than if it doesn't:
    case "${name}" in
      */*) cp "${name}" "${name%/*}/QA${name##*/}" ;;
      *)   cp "${name}" "QA${name}" ;;
    esac
  done

Note that that assumes that none of your directory names contain newline characters (the read command would split up newline-containing filenames). That's a reasonably safe assumption, but not absolutely safe.

Of course, you would generally want to have that in a script, not to try to type it on the fly on the command line.

  • Related