I want to rename all files to lowercase in a directory without renaming subdirectories. I know rename function but it renames files and subdirectories which is not desired. Can you help?
-
Welcome to UL.sx! Please, always provide as much information about your problem as possible and explain your previous attempts; maybe even give an example. As it is, your question cannot be answered. – Bananguin Oct 25 '15 at 21:21
-
I used rename 'y/A-Z/a-z/' * and it renamed all files and subdirectories in a directory. All I want is to rename files only – mony Oct 25 '15 at 21:25
-
1please edit your question to include all information. `*` means everything. If you don't want to change everything, you need to replace `*` with a list of candidates. – Bananguin Oct 25 '15 at 21:26
-
Which `rename` were you using? There are two incompatible versions floating around. – John1024 Oct 25 '15 at 21:30
-
Version if I am correct: perl v5.18.2. Bananguin: I found that instead of * I can use eg. *.txt for txt extension. But I don't know how to apply it to all files excluding directories – mony Oct 25 '15 at 21:40
5 Answers
Everything can be done in the perl expression.
Use -f, the regular file test so that only regular files (not directories) will be renamed.
This works:
rename 'y/A-Z/a-z/ if -f;' ./*
(Using ./* instead of * ensures that no file names will be interpreted as options, even if the filename begins with a dash (-).
- 6,690
- 1
- 19
- 38
-
1Note that it renames regular files _and_ symlinks to regular files. Use `-f && ! -l` if you want to exclude the symlinks. – Stéphane Chazelas Oct 26 '15 at 21:17
-
Also note that it only transliterates those ASCII English letters. It wouldn't rename STÉPHANE to stéphane for instance. Use `PERL_UNICODE=LSA rename '$_=lc if -f' -- *` for that. – Stéphane Chazelas Oct 26 '15 at 21:18
-
@stéphane-chazelas The `--` should be at the end of the _options_ (since no options, immediately following the command name). This also means that the extra parenthesis around `-f` are not necessary. – RobertL Oct 26 '15 at 21:40
-
True, but the original `rename` implementation as shipped with `perl` up to 5.7.2 did not support options so a `--` before the code would have been treated as code and caused an error (and after the code here would just have been ignored since it doesn't contain uppercase later), while for newer versions of rename that support options via `Getopt::Long`, that `--` may appear after the code to mark the end of options (unless POSIXLY_CORRECT). Maybe a cleaner approach would be `rename 'y/A-Z/a-z/ if -f' ./*` – Stéphane Chazelas Oct 27 '15 at 07:39
-
I tried `rename -n "s/Season /s/ if -f;" ./*/*` and it sure looks like it's telling me it will rename the directories and not the files. – Andreas Aug 10 '20 at 00:12
With zsh:
autoload zmv # best in ~/.zshrc
zmv '*(#q^/)' '${(L)f}'
To rename files of any type except directory. Or:
zmv '*(#q.)' '${(L)f}'
To rename regular files only. To also include hidden files, add the D glob qualifier.
Example (using -n for dry-run):
$ ls -alQF
total 132
drwxr-xr-x 3 stephane stephane 4096 Oct 27 09:11 "."/
drwxr-xr-x 533 stephane stephane 122880 Oct 27 09:11 ".."/
drwxr-xr-x 2 stephane stephane 4096 Oct 27 09:07 "DIR"/
lrwxrwxrwx 1 stephane stephane 3 Oct 27 09:07 "DIR-LINK" -> "DIR"/
prw-r--r-- 1 stephane stephane 0 Oct 27 09:07 "FIFO"|
-rw-r--r-- 1 stephane stephane 0 Oct 27 09:11 ".HIDDEN FILE"
-rw-r--r-- 1 stephane stephane 0 Oct 27 09:07 "HOLIDAYS IN МОСВА\nRED SQUARE.JPG"
lrwxrwxrwx 1 stephane stephane 23 Oct 27 09:08 "MY-RÉSUMÉ.PDF" -> "STÉPHANE'S RÉSUMÉ.PDF"
-rw-r--r-- 1 stephane stephane 0 Oct 27 09:07 "--READ-ME--.TXT"
srwxr-xr-x 1 stephane stephane 0 Oct 27 09:09 "SOCKET"=
-rw-r--r-- 1 stephane stephane 0 Oct 27 09:07 "STÉPHANE'S RÉSUMÉ.PDF"
$ zmv -n '*(#q^/)' '${(L)f}'
mv -- DIR-LINK dir-link
mv -- FIFO fifo
mv -- HOLIDAYS\ IN\ МОСВА$'\n'RED\ SQUARE.JPG holidays\ in\ мосва$'\n'red\ square.jpg
mv -- MY-RÉSUMÉ.PDF my-résumé.pdf
mv -- --READ-ME--.TXT --read-me--.txt
mv -- SOCKET socket
mv -- STÉPHANE\'S\ RÉSUMÉ.PDF stéphane\'s\ résumé.pdf
$ zmv -n '*(#q.)' '${(L)f}'
mv -- HOLIDAYS\ IN\ МОСВА$'\n'RED\ SQUARE.JPG holidays\ in\ мосва$'\n'red\ square.jpg
mv -- --READ-ME--.TXT --read-me--.txt
mv -- STÉPHANE\'S\ RÉSUMÉ.PDF stéphane\'s\ résumé.pdf
$ zmv -n '*(#qD.)' '${(L)f}'
mv -- .HIDDEN\ FILE .hidden\ file
mv -- HOLIDAYS\ IN\ МОСВА$'\n'RED\ SQUARE.JPG holidays\ in\ мосва$'\n'red\ square.jpg
mv -- --READ-ME--.TXT --read-me--.txt
mv -- STÉPHANE\'S\ RÉSUMÉ.PDF stéphane\'s\ résumé.pdf
See how some of them would break some symbolic links.
- 522,931
- 91
- 1,010
- 1,501
You can try
cd directory ; rename 'y/A-Z/a-z/' $(ls -p | grep -v /$) ; cd -
But better use find
cd directory ; find * -prune -type f -exec rename 'y/A-Z/a-z/' {} + ; cd -
- 14,806
- 20
- 36
-
1Your first example fails on files that have any special characters (e.g., space, `*`, `?` and `[`) in their names; see [Why you shouldn't parse the output of ls(1)](http://mywiki.wooledge.org/ParsingLs). Also, it will fail altogether if you don't `cd` into the directory. The second one will work fairly well, I guess, although that's an unusual way to use `-prune`. The more common way of using `find` to look at only one directory is `find . -maxlevel 1` (although that might not be the most portable way). … (Cont’d) – G-Man Says 'Reinstate Monica' Oct 25 '15 at 23:13
-
1(Cont’d) … Also, both your answers ignore **`.`** files (e.g., `.PROFILE`); you can fix that by specifying `-a` with `ls` or by using `find . -maxlevel 1`. … … … … … … … … … … … … … … … … … … … … P.S. The question says, "I want to rename … files to lowercase … without renaming subdirectories." It doesn't say that the OP doesn't want to *recurse* into subdirectories. – G-Man Says 'Reinstate Monica' Oct 25 '15 at 23:13
-
The first one won't work as the output of `ls` won't include the `directory/` prefix. – Stéphane Chazelas Oct 27 '15 at 10:21
-
-
if `directory` contains `FOO`, then the output of `ls -p directory` will be `FOO`, not `directory/FOO`, so `rename '...' $(ls -p directory)` won't work because `FOO` is not in the current directory, it's in `directory`. – Stéphane Chazelas Oct 27 '15 at 14:42
-
Converting to lowercase can be done by typesetting a var to lowercase.
EDIT: Replaced solution with find for a solution with for.
I first tried a solution using find, hoping to find files only with -type f.
This seems to work well:
I used -maxdepth 1 so I did not change anything in other folders (which would be slightly more difficult when a directory-name is in uppercase).
This resulted in
typeset -l smallfile
echo "Find solution looks OK but read more..."
find .-maxdepth 1 -type f| while read file; do
smallfile="${file}"
mv "${file}" "${smallfile}"
done
I thought this construction would handle filenames with spaces correctly, what I tested with filenames like "File with Spaces". @Chazelas commented correctly that the solution fails with filenames ending with a space ("Too bad "). Another comment explains how to fix the find-command:find ... -print0 | while IFS= read -rd '' file (your find must support print0, mostly bash and zsh).
Since I already am using a loop, I can just as well use test to see of the current item (given by *) is a regular file.
I found a solution with a simple for-loop and show it here including my testcode.
#!/bin/bash
clear
rm -rf testfiles 2>/dev/null
mkdir -p testfiles/SomeDir #2>/dev/null
mkdir testfiles/"Crazy Dir" 2>/dev/null
touch testfiles/Aha testfiles/"File with Spaces" testfiles/"End with space "
ls -lQ testfiles
echo ===================
typeset -l smallfile
for file in testfiles/* # Stack's formatting thinks /* starts comment, so I close that here: */
do
if [ -f "${file}" ]; then
smallfile="${file}"
# echo mv "${file}" "${smallfile}"
mv "${file}" "${smallfile}"
fi
done
echo ===================
ls -lQ testfiles
For the current dir, remove testdir/ from the for-loop.
- 694
- 4
- 11
-
That still has problems with file names that _end_ in space or tab characters or contain backslash or newline characters. `for file in *` doesn't have problems with spaces as long as you remember to quote `"$file"`. – Stéphane Chazelas Oct 27 '15 at 09:02
-
@Chazelas I changed the solution into a for-loop, but you earned my upvote with your zmv solution. – Walter A Oct 27 '15 at 12:53
-
For your `read` loop, try `find ... -print0 | while IFS= read -rd '' file` (assuming `zsh` or `bash`). – Stéphane Chazelas Oct 27 '15 at 12:56
This oneliner will solve it:
find path-to-search -depth -type f -exec rename 's/(.*)\/([^\/]*)/$1\/\L$2/' {} \;
Cya!
- 137
- 4