This is the part of the script I am using to rename the files returned by ls -A:

for FILE in `ls -A`
        NEWFILE=`echo $2_$NUM.$ext | sed 's/ /_/g'`

        mv "$FILE" "$NEWFILE"

But can not find names with space! The $2 parameter can not be the cause of the error why I ever step a name without a space as a parameter, and this script takes that name and rename all files in the folder so to let them numbered and does not modify the file extension. Unfortunately it does not rename files with spaces in the name. Can someone help me?

  • I don't have a bash shell to hand right now, but I think it might be due to the $2_$NUM.$ext. You're concatenating the variables with literals which will make the interpreter look for the variables $2_ and $NUM.
    – Hugo Buff
    Mar 14, 2014 at 12:04
  • 6

5 Answers 5


for iterates over words, words are delimited by whitespace. You should not iterate over the output of ls, you should use * .*:

for file in * .* ; do
    if [[ $file = . || $file = .. ]] ; then
    # ...

You can include hidden files ('dot files') in the bash '*' shell glob by setting the dotglob shell option

shopt -s dotglob
for file in *
  echo "$file"

e.g. for a directory that contains file, file with spaces and .hidden file (the last of which is hidden and has a space) this produces

file with spaces
.hidden file

You may want to add the nullglob option as well to prevent an error condition in the case that the directory is empty - see the excellent BashFAQ/004 . Remember to quote the variable "$file" and also it's best practice not to use all-caps for your variable names.

  • It worked, but it's funny. There are no pages in the manual for shopt command.
    – Patterson
    Mar 14, 2014 at 13:39
  • It's a bash shell builtin - you can get basic help by typing help shopt at the bash prompt, and a list of the available options and their status by typing shopt without any arguments. For more comprehensive info you can check the bash man page (type /shopt from within man to search for the shopt section). Mar 14, 2014 at 13:48
  • Got it! You are the masters in shell script. There's no better forum!
    – Patterson
    Mar 14, 2014 at 13:54

ls -A for me writes multiple filenames on one line, separated by white space. If you tried adding -1 as in ls -A1 that would output one filename per line, and might work better for you.

I've run into the same problems with spaces in filename, espeically when using find, but separating names with a null character handles spaces & newlines in filenames:
find (...) -print0 | xargs -0 (do stuff here)

But, if you just want to rename files you might consider man rename it can do things like:

For example, to rename all files matching "*.bak" to strip the extension,
you might say
     rename 's/\.bak$//' *.bak
To translate uppercase names to lower, you'd use
     rename 'y/A-Z/a-z/' *

Or for a gui solution for a few directories Thunar has a nice Rename Multiple Files interface that can do numbering how you're describing too. I just tried some filenames with spaces combined with find to Thunar and it seems to work:
find . -type f -print0 | xargs -0 thunar -B

  • I did ls-A1 as you said. I just needed to change the value of the IFS variable because I'm using one and it is understood the output of ls-A1 spaces as separators, so he could not find files with names like this: "File test.txt"
    – Patterson
    Mar 14, 2014 at 13:46
  • You're welcome! Spaces in filenames are a headache, but they're so pretty... reminds me of something...
    – Xen2050
    Mar 14, 2014 at 13:56
  • why instead of | xargs ... you don't use find -exec cmd {} +?
    – Braiam
    Mar 15, 2014 at 1:31
  • Used -print0 | xargs -0 for the filenames containing spaces, newlines, etc. The first 2 examples in find's manpage uses xargs. And I wasn't very successful with -exec's formatting, but it probably works with the right quoting
    – Xen2050
    Mar 15, 2014 at 12:47

In case you haven't figured out yet, parsing ls is a Bad Idea®. Unless you can know that your file names will always be sane (you usually can't), you need to be able to deal with file names containing:

  • spaces and tabs
  • consecutive spaces or tabs
  • newlines (\n)
  • carriage returns (\r)
  • backslashes (\)

All of the above are allowed by the Linux kernel (and are guaranteed to drive your sysadmin mad). The following two methods can deal with any combination of the above. I am using cp file directory/ as an example command):

  1. Use find and its -exec option, the {} will be replaced by each file or directory found.

    find . -exec cp {} directory/
  2. Pipe find's results to xargs as null separated strings (-print0), tell xargs to read null separated (-0) and tell it to replace {} with each file/directory name (-I {}).

    find . -print0 | xargs -0 -I {} cp {} directory/
  3. Use the shell alone, to match dotfiles as well, activate dotglob

     shopt -s dotglob
     for i in *; do cp -v "$i" directory/; done
  4. Combine the power of find and the versatility of the shell

    find . -print0 | while IFS= read -r -d '' i; do cp "$i" directory/; done

    The IFS= disables splitting at spaces, the -r disables backslash escapes (allows backslashes to be treated literally), the -d '' sets the record separator (lines if you like) to null.


How about:

ls -A | while read fname
    echo "$fname" # your code goes here

Piping ls forces it to send 1 filename per line, read then accepts the line into the variable fname.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.