4

I have a series of files that I would like to clean up using commandline tools available on a *nix system. The existing files are named like so.

filecopy2.txt?filename=3
filecopy4.txt?filename=33
filecopy6.txt?filename=198
filecopy8.txt?filename=188
filecopy3.txt?filename=19
filecopy5.txt?filename=1
filecopy7.txt?filename=5555

I would like them to be renamed removing all characters after and including the "?".

filecopy2.txt
filecopy4.txt
filecopy6.txt
filecopy8.txt
filecopy3.txt
filecopy5.txt
filecopy7.txt

I believe the following regex will grab the bit I want to remove from the name,

\?(.*)

I just can't figure out how to accomplish this task beyond this.

kepford
  • 41
  • 1
  • 3

5 Answers5

3
find . -depth -name '*[?]*' -exec sh -c 'for i do
  mv "$i" "${i%[?]*}"; done' sh {} +

With zsh:

autoload zmv
zmv '(**/)(*)\?*'  '$1$2'

Change it to:

zmv -Q '(**/)(*)\?*(D)' '$1$2'

if you want to rename dot files as well.

Note that if filenames may contain more than one ? character, both will only trim from the rightmost one.

Stephane Chazelas
  • 5,859
  • 2
  • 34
  • 31
  • 2
    That's awfully convoluted and complex. Look at @GoZoner's answer – Jim Garrison Jun 07 '13 at 21:02
  • 1
    @JimGarrison: GoZoner's answer ignores the `"recursively"` requirement. – jfs Jun 07 '13 at 21:51
  • @sch your first one is great and worked without installing any additional software. Thanks – kepford Jun 07 '13 at 22:55
  • +1. Why `-depth` instead of `-type f`? Is it correct: the second `sh` is to avoid losing the first filename in every batch due to `for i do` receives `"$@"` that doesn't include the first arg (it is usually a program name in other context). – jfs Jun 07 '13 at 23:32
  • @J.F.Sebastian, `-type f` is for regular files only. The OP didn't say he didn't want the fifo, symlink, directory, door, socket and other types of files renamed. Without `-depth`, you'd run into problems if directories had to be renamed. Yes, you're correct about `"$@"` starting at `$1` and `$0` receiving that `sh`. – Stephane Chazelas Jun 07 '13 at 23:55
3

A bash command:

for file in *; do
  mv $file ${file%%\?filename=*}
done
GoZoner
  • 67,920
  • 20
  • 95
  • 145
  • 1
    You could simplify to `\?*`. – Carl Norum Jun 07 '13 at 21:02
  • 1
    You're missing a `--` and quotes around the variables. Note that it will omit dotfiles. – Stephane Chazelas Jun 07 '13 at 21:03
  • No I'm not. Besides, the point is to illustrate `${var%%pattern}` – GoZoner Jun 07 '13 at 21:06
  • 3
    Then mention that your solution assumes that filenames don't contain space or tab or newline character don't start with `-`. What we know though is that they do contain wildcard characters which could be expanded (for instance, try it in a directory that contains `file?x`, `file-x` and `file.x`. – Stephane Chazelas Jun 07 '13 at 21:13
3

If all files are in the same directory (ignoring .dotfiles):

$ rename -n 's/\?filename=\d+$//' -- *

If you want to rename files recursively in a directory hierarchy:

$ find . -type f -exec rename -n 's/\?filename=\d+$//' {} +

Remove -n option, to do the renaming.

jfs
  • 399,953
  • 195
  • 994
  • 1,670
  • Note that it assumes the `perl` implementation of `rename`, not the one that comes with `util-linux`. – Stephane Chazelas Jun 07 '13 at 21:55
  • @sch: yes. Though `update-alternatives --list rename` (Ubuntu) shows the only choice: `/usr/bin/prename`. And `apt-file search $(readlink -f $(which rename))` confirms that indeed `prename` from `perl` package is used. – jfs Jun 07 '13 at 22:25
  • They are completely incompatible, so making them _alternatives_ wouldn't make sense. They used to be though (see [Debian bug #439935](http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=439935)). The util-linux `rename` is called `rename.ul` on Debian and its derivatives, but other distributions (I beleive Fedora to be amongst them) have the util-linux `rename` as the default `rename`. – Stephane Chazelas Jun 08 '13 at 00:01
1

I this case you can use the cut command:

echo 'filecopy2.txt?filename=3' | cut -d? -f1

example:

find . -type f -name "*\?*" -exec sh -c 'mv $1 $(echo $1 | cut -d\? -f1)' mv {}  \;

You can use rename if you have it:

rename 's/\?.*$//' *
Casimir et Hippolyte
  • 88,009
  • 5
  • 94
  • 125
0

I use this after downloading a bunch of files where the URL included parameters and those parameters ended up in the file name.

This is a Bash script.

for file in *; do
  mv $file ${file%%\?*};
done
rb-
  • 2,315
  • 29
  • 41