0

For example, running a command on n-number of sorted subdirectories, where n is an input. Or how can I run a for loop on a range of subdirectories where I can give that range as an input? Like the following except how can I define the range here?

for d in ["sd1"-"sd2"] do ( cd "$d" && do stuff ) done

4 Answers4

2

Use brace expansion if you have a shell that supports it:

for d in sd{1..2}; do
    ( cd "$d" && dostuff )
done

With zsh, ksh93 or yash -o braceexpand (but not bash), you can do

n=4
for d in sd{1..$n}; do
    ( cd "$d" && dostuff )
done

Related question: Can I use variables inside {} expansion without `eval`?

A variation on this would be

for (( i=1; i<=n; ++i )); do
  str="sd$i"
  ( cd ... )
done

This is the C-style for loop supported by bash and other shells (still an extension to the POSIX standard though).

Kusalananda
  • 333,661
  • "1..2"- are they index here or part of the label? Like say if the range was my_directory_111 to my_directory_121, would it be: for d in my_directory_{111..121}; do ( cd "$d" && dostuff ) done – DarkMatter Aug 23 '17 at 08:02
  • @0x1 Part of the text. {2..3} expands to 2 3. Test with echo my_directory_{111..121}. – Kusalananda Aug 23 '17 at 08:05
  • Awesome! This works! What if the subdirectories are dates? How do I input dates? 2016-01-0{1..2} This works temporarily, but when it gets greater than 9, I cannot generalize it anymore without changing the command. Is there any suggestion there? – DarkMatter Aug 23 '17 at 08:12
  • @0x1, with dates is much more complicated. You can use date command to generate dates – Romeo Ninov Aug 23 '17 at 08:18
  • Thanks @StéphaneChazelas! ksh93 and bash is what I use and know. – Kusalananda Aug 23 '17 at 08:18
  • @0x1 That's a different kettle of fish altogether. Warrants a new question. – Kusalananda Aug 23 '17 at 08:18
2

With zsh, you can do:

for d (sd<1-10>(Nn-/)) (cd $d && dostuff)
  • <1-10> is a glob operator that matches decimal integer numbers between 1 and 10. It will match on 1, 001... You can make it <-> to match any positive decimal integer number.
  • (Nn-/) is a glob qualifier.
    • N to not fail if there's no match
    • n to sort the elements numerically (so sd10 comes after sd2)
    • -/ to only include files of type directory (after symlink resolution).

If you wanted to list directories between two dates, where the date is encoded in the directory's name (like sd2017-08-01):

for d (sd<->-<->-<->(Nne{'
  [[ ! $REPLY < sd2015-06-06 && ! $REPLY > sd2017-08-09 ]]'}-/))
  (cd $d && dostuff)
1

Other possibe solution is to use seq command (where is available)

for i in `seq -f "sd%03.0f" 111 121`
do
( cd "$i" && do stuff )
done
Romeo Ninov
  • 17,484
  • What does "03.0f" mean here? Sorry I am new to bash. Is it telling about the range will be 3 floating point? – DarkMatter Aug 23 '17 at 08:13
  • 03.0f is the format of the numbers. leading zero, 3 digits, zero digits after comma (integer) – Romeo Ninov Aug 23 '17 at 08:16
  • 1
    @0x1 "zero-filled to tree positions with no numbers after the decimal comma". See the printf(3) manual (man 3 printf). – Kusalananda Aug 23 '17 at 08:16
  • @RomeoNinov For dates seq -f "2016-01-%02.0f" 1 31 this worked so far. Just have to figure out a way for different months and years. Thanks a lot! – DarkMatter Aug 23 '17 at 08:23
0

Glob expansions are sorted, which means you can do something like:

state=0
for d in *; do
   case $d in "sd1") state=1;; "sd2") break;; esac
   if [ 1 -eq "$state" ]; then
      ( cd "$d" && dostuff )
   fi
done

I'm assuming here that sd1 and sd2 aren't real examples and that the range can't be described with trivial numerical ranges. If they can, I'd use Romeo Ninov's solution.

Brace expansions are very neat, but unfortunately, many POSIX shells don't have them, as they're not a POSIX requirement.

Petr Skocik
  • 28,816