2

I'm trying to solve a problem involving recursive file creation and nested subdirectories. I've written a command to create the following nested directory structure:

    mkdir -p {2019,2020}/{01..03}

    ├── 2019
    │   ├── 01
    │   ├── 02
    │   ├── 03
    ├── 2020
    │   ├── 01
    │   ├── 02
    │   ├── 03

The parent directory is meant to be a year, and the subdirectories are meant to correspond to numbered months, January is 01, February is 02 etc.

I'm trying to figure out how I can use this directory structure with the cal builtin to recursively create files that are prints of the calendar year and month which correspond with each subdirectory name. The expected output would be:

    ├── 2019
    │   ├── 01
    |   |  ├── 01-2019-cal.dat   
    │   ├── 02
    |   |  ├── 02-2019-cal.dat
    │   ├── 03
    |   |  ├── 03-2019-cal.dat
    ├── 2020
    │   ├── 01
    |   |  ├── 01-2020-cal.dat
    │   ├── 02
    |   |  ├── 02-2020-cal.dat
    │   ├── 03
    |   |  ├── 03-2020-cal.dat

One command to do this just once for a given year and month is, for example, cal 03 2020 > 03-2020-cal.dat. But, how can I solve for this by passing the directory structure itself to cal and have the correct outputs generated in the correct locations? This is at the absolute limit of my command line skills... I've seen some answers to problems here that have similar elements using the find command but I don't truly understand what it is that I'm reading.

Thank you for any help you can provide.

1
  • 1
    see comment (more readable version) I added to the good answer – Olivier Dulac Sep 24 '20 at 6:15
2

You can try this, if I'm not too rusty with bash:

for d in */*; do 
  cal "${d#*/}" "${d%/*}" > "${d%/*}/${d#*/}/${d#*/}-${d%/*}-cal.dat"
done

assuming you are in the directory that has the year folders.

Which results in:

├── 2019
│   ├── 01
│   │   └── 01-2019-cal.dat
│   ├── 02
│   │   └── 02-2019-cal.dat
│   └── 03
│       └── 03-2019-cal.dat
├── 2020
│   ├── 01
│   │   └── 01-2020-cal.dat
│   ├── 02
│   │   └── 02-2020-cal.dat
│   └── 03
│       └── 03-2020-cal.dat

and cat one of the files:

cat 01-2019-cal.dat

Outputs (sorry the spanish version):

     Enero 2019       
do lu ma mi ju vi sá  
       1  2  3  4  5  
 6  7  8  9 10 11 12  
13 14 15 16 17 18 19  
20 21 22 23 24 25 26  
27 28 29 30 31 

The trick is using parameter expansion.

4
  • 2
    maybe clearer : for dir in 20*/* ; do yyyy="${dir%/*}" ; mm="${dir#*/}" ; LC_ALL="C" cal $mm $yyyy > "${dir}/${mm}-${yyyy}-cal.dat" ; done – Olivier Dulac Sep 24 '20 at 6:13
  • 1
    I added in my comment LC_ALL=C to cal's invocation to ensure cal uses the locale you want (should result in US&english calendars... but I can not test now ) – Olivier Dulac Sep 24 '20 at 6:20
  • 1
    @OlivierDulac Yes it's nicer :), maybe I'm too obssesed with one liners haha (if you want, feel free to edit the answer). By default cal will use the system locale, which is what most users will prefer IMO. – schrodigerscatcuriosity Sep 24 '20 at 12:25
  • 1
    I am actually curious: when preceding "cal" with "LC_ALL=C" : does it generate the US calendar? (and I like one-liners a lot for my everyday work... but then a few weeks later I need to use one again & need to modify it, and sometimes have a hard time finding out what I did ^^. I try to go more and more for the readability even for "one-shot" one-liners...). + feel free to edit your answer to include it, if you want to ^^ – Olivier Dulac Sep 24 '20 at 13:35

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.