1

I have a android app that i decompiled and im writing a script to rename directories.

Inside the decompiled folder is a path:

smali/com/FOLDER/SUB-FOLDER

I want to rename the EXISTING folders to:

smali/com/NEW-NAME/SUB-NEW-NAME

I was thinking i could use the mv command but digging through the man page it looks like it cant be done. I was trying this:

mv smali/com/FOLDER smali/com/FOLDER/SUB-FOLDER -t smali/com/NEW-NAME/SUB-NEW-NAME

This only works for moving multiple files to a single destination.

EDIT:

Exisiting folders:

  smali/com/FOLDER/SUB-FOLDER 

I am trying to rename both folders while keeping the contents of each folder.

A example of renaming each folder one by one would be:

mv smali/com/FOLDER/ smali/com/NEW-NAME
mv smali/com/NEW-NAME/SUB-FOLDER smali/com/NEW-NAME/SUB-NEW-NAME
goose goose
  • 86
  • 3
  • 15
  • https://unix.stackexchange.com/q/189253/273492 – kvantour May 27 '19 at 22:04
  • Possible duplicate of [Is there a way to make mv create the directory to be moved to if it doesn't exist?](https://stackoverflow.com/questions/547719/is-there-a-way-to-make-mv-create-the-directory-to-be-moved-to-if-it-doesnt-exis) – kvantour May 27 '19 at 22:05
  • The folders already exist and have contents in each i only want to rename the folders – goose goose May 27 '19 at 22:30
  • 3
    "With a one-liner"? Unless you have a compelling technical reason, I'd argue that that constraint takes this question out of the "**practical**" criteria required for this site. Prioritizing terseness over correctness or readability is appropriate for playing code golf (as done at [codegolf.se]), not real-world use cases. – Charles Duffy May 27 '19 at 23:42
  • "Only want to rename the folders" -- so, should `smali/com/FOLDER/OTHER-NAME` be changed to `smali/com/NEW-NAME/OTHER-NAME`? You said "folders", plural, and if you rename `FOLDER` to `NEW-NAME`, it's expected for `OTHER-NAME` to come with. If you want both `FOLDER` and `NEW-NAME` to exist at the same time (so `OTHER-NAME` can still stay in `FOLDER`), then you can't rename the *existing* `OTHER-NAME`, you have to create a *new* `OTHER-NAME`. – Charles Duffy May 27 '19 at 23:54
  • I would like to rename both folders. A ugly example using booleans would be mv smali/com/FOLDER/ smali/com/NEW-NAME && mv smali/com/NEW-NAME/SUB-FOLDER smali/com/NEW-NAME/SUB-NEW-NAME – goose goose May 28 '19 at 00:13
  • In your "ugly example", the first `mv` only has one argument (other than its own name). And I don't think you answered my question about whether you intend the side-effect of renaming `FOLDER/OTHER_NAME` and not just `FOLDER/SUB-FOLDER`. – Charles Duffy May 28 '19 at 00:22
  • @CharlesDuffy to answer your question smali/com/FOLDER/SUB-FOLDER should be renamed smali/com/NEW-NAME1/NEW-NAME2 im not looking to carry over any other names besides smali/com. So the directory of "FOLDER" and its child directory of "SUB-FOLDER" both should be renamed. – goose goose May 28 '19 at 00:36
  • @goosegoose, I didn't ask about `FOLDER/SUB-FOLDER` (you were clear about that in the original question), I asked about `FOLDER/OTHER-NAME`. If you rename `FOLDER`, then `OTHER-NAME` will move with it. **Is that what you want?** Yes, or no? – Charles Duffy May 28 '19 at 00:41
  • @CharlesDuffy If im understanding your question correctly the answer is yes – goose goose May 28 '19 at 00:50

3 Answers3

1

This is an entirely trivial one-liner; for example:

rename_pieces smali/com/FOLDER/SUB-FOLDER smali/com/NEW/SUBNEW

...assuming, of course, that you run an appropriate function definition first. If you only want to rename SUB-FOLDER, after creating NEW-NAME should it not exist, that would look like:

rename_pieces() {
  local old=$1 new=$2
  [[ $new = */* ]] && mkdir -p -- "${new%/*}"
  mv -T -- "$old" "$new"
}

...and this is much more likely to be the behavior you really do want than any other interpretation, insofar as it leaves contents of FOLDER other than SUB-FOLDER alone with its original name.


By contrast, if you really want to rename both directories, that gets a lot more interesting. If we have a guarantee that both source and destination are at the same depth, this might look something like:

log_or_run() {
  if [[ $log_only ]]; then   # just log what we would run
    printf '%q ' "$@" >&2    # use printf %q to generate a safely-escaped version
    printf '\n' >&2          # ...and terminate with a newline.
  else
    "$@"                     # actually run the command
  fi
}
rename_pieces() {
  local old=$1 new=$2 common
  while [[ ${old%%/*} = "${new%%/*}" ]]; do
    common+=/"${old%%/*}"
    old=${old#*/}; new=${new#*/}
  done
  while [[ $old && $new ]]; do
    log_or_run mv -T -- "${common#/}/${old%%/*}" "${common#/}/${new%%/*}"; echo
    common+=/"${new%%/*}"
    [[ $old = */* && $new = */* ]] || return
    old=${old#*/}; new=${new#*/}
  done
}

Whereafter:

log_only=1 rename_pieces smali/com/FOLDER/SUB-FOLDER smali/com/NEW/SUBNEW

...emits on output:

mv -T -- smali/com/FOLDER smali/com/NEW
mv -T -- smali/com/NEW/SUB-FOLDER smali/com/NEW/SUBNEW

...and doing likewise without log_only=1 actually runs those commands.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
0

Can't think of anything more elegant, but the following should work: find . -path '.*com/FOLDER/SUBFOLDER' | awk '{new_dir=$1; sub("/FOLDER/SUBFOLDER","/NEW/SUBNEW", $new_dir); printf("mkdir -p %s\nmv %s %s\n", $new_dir, $1, $new_dir);}' > cmds

That gets all the required mv commands in the cmds file. Verify it is correct, and then execute using sh cmds. Although this isn't the most fun, I like this approach when dealing with important data because it is always better to verify what you're going to run.

Edit: Fixed the issue pointed out in comments about the script not creating the directory if it does not exist.

Hari Menon
  • 33,649
  • 14
  • 85
  • 108
  • From a security perspective, this is a horrid idea; it *needs* to be verified because you're generating a string, and that string could have contents parsed as syntax. By contrast, regular shell expansions *aren't* parsed as syntax, so they're safe to run no matter what their contents are (at least, safe insofar as they can't run completely unrelated commands, redirections, etc). – Charles Duffy May 27 '19 at 23:45
  • For example: `mv -- "$old" "$new"` can be trusted to never run any command but `mv`, even if `new=$'$(rm -rf ~)\'$(rm -rf ~)\''`. As soon as you use `awk` to generate that `mv` command and run it through a shell, though, those guarantees completely disappear, because you're parsing data as code whereas otherwise it would never be able to become anything but data. – Charles Duffy May 27 '19 at 23:46
  • Moreover, this **doesn't actually work**. It generates a single command, `mv ./smali/com/FOLDER/SUBFOLDER ./smali/com/NEW/SUBNEW`, but that command will fail because `smali/com/NEW` doesn't exist. – Charles Duffy May 28 '19 at 02:10
  • @CharlesDuffy Well, that's why you need to verify what was generated in the `cmds` file. I agree with the miss on generating the sub-directory, but that should be easily fixable by prefixing with mv. Your answer is definitely the more "correct" one, but if I had to do something quick, ad-hoc and dirty, this is what I would do. – Hari Menon May 28 '19 at 02:32
  • Err -- if you `mkdir new_dir`, then `mv something new-dir` will create `new-dir/something`; so in this case you're ending up with `/NEW/SUBNEW/FOLDER` instead of `/NEW/SUBNEW`. Moreover, the OP is very clear in comments on the question (after, admittedly, I spent a lot of time trying to press them for clarity) that they really do want to rename *both* directories, such that `/FOLDER/something-else` becomes `/NEW-NAME/something-else`. Were it not so, this question would be a duplicate. – Charles Duffy May 28 '19 at 02:56
  • (err, `NEW/SUBNEW/SUB-FOLDER`, rather, is the outcome of running this code as-amended). – Charles Duffy May 28 '19 at 11:38
0

Ok I'll bite...

If all you want is a one liner u can always use the AND_IF command && to turn multiple lines into one.

mv ./FOLDER ./NEW-NAME && mv ./NEW-NAME/SUB-FOLDER ./NEW-NAME/SUB-NEW-NAME

This has the added effect that the sub folder rename will only occur if the parent rename succeeded.

However doesn't it make more sense to move the subfolders first? Lets assign two variables $f for the from label and $t for to. These can go on one line but this way is easier for when we want to change things back again.

f=FOLDER; t=NEW-NAME;
mv "./$f/SUB-$f" "./$f/SUB-$t" && mv "./$f" "./$t"

All the remaining examples assume that the from and to variables are set.

Lets assign $p to the parent lookup and $s to all matching subfolders to ensure they all exist. This example and all the ones above are still pure BASH solutions.

p=(./*$f); [[ -d $p ]] && for s in $p/*$f; do mv "$s" "${s%$f}$t"; done && mv "$p" "${p%$f}$t"

Another option to consider is using find as in this one liner.

find . -d -type d -name "*$f" -execdir sh -c 'mv -- "$0" "${0%$1}$2"' "{}" "$f" "$t" \;

This will only rename matching folders but it does go and find matches in all subfolders, use -maxdepth 2 to limit traversal depth or add -mindepth to calibrate if using a different starting point.

nJoy!

nickl-
  • 8,417
  • 4
  • 42
  • 56