LinuxQuestions.org
Download your favorite Linux distribution at LQ ISO.
Go Back   LinuxQuestions.org > Forums > Linux Forums > Linux - Software
User Name
Password
Linux - Software This forum is for Software issues.
Having a problem installing a new program? Want to know which application is best for the job? Post your question in this forum.

Notices

Reply
 
Search this Thread
Old 07-25-2011, 04:08 PM   #1
mrmnemo
Member
 
Registered: Aug 2009
Distribution: linux
Posts: 527

Rep: Reputation: 51
rename HIDDEN files recursively


Hi,
How would I rename all files with a leading decimal point recursivley? I some how got all my music files to have a decimal point.
I tried the below and got a " sed argument to long".
[CODE]find /media/MUSIC -type f -name "*.wma" | xargs -0 sed -i 's/.\(.*\)/\1/'[CODE]

Another question, can i just use -type f with out -name ? I am sure that all the files got the decimal point added as the first character.
 
Click here to see the post LQ members have rated as the most helpful post in this thread.
Old 07-25-2011, 05:22 PM   #2
berbae
Member
 
Registered: Jul 2005
Location: France
Distribution: Arch Linux
Posts: 540

Rep: Reputation: Disabled
You can try :
Code:
#!/bin/bash
for f in $(find /media/MUSIC -type f -name "\.*"); do
    fn=$(basename $f)
    fm=${fn:1}
    mv $fn $fm
done

Last edited by berbae; 07-25-2011 at 05:31 PM.
 
1 members found this post helpful.
Old 07-25-2011, 05:32 PM   #3
MTK358
LQ 5k Club
 
Registered: Sep 2009
Posts: 6,443
Blog Entries: 3

Rep: Reputation: 713Reputation: 713Reputation: 713Reputation: 713Reputation: 713Reputation: 713Reputation: 713
That won't go into subdirectories recursively. Try this:

Code:
find /media/MUSIC -type f -name '.*.wma' | while read file
do
    basename="${file##*/}"
    mv "$file" "${file%/*}/${basename#.}"
done

Last edited by MTK358; 07-25-2011 at 05:34 PM.
 
1 members found this post helpful.
Old 07-25-2011, 05:38 PM   #4
berbae
Member
 
Registered: Jul 2005
Location: France
Distribution: Arch Linux
Posts: 540

Rep: Reputation: Disabled
Quote:
Originally Posted by MTK358 View Post
That won't go into subdirectories recursively
Yes I saw it and I edited my post to go to subdirectories, before seeing your post.
 
1 members found this post helpful.
Old 07-25-2011, 05:53 PM   #5
MTK358
LQ 5k Club
 
Registered: Sep 2009
Posts: 6,443
Blog Entries: 3

Rep: Reputation: 713Reputation: 713Reputation: 713Reputation: 713Reputation: 713Reputation: 713Reputation: 713
Quote:
Originally Posted by berbae View Post
Yes I saw it and I edited my post to go to subdirectories, before seeing your post.
Your new solution has another problem: It won't work if the filenames have spaces.
 
1 members found this post helpful.
Old 07-25-2011, 09:01 PM   #6
Nominal Animal
Senior Member
 
Registered: Dec 2010
Location: Finland
Distribution: Xubuntu, CentOS, LFS
Posts: 1,723
Blog Entries: 3

Rep: Reputation: 942Reputation: 942Reputation: 942Reputation: 942Reputation: 942Reputation: 942Reputation: 942Reputation: 942
I recommend using
Code:
find ./ -depth -name '.*.wma' -print0 | xargs -r0 rename -v 's|/\.+([^/]+)$|/$1|'
  • The -depth option for find selects depth-first search.
    In this case it does not matter since we work on files only, not on directories.
    If you rename directories too, you want to rename the deepest items first.
    I think it's best to always use -depth when renaming.
  • The -print0 option for find and the -0 option for xargs are a pair. This way the file names are separated by NULs (zero bytes); the only byte never allowed in file names. This will work correctly for even the weirdest file names, for example those containing newlines.
  • The -r option for xargs tells it to not run the command if there are no file names. It just avoids a silly error message if the find does not find anything.
  • The rename command is a common shell utility written in Perl.
    It receives a regular expression and one or more file or directory names as parameters. It will apply the regular expression to each name; if the name changes, the file or directory is renamed accordingly. In this case, it will remove any leading dots from the file name component.

Note that the regular expression for rename works on the entire argument. In this case the arguments contain the entire path. Because the regular expression should only work on the final component, the file name, the pattern starts with a slash. (There will always be a slash, if find starts at ./.)

