Reputation: 305
I have a directory with several large files. Every file comes in a pair, and I would like to use a bash loop to every time select two files, run a command line tool on them and then move on to the next pair of files.
My directory would be like: file1, file2, file3, file4, file5, file6
I would then take file1 and file2, do something, take file3 and file4, do something etc.
I only managed to do this for a single file:
for file_name in dir_name; do something; done
Upvotes: 0
Views: 1136
Reputation: 46846
I'm in the "use an array" camp, but I wouldn't populate or parse the array using external tools like find
or seq
. Everything you need is in bash already.
files=( * )
for ((i=0; i<${#files[@]}; i+=2)); do
printf '%s / %s\n' "${files[i]}" "${files[$((i+1))]}"
done
Upvotes: 0
Reputation: 14452
Going back to basic for loop, no array, etc. Will work for any shell (does not rely on bash features).
Capture the name of the first file in a pair, and execute the command when on the second file.
first=
for file_name in dir_name/* ; do
if [ "$first" ] ; then
# 2nd entry - pair
do-something "$first" "$file_name"
first=
else
# First entry - just remember.
first=$file_name
fi
done
Upvotes: 1
Reputation: 22012
If you want to be strict about handling special characters in filenames, how about:
while IFS= read -r -d "" f; do
ary+=("$f")
done < <(find "dir_name" -type f -print0 | sort -z)
for ((i=0; i<${#ary[@]}; i+=2 )); do
echo "${ary[i]}" "${ary[i+1]}"
# or some_command "${ary[i]}" "${ary[i+1]}"
done
It allows the filenames to contain whitespace, tab, newline, quote, or
any other special characters.
(Although some people dislike this kind of serious approach :-/)
Hope this helps.
Upvotes: 0
Reputation: 6048
Assuming that your file names contain no spaces or quotes:
ls dir_name \
| xargs -L 2 \
| while read FILE1 FILE2; do \
printf "file1 %s file2 %s\n" "$FILE1" "$FILE2"
done
Example:
$ ls
a b c d e f
$ ls . \
| xargs -L 2 \
| while read FILE1 FILE2; do \
printf "file1 %s file2 %s\n" "$FILE1" "$FILE2"
done
file1 a file2 b
file1 c file2 d
file1 e file2 f
Note 1:
Because of the pipes, each execution of printf
is in another shell process. Setting variables won't apply across loop iterations. If you want to do that, you can read all the lines into an array with
readarray -t FILES < <(ls dir_name | xargs -L 2)
and then iterate the array with
COUNT=0
for LINE in "${FILES[@]}"; do
FILE1="${LINE%% *}"
FILE2="${LINE##* }"
printf "file1 %s file2 %s\n" "$FILE1" "$FILE2"
((COUNT++))
done
This allows you to set variables e.g. COUNT
across iterations, however, it takes more memory and stops working when you want the files in triplets.
You can use an array for an arbitrary tuple of files:
COUNT=0
for LINE in "${FILES[@]}"; do
TUPLE=( $LINE ) # note: no quotes
printf "file1 %s file2 %s\n" "${TUPLE[0]}" "${TUPLE[1]}"
((COUNT++))
done
Note 2:
You can also use readarray
's callback mechanism:
COUNT=0
callback()
{
TUPLE=( $2 ) # note: no quotes
printf "file1 %s file2 %s\n" "${TUPLE[0]}" "${TUPLE[1]}"
((COUNT++))
}
...
readarray -t -C callback -c 1 FILES < <(ls dir_name | xargs -L 2)
Upvotes: 0
Reputation: 5056
If you know the name of the file and the amount of them you can simply do this :
#!/bin/bash
limit=20
for ((i=0; i < limit; i+=2 )) {
echo "file${i} file$(( i + 1))"
}
file0 file1
file2 file3
file4 file5
file6 file7
file8 file9
file10 file11
file12 file13
file14 file15
file16 file17
file18 file19
Assuming you do not know the name of the files, you can use this Ruby script :
#!/usr/bin/ruby
require 'find'
search_in='.'
files = []
Find.find(search_in) do |path|
files << path if path =~ /.*\.txt$/
end
files.sort_by!{|f| f.scan(/[0-9]+/)[0].to_i }
files.each_slice(2) do |a, b|
system("echo #{a} #{b}")
end
And just change echo for whatever you want.
Hope it helps!
Upvotes: 0
Reputation: 4574
One way would be to put them in an array and build pairs as below :
files=(*) ## assuming you're in current dir
for i in $(seq 0 $((${#files[@]}-1)))
do
if [[ $(( $i % 2)) == 0 ]]
then
pair="${files[$i]} ${files[$i+1]}"
echo "$pair"
# do what you want with the pair here
fi
done
Upvotes: 0