8

I would like to create a directory name for each month. I know, after some playing with the shell, that:

date -d 1/01 +%b # Gives Jan
date -d 2/01 +%b # Gives Feb
.
date -d 12/01 +%b # Gives Dec

So I have used brace expansion, echo {1..12}/01 and tried to xargs it:

echo {1..12}/01 | xargs -n 1 -I {} date -d {} +%b

But it fails miserably :/ (afterwards I'd like to apply mkdir). How can I do this?

Peter Mortensen
  • 1,029
  • 1
  • 8
  • 10
JammingThebBits
  • 426
  • 4
  • 13
  • 4
    You seem to be over-programming this. If there's a reason, it would help if you explained why you don't simply write 12 lines, "mkdir "Jan"; mkdir "Feb";...". If you need to do it multiple times, put the code in a function, and call as needed. – jamesqf Aug 10 '18 at 17:12
  • 4
    Why are you naming your directories with the *names* of the months instead of the numbers 01-12? It's gotta be awkward having them `ls`ed in alphabetical order (Apr, Aug, Dec, Feb, Jan, Jul, Jun, Mar, May, Nov, Oct, Sep). – user1024 Aug 10 '18 at 18:21
  • 2
    @jamesqf Why would you do that when you can simply do *`mkdir january february [...]`* ? Unless you really need to create them separately there is no need to do it the way you suggested. – Pryftan Aug 10 '18 at 22:17
  • @user1024 I would normally agree with you but I have for one use case the name of the months (lower case though); in another I have 01january etc. Who can tell? There are numerous reasons it might or might be better one way or another. (But yes - if sorting is an issue...) – Pryftan Aug 10 '18 at 22:20
  • @Pryftan: Only a personal preference for shorter lines :-) Also, if I later wanted to change to e.g. "01Jan", it's easier in my editor to do a "c/ / 01/ 12 1" than to do the change on one line. – jamesqf Aug 11 '18 at 16:44
  • @jamesqf Well fair enough. As a long time mate of mine put it when we were collaborating on something in UNIX there are many ways to skin the *`cat`*... Personally I would find one directory an invocation a nightmare - and extra typing too although that's less of an issue for me. Or maybe it's not. I really don't know why - it's probably in part also because I'm used to taking advantage of argc/argv (both in my own code and others). Anyway, whatever works best for you is what matters in the end. – Pryftan Aug 11 '18 at 21:28

6 Answers6

17

With -I, xargs gets one argument per line as opposed to the default of one argument per (blank or newline delimited, possibly quoted) word without -I (and implies -n). So in your example date is called only once with {} expanded to the whole output of echo (which is on one line), minus the trailing newline.

Here you can do (note that that -d is a GNU extension):

printf '%s\n' {1..12}/01 | xargs -I {} date -d {} +%b | xargs mkdir --

(note that it won't work correctly in locales where month name abbreviations contain spaces or quote characters; with GNU xargs, you can work around that by using xargs -d '\n' mkdir --)

Now, to get the list of month abbreviations in your locale, querying the locale directly would make more sense:

(IFS=';'; set -o noglob; mkdir -- $(locale abmon))

(see also locale -k LC_TIME to see all the locale data in the LC_TIME category).

Or natively in zsh:

zmodload zsh/langinfo
mkdir -- ${(v)langinfo[(I)ABMON_*]}

At least on GNU systems, in some locales, month abbreviations are padded to fixed width with spaces:

$ LC_ALL=et_EE.UTF-8 locale title abmon
Estonian locale for Estonia
jaan ;veebr;märts;apr  ;mai  ;juuni;juuli;aug  ;sept ;okt  ;nov  ;dets
$ LC_ALL=zh_TW.UTF-8 locale title abmon
Chinese locale for Taiwan R.O.C.
 1月; 2月; 3月; 4月; 5月; 6月; 7月; 8月; 9月;10月;11月;12月

You may want to remove that padding.

The leading spaces would be removed by xargs -I, but not the trailing ones. With zsh:

zmodload zsh/langinfo
set -o extendedglob
mkdir -- ${${${(v)langinfo[(I)ABMON*]}##[[:space:]]#}%%[[:space:]]#}
Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
9

Try a loop?

$ for m in {1..12}; do
> date -d "$m"/01 +%b
> done
jan
feb
mar
apr
maj
jun
jul
aug
sep
okt
nov
dec

If you want to make a directory for each month, I would do something like:

for m in {1..12}; do newdir=$(date -d "$m"/01 +%b); mkdir "$newdir"; done
maulinglawns
  • 8,426
  • 2
  • 28
  • 36
5

Your command does not work, because of using -I changes the delimiter of xargs:

-I replace-str
Replace occurrences of replace-str in the initial-arguments with names read from standard input. Also, unquoted blanks do not terminate input items; instead the separator is the newline character.

You can add -d " " to xargs to make it work. But you don't even need -I{} in your case:

Try this,

echo {1..12}/01 | xargs -n1 date +%b -d | xargs mkdir
pLumo
  • 22,231
  • 2
  • 41
  • 66
  • 1
    Note that for GNU `date` to accept options after non-option arguments like that, there must not be a `POSIXLY_CORRECT` variable in the environment. – Stéphane Chazelas Aug 10 '18 at 12:19
5

In shells with brace expansion, and date accepting DATAFILE input, try

echo {01..12}/01$'\n' | date -f- +"mkdir %b"

and pipe into shell if happy with the result.

RudiC
  • 8,889
  • 2
  • 10
  • 22
3

You where so so close.

The problem is that echo is producing a single line 1/01 2/01 3/01 4/01 5/01 6/01 7/01 8/01 9/01 10/01 11/01 12/01, and that xargs is using the newline character as a field separator (not the space).

The solution: Tell echo to put a space between each field.

echo -e {1..12}/01\\n | xargs -n 1 -I {} date -d {} +%b

I only inserted \\n at the end of the echo.

Then to make the directories add |xargs mkdir

e.g.

echo -e {1..12}/01\\n | xargs -n 1 -I {} date -d {} +%b | xargs mkdir

ctrl-alt-delor
  • 27,473
  • 9
  • 58
  • 102
1

The simplest and most robust way of doing this:

mkdir jan feb mar apr maj jun jul aug sep okt nov dec

It's a static list of months after all...

If you are intent on using GNU date (here assuming an unmodified $IFS and GNU date like for your date -d):

mkdir $( printf '%s\n' {1..12}/01 | date -f - +%b )
Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
Kusalananda
  • 320,670
  • 36
  • 633
  • 936