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
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It only takes a minute to sign up.
Sign up to join this communityIf 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
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 | ...
.
Oct 4, 2017 at 16:36
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.
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.
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.
"some
and file"
for a file called some file
.
ls -Q
will rewrite. And ls -Q
also adds quotes. You don't really gain anything by this approach.
-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
?
Oct 4, 2017 at 14:22
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.