0

I am trying to select elements in a list of files using a sequence. For this I have created a list of all my files following a pattern in a folder:

ls -d -1 /my/path/S1*.zip

Where the output is:

/my/path/S1A_IW_SLC__1SDV_20180412T171648_20180412T171715_021437_024E95_BDA1.zip
/my/path/S1A_IW_SLC__1SDV_20180424T171648_20180424T171715_021612_02540A_BB21.zip
/my/path/S1A_IW_SLC__1SDV_20180506T171649_20180506T171716_021787_025996_98AB.zip
/my/path/S1A_IW_SLC__1SDV_20180518T171649_20180518T171716_021962_025F27_A15C.zip
/my/path/S1A_IW_SLC__1SDV_20180530T171650_20180530T171717_022137_0264C8_5D94.zip
/my/path/S1A_IW_SLC__1SDV_20180611T171651_20180611T171718_022312_026A3D_BBFC.zip
/my/path/S1A_IW_SLC__1SDV_20180623T171652_20180623T171719_022487_026F7C_450E.zip
/my/path/S1A_IW_SLC__1SDV_20180623T171652_20180623T171719_543456_64324F_452W.zip

I would like to create a forloop in which for every iteration, the files are processed in groups of two. So, in the first iteration, file in position 1 in my list and file in position 2 are processed. In the second interation, file in position 3 and file in position 4, etc.

To make it simpler, I will take the example of unzipping these files in grousp of 2:

for i in $(ls -d -1 /shared/Training/WROC0320_UrbanMapping_Germany/Original/S1*.zip)
do
unzip i
unizp i+1 # Not sure if i+1 works like that in `bash`
done

The problem I see with this approach is that in the first iteration it will work (i will be 1 and 2) but in the secodn i will be 2 and 3 and it should be 3 and 4.

My question is then, how to iterate over the result of ls is such way?

1

In general, you should avoid iterating over the results of ls - see for example

Compared to a for loop, a while loop gives you much more control over delimiters and file selection. For example:

printf '%s\0' my/path/*.zip | while IFS= read -r -d '' f1 && IFS= read -r -d '' f2; do 
  echo 'unzipping files'; echo unzip "$f1"; echo unzip "$f2"
done
unzipping files
unzip my/path/S1A_IW_SLC__1SDV_20180412T171648_20180412T171715_021437_024E95_BDA1.zip
unzip my/path/S1A_IW_SLC__1SDV_20180424T171648_20180424T171715_021612_02540A_BB21.zip
unzipping files
unzip my/path/S1A_IW_SLC__1SDV_20180506T171649_20180506T171716_021787_025996_98AB.zip
unzip my/path/S1A_IW_SLC__1SDV_20180518T171649_20180518T171716_021962_025F27_A15C.zip
unzipping files
unzip my/path/S1A_IW_SLC__1SDV_20180530T171650_20180530T171717_022137_0264C8_5D94.zip
unzip my/path/S1A_IW_SLC__1SDV_20180611T171651_20180611T171718_022312_026A3D_BBFC.zip
unzipping files
unzip my/path/S1A_IW_SLC__1SDV_20180623T171652_20180623T171719_022487_026F7C_450E.zip
unzip my/path/S1A_IW_SLC__1SDV_20180623T171652_20180623T171719_543456_64324F_452W.zip

Here printf '%s\0' my/path/S*.zip expands the shell glob into a list that is unambiguously delimited by the NULL character (the only character that may not appear in a Unix filepath), and then each read command reads one of them. The IFS= unsets the field separator - the only effect of this is to prevent the reads from stripping leading whitespace (in the very unlikely event that you need to handle such names).


A possibly cleaner way, using the set builtin with a shell glob to read the file names unambiguously into the positional parameter array $@:

shopt -s nullglob

set -- my/path/S*.zip
while (($# > 1)); do 
  echo "unzipping files"; 
  echo unzip "$1"; echo unzip "$2"; 
  shift 2
done
| improve this answer | |
  • Thank you for your answer. I will try to adapt it to my real application as it is slightly differnt that the example I put to make my question more clear! I'll come back with the feedback – GCGM Mar 9 at 14:23
  • I managed to adapt your code. Thanks for the links explaining why my first idea is not good practice. As I am new with bash, I am not understanding completely the sintax of your first line - printf '%s\0' my/path/*.zip | while IFS= read -r -d '' f1 && IFS= read -r -d '' f2; do. Could you please comment a little bit the logic behind? I see it works but would like to understand the idea. Thanks – GCGM Mar 10 at 13:01
  • @GCGM I've added some explanation plus an (I think better) alternative that uses the shell's positional parameter array – steeldriver Mar 10 at 23:19
1

Loop a version sorted array with step of two.

#!/bin/bash

eval \
"a=($(ls -v1 --quoting-style=shell-escape files/*))"

for ((i=0; i<${#a[@]}; i+=2)); do
    [[ -r ${a[$i]} ]] && \
        unzip "${a[$i]}"
    [[ -r ${a[(i+1)]} ]] && \
        unzip "${a[(i+1)]}"
done
| improve this answer | |
  • 1
    Thank you for your answer. I will try to adapt it to my real application as it is slightly differnt that the example I put to make my question more clear! I'll come back with the feedback – GCGM Mar 9 at 14:23
  • Thank you, your solution works also for my application but I prefer the other answer as it is a little bit easier for me to understand the sintax! – GCGM Mar 10 at 13:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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