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?

  • 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 lsed 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

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:]]#}
  • Nice! Did not know about locale abmon. Aug 10 '18 at 13:25
  • Nice answer, but tl;dr : IFS=';'; set -o noglob; mkdir -- $(locale abmon). ;) Aug 11 '18 at 11:46

Try a loop?

$ for m in {1..12}; do
> date -d "$m"/01 +%b
> done

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
  • Note that {1..12} is a bashism. For a POSIX shell, use for m in $(seq 1 12)... (though note that seq itself is not mandated by POSIX) Aug 10 '18 at 14:07
  • 2
    @RadovanGarabík, technically, {1..12} is a zshism, copied later by ksh93, bash and several other shells. Aug 10 '18 at 14:12

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.


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
  • 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. Aug 10 '18 at 12:19

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


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


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 )
  • 3
    Except that it doesn't make month name abbreviations in the language of the user. Aug 10 '18 at 13:11
  • @StéphaneChazelas Unless they are Swedish, like me :-)
    – Kusalananda
    Aug 10 '18 at 18:34

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.