It seems that Perl 5.10.1 fails to obtain statistics on files or directories when the name contains newlines. (Weird. Neither the stat utility or the stat() family of functions have any issues with such files. It must be some Perl brain damage.) Fortunately, this does not seem to cause any errors or even problems when running the rename command; you will just see superfluous Unsuccessful stat on filename containing newline warnings. You can safely ignore those.
 
2 members found this post helpful.
Old 07-25-2011, 10:26 PM   #7
grail
Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 7,412

Rep: Reputation: 1874Reputation: 1874Reputation: 1874Reputation: 1874Reputation: 1874Reputation: 1874Reputation: 1874Reputation: 1874Reputation: 1874Reputation: 1874Reputation: 1874
@Nominal - nice one liner although if you don't have the Perl version of rename it tends to fall in a bit of a hole
 
1 members found this post helpful.
Old 07-26-2011, 04:28 AM   #8
berbae
Member
 
Registered: Jul 2005
Location: France
Distribution: Arch Linux
Posts: 540

Rep: Reputation: Disabled
I forgot the path in the mv command. So here is my last try, with double quotes in case of space in the names :
Code:
#!/bin/bash
for f in $(find /media/MUSIC -type f -name "\.*"); do
    dn="$(dirname "$f")"
    fn="$(basename "$f")"
    fm="${fn:1}"
    mv "$dn/$fn" "$dn/$fm"
done

Last edited by berbae; 07-26-2011 at 04:39 AM.
 
1 members found this post helpful.
Old 07-26-2011, 06:14 AM   #9
grail
Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 7,412

Rep: Reputation: 1874Reputation: 1874Reputation: 1874Reputation: 1874Reputation: 1874Reputation: 1874Reputation: 1874Reputation: 1874Reputation: 1874Reputation: 1874Reputation: 1874
Quote:
with double quotes in case of space in the names
The point you are missing here is that it is the for loop which will split on white space so no amount of quoting inside will help as the splitting has already occurred.
 
1 members found this post helpful.
Old 07-26-2011, 08:00 AM   #10
MTK358
LQ 5k Club
 
Registered: Sep 2009
Posts: 6,443
Blog Entries: 3

Rep: Reputation: 713Reputation: 713Reputation: 713Reputation: 713Reputation: 713Reputation: 713Reputation: 713
Quote:
Originally Posted by berbae View Post
I forgot the path in the mv command. So here is my last try, with double quotes in case of space in the names :
Code:
#!/bin/bash
for f in $(find /media/MUSIC -type f -name "\.*"); do
    dn="$(dirname "$f")"
    fn="$(basename "$f")"
    fm="${fn:1}"
    mv "$dn/$fn" "$dn/$fm"
done
No, it will still fail with spaces in filenames. This is where the problem is:

Code:
for f in $(find /media/MUSIC -type f -name "\.*")
How is the shell supposed to know the difference between spaces in filenames and the whitespace that find uses to separate the filenames?
 
1 members found this post helpful.
Old 07-26-2011, 10:17 AM   #11
berbae
Member
 
Registered: Jul 2005
Location: France
Distribution: Arch Linux
Posts: 540

Rep: Reputation: Disabled
Yes you are right. So I think that will work better now :
Code:
#!/bin/bash
listfiles=$(find /media/MUSIC -type f -name "\.*")
while read f; do
    dn="$(dirname "$f")"
    fn="$(basename "$f")"
    fm="${fn:1}"
    mv "$dn/$fn" "$dn/$fm"
done <<< "$listfiles"

Last edited by berbae; 07-26-2011 at 10:26 AM.
 
1 members found this post helpful.
Old 07-26-2011, 01:05 PM   #12
Nominal Animal
Senior Member
 
Registered: Dec 2010
Location: Finland
Distribution: Xubuntu, CentOS, LFS
Posts: 1,723
Blog Entries: 3

Rep: Reputation: 942Reputation: 942Reputation: 942Reputation: 942Reputation: 942Reputation: 942Reputation: 942Reputation: 942
For those who wish to apply a Basic Regular Expression to all file and directory names using nothing but Bash, GNU find, GNU sed, and GNU tr, consider using this snippet:
Code:
bash -c '
    OLD_LANG="$LANG"
    OLD_LC_ALL="$LC_ALL"
    LANG=C
    LC_ALL=C
    export LANG LC_ALL
    find ./ -depth -name '"'"'.*.wma'"'"' -print0 | while read -d "" FILE ; do
        DIR="${FILE%/*}"
        OLD="${FILE##*/}"
        echo -n "$OLD" \
        | tr '\n' '\0' \
        | sed -e '"'"'s/^\.\+//'"'"' \
        | tr '\0' '\n' \
        | (
            read -d "" NEW
            if [ "$NEW" != "$OLD" -a -n "$NEW" -a -n "$OLD" ]; then
                env LANG="$OLD_LANG" LC_ALL="$OLD_LC_ALL" \
                mv -vi "$DIR/$OLD" "$DIR/$NEW" || exit $?
            fi
        )
    done
