Reputation: 472
I have grabbed a whole website template using wget. It created many asset files in different subdirectories where filenames contain question marks, ./fonts/fontawesome-webfont.woff?v=4.5.0
for example. I need to truncate ?
and the rest from these filenames. I tried this command:
find . -type f -name '*\?*' -exec mv "{}" "$(echo {} | sed 's/\?.*//g')" \;
But it does not work; nothing is replaced/removed, it shows this error:
mv: './fonts/fontawesome-webfont.woff?v=4.5.0' and './fonts/fontawesome-webfont.woff?v=4.5.0' are the same file
I have these type of filenames in different directories, so I tried find
command. What am I missing here ?
Upvotes: 1
Views: 3668
Reputation: 50795
Here is a bulletproof one.
find . -name '*\?*' -type f -exec sh -c '
for fp; do
fn=${fp##*/}
echo mv "$fp" "${fp%/*}/${fn%%\?*}"
done' sh {} +
The loop above isolates the name of each selected file (in the variable named fn
) in order to be able to remove the leftmost question mark and the rest from it without breaking anything; if we did ${fp%%\?*}
instead, we couldn't process the contents of a directory with a question mark in its name. Though if we knew for sure that each file name contains only one question mark, ${fp%\?*}
would work fine.
Remove echo
if the output looks good.
Upvotes: 6
Reputation: 627126
Edit
A variation of Oguz ismail's solution:
find . -name '*\?*' -type f -exec sh -c 'for fp; do echo mv -- "$fp" $(printf "%s" "$fp" | sed "s,?[^/]*$,,"); done' sh {} +
## or
find . -name '*\?*' -type f -exec sh -c 'for fp; do echo mv "$fp" $(echo "$fp" | sed "s,?[^/]*$,,"); done' sh {} +
The find
command searches for files (-type f
) that contain a ?
char anywhere in the file name (-name '*\?*'
) starting from the current folder (.
), and once the file is found the sh
script is executed. The script is for fp; do echo mv -- "$fp" $(printf "%s" "$fp" | sed "s,?[^/]*$,,"); done
, and it iterates over the filepaths (fp
) found, and moves (renames) the filepaths with the same filepath without the part beginning with the first ?
char found in the file path.
The sed "s,?[^/]*$,,"
command actually finds a ?
, then any amount of chars other than /
till the end of string, and removes this part from the file path (so, it only removes the part of a file name, folder names can contain ?
chars).
The --
after mv
are necessary in case a filepath starts with a -
char.
NOTE: Remove the echo
from the above command to actually rename the files.
Linux 18.04 tested solution with perl-rename
You can use
find . -depth -type f -exec rename -n -v 's/\?.*//' {} +
Check the results and if they are as expected remove -n
that is meant for a "dry" run to only check the command workings. Here, -v
("verbose" option) is used to print the names of files that are successfully renamed.
You can read about how to install and use this rename
command here.
Upvotes: 0
Reputation: 35106
Assuming rename
is not available (or you're unable to get it installed), I'd probably opt for something a bit more straight forward and easier (?) to understand/maintain.
Suppose I have the following under a directory named question
:
$ ls -1 question
abc?v=4.5.0
def.txt
ghi.pdf?v=1.4.3
One idea using a simple while
loop and parameter substitution:
find . -type f -name '*\?*' |
while read -r fname
do
echo mv "${fname}" "${fname//\?*/}"
done
This generates:
mv ./question/abc?v=4.5.0 ./question/abc
mv ./question/ghi.pdf?v=1.4.3 ./question/ghi.pdf
Once you're satisfied with the generated mv
commands the echo
can be removed in order to actually run the mv
commands.
Upvotes: 1
Reputation: 44102
Please Consider @Wiktor's answer, this answers purely focuses on the find
command
As stated in the comments, the sed
is called before find
finds it files. We can work around this using -exec bash -c
and pass the pathnames ({}
) as argument:
find . -type f -exec bash -c 'echo mv $0 $(sed "s/\?.*//g" <<< $0)' "{}" \;
If the output seems find for each file, remove the echo
prefix to mv
the files:
find . -type f -exec bash -c 'mv $0 $(sed "s/\?.*//g" <<< $0)' "{}" \;
Small example on my local machine:
$ ls -lt
total 0
-rw-r--r-- 1 me wheel 0 Jun 3 17:32 'something.exe?v=23456'
-rw-r--r-- 1 me wheel 0 Jun 3 17:32 'myrandom_file?x=123'
-rw-r--r-- 1 me wheel 0 Jun 3 17:32 'fontaweietsanderssome-webfont.woff?v=4.5.0'
$
$ find . -type f -exec bash -c 'mv $0 $(sed "s/\?.*//g" <<< $0)' "{}" \;
$
$ ls -lt
total 0
-rw-r--r-- 1 me wheel 0 Jun 3 17:32 something.exe
-rw-r--r-- 1 me wheel 0 Jun 3 17:32 myrandom_file
-rw-r--r-- 1 me wheel 0 Jun 3 17:32 fontaweietsanderssome-webfont.woff
$
Upvotes: 1