Take the 2-minute tour ×
Ask Ubuntu is a question and answer site for Ubuntu users and developers. It's 100% free, no registration required.

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

for FILE in `ls -A`
do
        ext=${FILE##*.}
        NUM=$(($NUM+1))
        NEWFILE=`echo $2_$NUM.$ext | sed 's/ /_/g'`

        mv "$FILE" "$NEWFILE"
done

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?

share|improve this question
    
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 at 12:04
6  
add comment

5 Answers 5

up vote 2 down vote accepted

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

share|improve this answer
    
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 at 13:46
    
Thank you very much! –  Patterson Mar 14 at 13:47
    
You're welcome! Spaces in filenames are a headache, but they're so pretty... reminds me of something... –  Xen2050 Mar 14 at 13:56
    
why instead of | xargs ... you don't use find -exec cmd {} +? –  Braiam Mar 15 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 at 12:47
add comment

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
        continue
    fi
    # ...
done
share|improve this answer
5  
+1 Do not parse ls. –  ignis Mar 14 at 12:29
add comment

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

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

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
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.

share|improve this answer
    
It worked, but it's funny. There are no pages in the manual for shopt command. –  Patterson Mar 14 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). –  steeldriver Mar 14 at 13:48
    
Got it! You are the masters in shell script. There's no better forum! –  Patterson Mar 14 at 13:54
add comment

How about:

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

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

share|improve this answer
    
-1: Please don't recommend parsing ls. This will fail on file names containing backslashes and newlines, as well as more than one space. –  terdon Mar 15 at 3:43
add comment

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.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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