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.

I want to loop through a directory and for every file that is not a directory, i want to append the names together and separate them by comma.

files=""
for f in $(ls ~/Downloads | grep -v ^d)
        do

                files+="$f"","

        done 
        echo "$files"

here the downloads folder has files like

this : 01 - Agar Mujhse Mohabbat Hai - www.downloadming.com.mp3

Expected output : 01 - Agar Mujhse Mohabbat Hai - www.downloadming.com.mp3

Actual output: 01,-,Agar,Mujhse,Mohabbat,Hai,-,www.downloadming.com.mp3

I tried many things but no help.Can anyone post a solution that would be great.

share|improve this question
1  
i think this ls ~/Downloads | grep -v ^d will prints every file or directory that doesn't start with d. –  Avinash Raj May 8 at 16:44
    
Use zsh - files=(*(.)); echo ${(j:,:)files}. –  Kevin May 8 at 21:14

4 Answers 4

up vote 12 down vote accepted

Don't parse ls

files=()

cd ~/Downloads
for file in *; do
    [[ -d $file ]] && continue
    files+=( "$file" )
done

printf "%s\n" "${files[@]}"

comma_separated=$(IFS=,; echo "${files[*]}")
echo "$comma_separated"

As you've seen, for f in $(ls) iterates over the words in the output, not the lines. Save ls for when you want human eyes to see the files, and use the shell filename patterns when you want your script to do something with filenames.

share|improve this answer
    
Thanks alot it really helped me. –  cafebabe1991 May 8 at 17:21

Shorter solution: find ~/Downloads -type f -not -name "d*" -printf "%f,"

share|improve this answer
    
The OP was using grep -v ^d to avoid listing directories, your approach will miss all files starting with d. What you wanted is find ~/Downloads -type f. –  terdon May 8 at 18:23
1  
@terdon How does ls dirname | grep -v ^d avoid listing directories? –  Jos May 8 at 19:58
    
@terdon dunno you, but in zsh + grep 2.18 + ls 8.21 ls ~/Downloads | grep -v ^d doesn't prevent listing directories. –  Braiam May 8 at 22:34
    
@Jos It won't of course, I completely misread that. I thought it was ls -l and I'd read Glenn's answer which skips directories, so I assumed. Sorry, my bad. –  terdon May 8 at 23:45
    
@terdon OK. Actually, the OP may have tried to skip directories, starting out by doing ls -l, then changing their mind to using ls and leaving the ^d in. But I read it as "skip filenames starting with d". –  Jos May 9 at 6:24

Use while read instead of for

The safest way I've found for iterating over filename lists is to pipe to while read thusly:

find ~/Downloads -type f | while read filename; do
  echo "found file called $filename";
done

As you can see, it puts the whole filename (separating by lines instead of whitespace) into the variable name you supply, here $filename.

From the original question, however, if what is actually wanted is a comma-separated list of files, then you don't even need to iterate, just use tr to translate line separators into commas:

find ~/Downloads -type f | tr '\n' ','
share|improve this answer

This is a problem Internal Field Separator. Rather, it's not a problem, it was designed that way. It splits up the input by whitespace, just as in positional parameters.

From the Bash man page:

for name [ [ in [ word ... ] ] ; ] do list ; done

The list of words following in is expanded, generating a list of items. The variable name is set to each element of this list in turn, and list is executed each time. If the in word is omitted, the for command executes list once for each positional parameter that is set. The return status is the exit status of the last command that executes. If the expansion of the items following in results in an empty list, no commands are executed, and the return status is 0.

This means, that it expands the results of the expression into a list of words. This is where the Internal Field Separator, $IFS comes in. By default, $IFS is set to whitespace (space, tab, and newline). This means that a field consists of everything up to (but not including) these whitespace characters. Because the filenames consist of spaces, it is treating them as several different fields and running the loop once for each field. The way to fix this is to adjust $IFS value.

So, you need to adjust the $IFS to something other than whitespace. Using your current method however there would be no way to tell where one file name ends and the other begins unless they all happen to have file extensions. An easier way to go about this would be using the find utility like so:

#Save the original IFS
oldifs=$IFS
#Set the new IFS to the ASCII null character \x00
IFS=$(echo -ne "\x00")

files="" #To prevent scoping issues

#Use the find utility to find and print just regular files (not directories),
#and not going further than ~/Downloads (remove '-maxlength 1' to make the
#search fully recursive), then print the filenames in the format
#file1\x00file2\x00file3\x00file4...

#However, since our new IFS is now \x00, the boundary upon which these filenames are
#separated now works as expected
for f in $(find ~/Downloads -maxdepth 1 -type f -print0)
do
  files="$files,$f"
done
echo "$files"

#Reset IFS
IFS=$oldifs

I would also like to point out that your statement ls ~/Downloads | grep -v ^d probably doesn't do what you expect. It sounds like you were trying to filter directories out, but what is actually happening here is that it's filtering out files that start with 'd'. This is because you're not using the long form of ls like ls -l. ls -l prints output like:

drwxrwxrw- user group SomeDirectory size date_modified filename

wheras 'ls' prints just the files names:

SomeDirectory
file1
file2
file3
share|improve this answer
    
I think this is not what the OP wants. If i try this i get something like this ,/home/user/Pictures/file1/home/user/Pictures/file2/home/user/Pictures/file2 the loop seems to be unnecessary because there is always only one output for all files together. –  TuKsn May 11 at 17:40

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.