I tried to rename files ending with ".txt" to ".abc" using the find command as below. Now the files are not available on my disk.
find ./ -type f -iname '*.txt' -exec sh -c 'mv "$0" "$1.abc"' {} \;
can someone explain me the above command in detail what is did with the files.
Is there any possibility to retrive those, if yes how ?
You used the wrong variable and most of the files cannot be retrieved.
find ./ -type f -iname '*.txt' -exec sh -c 'mv "$0" "$1.abc"' {} \;
will rename every .txt file to a file called .abc. So if you files named bar.txt and foo.txt it will rename bar.txt to .abc and then rename foo.txt to .abc which will overwrite the original contents of bar.txt. You cannot see the file because it is .abc and is hidden under a normal list. If you run 'ls -a' you will see a file named ".abc" which will have the contents of the last .txt file that was renamed.
I am not sure how to do exactly what you wanted but running
find ./ -type f -iname '*.txt' -exec sh -c 'mv "$0" "$0.abc"' {} \;
will rename each .txt file to a .txt.abc file. So you would have bar.txt.abc and foo.txt.abc
The explanation of rondo is correct.
But what are the solutions for your problem?
If you want to replace the suffix .txt with .abc you can use rename
You will have success, if you use rename and find like this:
find . -type f -iname '*.txt' -print0 | xargs -0 rename .txt .abc
For all files found by find like x.txt or a/b.txt the appropriate command will execute, e.g.
rename .txt .abc x.txt
rename .txt .abc a/b.txt
so x.txt -> x.abc, and a/b.txt -> a/b.abc
If you only want to add the suffix .abc to all files you can still use mv
find . -type f -iname '*.txt' -print0 | xargs -0 mv {} {}.abc
With xargs for each file the command mv is executed.
BTW: with the find option "-print0" and the xargs option "-0" the commands work also with filenames which includes spaces.
Related
i wish to rename files with single digit numbers by adding a "0" in front, while ignoring files with double digit numbers, for example: 1.fileA, 2.fileB to 01.fileA, 02.fileB, and ignoring files 10.fileK, 11.fileL
when i use the following command, nothing happens. i assume it's because find returns the full path of the filename for which my rename function does not work. below, mediaDir is the path to my folder where my media files are located.
i tried the following but it still does not work:
find "$mediaDir" -type f -name "*.mp4" -exec rename 's/^(\d)\./0$1./' {} \;
so i tried the following instead, which also does not rename the files:
find "$mediaDir" -type f -name "*.mp4" -exec rename 's/^(\d)\./0$1./' $(basename {}) \;
although the following correctly lists out the basename of the files without the fullpath
find "$mediaDir" -type f -name "*.mp4" -exec basename {} \;
i have spent a whole day googling and trying but to no avail. please help.
After struggling further to understand the find command, thanks to Kusalananda who did a great job explaining "find" that "man find" does an incomplete job of:
Basic usage of -exec
In my question above, -exec doesn't work because find returns the full path to the found file, which makes my rename's regex using ^ fail since the digit to be appended to is no more at the start of the string, being within the full path.
Using "-execdir" instead, returns only the filename minus the path; however, GNU find prefixes the returned filename with "./" which defeats my regex. the following finally worked:
find "$mediaDir" -type f -name "*.mp4" -execdir rename 's/\/(\d)\./\/0$1./' {} \;
Note that the "^" is no more in my regex expression, due to the "./" prefix that "find" appends
To use -exec instead of -execdir, the following worked:
find "$mediaDir" -type f -name "*.mp4" -exec basename {} \; -exec rename 's/\/(\d)\./\/0$1./' {} \;
It seems that "-execdir" is shorter and better than "-exec".
Use find with regex to match only one digit and then process the output in a loop, executing mv to rename the file.
while read file;
do
filename=${fil3##*/}; # Extract the directory
dir=${file%/*} # Extrasct the file name
mv "$file" "$dir/0$filename" # Execute the move command
done <<< "$(find /path/to/dir -regextype posix-extended -regex "^.*/[[:digit:]]{1}\.file.*")"
How can I modify the script to not treat files with spaces as separate files?
thetime=`date +%Y-%m-%d_%H:%M:%S` #
for i in $(find . ! -name "*.filepart")
do
extn=${i##*.} # save the extension of the file
mv "$i" "${i%.*}"$(date "+_%Y-%m-%d_%H:%M:%S.${extn}")
done
mv: cannot stat ‘./user1/upload/Axle’: No such file or directory
mv: cannot stat ‘Assy’: No such file or directory
mv: cannot stat ‘Removal.doc’: No such file or directory
#find . ! -name "*.filepart"
#./user1/upload/Axle Assy Removal.doc
This won't work with files whose names contain whitespace:
for i in $(find . ! -name "*.filepart"); do # Fail
The following two methods will work for all file names:
Method 1: Use -execdir
find . -type f ! -name "*.filepart" -execdir sh -c 'f=$(basename "$1"); mv "./$f" "./${f%.*}$(date "+_%Y-%m-%d_%H:%M:%S")${f#${f%.*}}"' Mv {} \;
Method 2: Use -print0
find . -type f ! -name "*.filepart" -print0 | while IFS= read -r -d $'\0' file
do
d=$(dirname "$file")
f=${file#$d}
mv "$d$f" "$d${f%.*}$(date "+_%Y-%m-%d_%H:%M:%S")${f#${f%.*}}"
done
Further reading
How can I find and safely handle file names containing newlines, spaces or both?
So I have a directory called testdir and I want to delete the underscores in the filenames from the files in that directory.
I tried to use this command
find testdir -type f -exec ls {} \; | sed 's/_*//'
It will output the filenames without the underscores but it won't delete the underscores permanently. Could anyone help me?
Thanks!
If you are just looping through the files in your dir, use a simple loop:
while IFS= read -r file
do
echo mv "$file" "${file//_/}" #once you are sure it works, remove the echo!
done < <(find -type f -name "*_*")
This will feed the while loop with the output of the find command. Then, uses ${var//_/} to remove all _ in the name.
Why wasn't your approach working?
Because you are saying
find ... -exec ls {} \; | sed '...'
That is, you are finding something and then changing the output with sed. That is, nothing is done to the file itself.
This may help you
find testdir -type f | rename 's/_//'
Regards
How to copy a file to several directories of the form *Co*? or *52?
Apparently, just typing
cp fileA *Co*
won't work.
My other concern is that if a directory already contains fileA, I don't want it to be overwritten. That is, if the directory *Co* contains fileA, do NOT copy. Is there a one line solution for this, since I think writing a script with if-else is an overkill.
Thanks!
If your version of cp supports -n, you can do:
find . -name '*Co*' -exec cp -n fileA {} \;
If not:
find . -name '*Co*' -exec sh -c 'test -f $0/fileA || cp fileA $0' {} \;
Note that these will each descend recursively: if you don't want that you can limit the scope of find. To find either Co or *52, you can do:
find . \( -name '*Co*' -o -name '*52' \) -exec ...
As specified in title I am looking for a way how to create links to all subfolders containing specified text in their names, so for example for all subfolders of root directory containing ".app" in their names an link will be created to "/AppLinks" directory. I would like to use it in bash script (open source, free).
Does anyone know how to do that?
I searched it by google with no luck.
find yourdir -type d -name '*.app' -exec ln -s {} /AppLinks \;
Find all directories named something.app in yourdir, and create a symlink to them in /AppLinks.
single line bash-fu
function FUNCsymlink() { echo "$1"; fileName=`basename "$1"`; ln -s "$1" "/AppLinks/$fileName"; }; export -f FUNCsymlink; find `pwd`/ -maxdepth 1 -type d -iname "*.app" -exec bash -c "FUNCsymlink '{}'" \;
to easy reading:
function FUNCsymlink() {
echo "$1";
fileName=`basename "$1"`;
ln -s "$1" "/AppLinks/$fileName";
};
export -f FUNCsymlink;
find `pwd`/ -maxdepth 1 -type d -iname "*.app" -exec bash -c "FUNCsymlink '{}'" \;
you may have to adjust it a bit for your specific solution.
wherever you run it, it will create the symlinks to /AppLinks
it will only look for direct subfolders, not subfolders of subfolders, thats what I believe you need..