'
The first red part specifies the directories where the search is to be started in. They must all end with a slash.

The second red pattern is the glob pattern defining the file names to be modified.

The third red pattern is the regular expression or expressions to apply to the file name. Since it is a POSIX BRE (Basic Regular Expression, see Wikipedia) and not a Perl Regular Expression, parentheses and the plus ("one or more") need to be escaped, and so is a bit different to when using rename . Since this one operates on the file name, there will never be a slash in the subject.

The '"'"' may look a bit weird, but it just evaluates to one single-quote ('). This is because the entire body of the command is in single quotes. (The body will only work in Bash, so I made sure it is run with Bash too.)

Locale (LANG and LC_ALL) and tr are needed to stop sed from applying the pattern in the middle of the file name, if it happens to have newlines in it. Newlines are temporarily converted to NULs (\0), so if your pattern specifies newlines, use \0 instead of \n.

The snippet is only lightly tested, but it really should work with any file and directory names possible in Linux.
 
1 members found this post helpful.
Old 07-30-2011, 01:10 PM   #13
mrmnemo
Member
 
Registered: Aug 2009
Distribution: linux
Posts: 527

Original Poster
Rep: Reputation: 51
Wink

Quote:
Originally Posted by Nominal Animal View Post
I recommend using
Code:
find ./ -depth -name '.*.wma' -print0 | xargs -r0 rename -v 's|/\.+([^/]+)$|/$1|'
Thanks for that. I went that route to get things solved. It was the regex stuff. so, for a "." you have to back tick it to make it be seen as absolute? 's|/. vs. 's|/\. ?

Thanks to all of you guys.
Marking as solved. However, I really like what I am learning regarding regex from people that know it. While marking as solved I may post questions regarding the use of regex; that is, assuming thats ok.

Last edited by mrmnemo; 07-30-2011 at 01:11 PM. Reason: marking as solved.
 
Old 07-30-2011, 02:18 PM   #14
MTK358
LQ 5k Club
 
Registered: Sep 2009
Posts: 6,443
Blog Entries: 3

Rep: Reputation: 713Reputation: 713Reputation: 713Reputation: 713Reputation: 713Reputation: 713Reputation: 713
Quote:
Originally Posted by mrmnemo View Post
's|/. vs. 's|/\. ?
I have no idea what you mean here.

Putting a backslash before a regex special character makes it literal. So to match a literal dot:

Code:
\.
 
1 members found this post helpful.
Old 07-30-2011, 07:28 PM   #15
mrmnemo
Member
 
Registered: Aug 2009
Distribution: linux
Posts: 527

Original Poster
Rep: Reputation: 51
One more question: if my variable has () in it
Code:
find /media/music/ -depth \( -name "*.m4a" -o -name "*.wma \)
how would i format the command to work as
Code:
 for f in $((find /media/music/ -depth \( -name "*.m4a" -o -name "*.wma \)); do ffmpeg -i $f -sameq -map_meta_data ${f%.*}.mp3:$f ${f%.*}.mp3; done
I guess its really two questions. If I have options in closed in () that is ALSO insidew a variable do i have to do anything special? Also, if I want to use the file name found using the variable as the new file name would the above work?
any help would be appreciated.

Last edited by mrmnemo; 07-30-2011 at 07:32 PM. Reason: bash scripting
 
  


Reply

Tags
bash sed find


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Recursively rename some files mail4mz Programming 21 07-15-2011 10:18 AM
script to rename files recursively rs232 Linux - General 3 05-08-2011 02:05 PM
[SOLVED] how to rename files recursively, only keeping last x digits plus extension furryspider Programming 2 11-29-2009 12:55 PM
Copying hidden files recursively? mrd Linux - General 1 09-30-2005 07:51 AM
How to recursively rename files using their directory name pattern ceg4048 Linux - General 2 09-28-2005 01:16 PM


All times are GMT -5. The time now is 07:24 PM.

Main Menu
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration