5

This question already has an answer here:

I have a script generating an index based on each file in a folder. All file names are a number with extension.

How can I modify my loop to process them in numeric order?

for file in xml/*.xml; do
  ...
done

marked as duplicate by don_crissti, Jeff Schaller, Anthony Geoghegan, sebasth, muru Oct 4 '17 at 22:55

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • Use filenames that have zero-filled numbers: file-0001.xml, file-0002.xml etc. They would sort lexicographically. That would be easiest. Or loop over the numbers instead of the filenames, and construct the filenames from the numbers. That works if you filenames are otherwise constant. – Kusalananda Oct 4 '17 at 13:31
8

If you have GNU sort that has the option to de-limit on the \0 delimiter you can do. This way the while loop will start getting files in the sorted order for you to process. Replace the printf option with your own custom logic.

shopt -s nullglob

printf '%s\0' xml/*.xml | sort -zV | while read -rd '' file; do
    printf "%s\n" "$file"
done

shopt -u nullglob

The nullglob option is to prevent shell from expanding an empty glob if no xml files are found in the current folder. The option -u unsets it after your processing is done.


As Tony Speight rightly points out, if you don't want to mess with the shell options (e.g. it may be enabled for other reasons) you could just set in the sub-shell and let the glob expansion happen

( shopt -s nullglob; printf '%s\0' xml/*.xml ) | sort -zV |  while read -rd '' file; do
    printf "%s\n" "$file"
done
  • 1
    How did you know nullglob was unset to start with? A safer option would be to set it in a subshell, i.e. ( shopt -s nullglob; printf '%s\0' xml/*.xml ) | sort | -zV | .... – Toby Speight Oct 4 '17 at 16:36
5

If you can use zsh (for a script, it's a trade-off between convenient development and convenient deployment):

for file in xml/*.xml(n); do
  ...
done

The glob qualifier n specifies that when ordering the matches for the glob pattern, sequences of digits must be ordered according to the numerical order of the integer they represent rather than in lexicographic order, i.e. foo10bar comes after foo9bar.

You can also use setopt numeric_glob_sort to make all glob patterns use this setting.

4

Along the lines of one of Kusalananda's suggestions:

Prep:

mkdir xml && cd xml
touch 1.xml 10.xml 23.xml 999.xml
cd ..

Loop:

for f in {1..100}; do [ -f "xml/$f.xml" ] && echo process "xml/$f.xml"; done

Results in:

process xml/1.xml
process xml/10.xml
process xml/23.xml

Make the brace expansion loop large enough to cover the largest numeric file.

-3
ls -Q xml*.xml | while read line;do 
   ...
done

ls will list the files in numeric order and -Q will ensure that any spaces are dealt with.

  • No it wouldn't. The result would still be split on the spaces (and newlines). You'd end up with the words "some and file" for a file called some file. – Kusalananda Oct 4 '17 at 13:41
  • Thanks Kusalananda. I have amended my answer accordingly. – Raman Sailopal Oct 4 '17 at 13:47
  • Still a problem with filenames that contain characters that ls -Q will rewrite. And ls -Q also adds quotes. You don't really gain anything by this approach. – Kusalananda Oct 4 '17 at 13:50
  • 4
    With -Q, it won't work at all, due to the quotes. With just ls, it would work for reasonably non-exotic file names — it would choke on backslashes, newlines, and on some systems also on characters in an encoding that ls doesn't recognize as printable. And anyway, ls does not list files in numeric order. Did you mean ls -v? – Gilles Oct 4 '17 at 14:22

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