11

What is the syntax to create multiple directories with PowerShells md (or mkdir, New-Item...) equivalent to the 'nix command mkdir ch{1..9} i.e.

~/parent_dir/  
ch1/  
ch2/  
ch3/  
ch4/  
ch5/  
ch6/  
ch7/  
ch8/  
ch9/  

I've looked in the man pages and get-help for examples, but I do not know the syntax for PowerShell to do such a simple thing. Thank you.

Uwe Keim
  • 2,062
  • 8
  • 31
  • 52
MmmHmm
  • 738
  • 11
  • 24

4 Answers4

23

You don't need to invoke mkdir multiple times, because New-Item can take an array of paths. For example:

mkdir $(1..9 | %{"ch$_"})

@DavidPostill has explained most of the concepts in his answer. This also takes advantage of string interpolation instead of performing an explicit concatenation. Additionally, the % shorthand is used instead of ForEach-Object, but has the same meaning.

Unfortunately, there does not appear to be an easy way to interpolate a string into an array of strings as in bash.

Bob
  • 60,938
  • 25
  • 191
  • 216
  • Oh. A much simpler solution. Should I delete my beginners effort? – DavidPostill Sep 25 '16 at 15:19
  • 2
    @DavidPostill You've explained in far more detail than I. This might be more of an addendum to your answer :P – Bob Sep 25 '16 at 15:20
  • 2
    @DavidPostill please don't delete it - your explanation is very useful – MmmHmm Sep 25 '16 at 15:22
  • Bob, excellent point about only invoking mkdir once with this syntax. – MmmHmm Sep 25 '16 at 15:24
  • 3
    Maximally golfed version: `md(0..9|%{"ch$_"})` – Ben N Sep 25 '16 at 15:24
  • @BenN s/golf/obfuscat/ :) I need a translator for that. Is there something that translates back and forth? – DavidPostill Sep 25 '16 at 15:27
  • 2
    @DavidPostill `md` is a standard alias for `mkdir`, which is a PowerShell function. `%`, as Bob mentioned, is a standard alias for `ForEach-Object`. Double-quoted strings interpolate variables, so `"ch$_"` is equivalent to `'ch' + $_`. You can look up an alias by running `Get-Command` (`gcm`) on it. – Ben N Sep 25 '16 at 15:33
  • Bob and @BenN , with this syntax, how to move corresponding ch#.files into ch#/ directories? This (and similar incantations) are failing: `mv $( 1..9 | %{ "ch$_*.*" } { "ch$_" } )` – MmmHmm Sep 25 '16 at 15:37
  • Ahh... got it: `mv $( 1..9 | %{ "ch$_*.* ch$_" } )` – MmmHmm Sep 25 '16 at 15:41
  • Good point! Any idea how to pipe git add and commit commands to these folders so each corresponding set has their update comments with corresponding #s? – MmmHmm Sep 25 '16 at 15:43
  • @Mr.Kennedy ...are you sure that works? I don't think move can take multiple destinations at a time, so you'll have to go with a pipeline as in David's answer for a move. Do keep in mind that "ch1*.*" will catch "ch11.txt" as well... – Bob Sep 25 '16 at 15:43
  • Bob, whoops - you are correct, sir! My bad! – MmmHmm Sep 25 '16 at 15:46
  • 1
    @Mr.Kennedy Something like: `1..9 | %{"ch$_"} | %{git add "$_"; git commit -m "$_"}` or `1..9 | %{$name = "ch$_"; git add "$name"; git commit -m "$name"}`. These comments are getting a bit long now, so if you have further questions - please ask them as questions, or maybe drop into chat. – Bob Sep 25 '16 at 15:47
17

What is the syntax to create multiple directories with PowerShell

Use the following command:

0..9 | foreach $_{ New-Item -ItemType directory -Name $("ch" + $_) }

How it works:

  • 0..9 the range operator .. generates the sequence of numbers 0, 1, ... 9
  • the numbers are pipelined | to the next command
  • foreach loops (through each number in turn)
  • { ... } is a script block
  • New-Item -ItemType directory -Name $("ch" + $_) creates the directories
  • $_ is an automatic variable that represents the current object in the pipeline (the number)

Example:

> 0..9 | foreach $_{ New-Item -ItemType directory -Name $("ch" + $_) }


    Directory: F:\test


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----       25/09/2016     14:57                ch0
d-----       25/09/2016     14:57                ch1
d-----       25/09/2016     14:57                ch2
d-----       25/09/2016     14:57                ch3
d-----       25/09/2016     14:57                ch4
d-----       25/09/2016     14:57                ch5
d-----       25/09/2016     14:57                ch6
d-----       25/09/2016     14:57                ch7
d-----       25/09/2016     14:57                ch8
d-----       25/09/2016     14:57                ch9
DavidPostill
  • 153,128
  • 77
  • 353
  • 394
  • 3
    There is nothing less verbose than entering the following: `1..9 | % $_{ md -name $("ch" + $_) }` ? – MmmHmm Sep 25 '16 at 14:14
  • 1
    I think so. But I'm no PowerShell expert. – DavidPostill Sep 25 '16 at 14:15
  • Ha - me neither! ...as a follow up to this, if the parent dir were full of files prepended with corresponding ch#s, would `1..9 | % $_{ mv ch$_.* ch$_ }` put all the ch#.ext files in the corresponding ch#/ directories? – MmmHmm Sep 25 '16 at 14:16
  • 1
    It looks OK, but I suggest you try it and see :) – DavidPostill Sep 25 '16 at 14:17
  • Oh, of course, I've been trying various incatations to no avail :\ – MmmHmm Sep 25 '16 at 14:18
  • 1
    a-HA - got it: `1..9 | % $_{ mv ch$_*.* ch$_ }` :^D – MmmHmm Sep 25 '16 at 14:22
  • YaY - `git add` worked: `1..9 | % $_{ git add $("ch" + $_) }` ...but this: `1..9 | % $_{ git commit -m $("ch" + $_) }` will take some thinking upon... Thanks again, IRL, you have a cup of coffee on me anytime! – MmmHmm Sep 25 '16 at 15:00
  • I've added some more explanation to the answer (how it works). – DavidPostill Sep 25 '16 at 15:10
  • Very useful explanation. In the continuing case of taking these new directories and moved files to a github repository, would this long piping add and commit files so each ch# dir and files have a corresponding ch# update comment? `1..9 | % $_{ git add $("ch" + $_) } | % $_{ git commit -m $("ch" + $_) }` – MmmHmm Sep 25 '16 at 15:13
  • I don't know. I'm still a PowerShell beginner. I had to look things up to answer your question ;) – DavidPostill Sep 25 '16 at 15:17
  • K - if i find out how, I'll post a solution, thx!!! – MmmHmm Sep 25 '16 at 15:25
  • 1
    @Mr.Kennedy Couple things. You should try running the `git add` and see what it returns - keep in mind that `$_` inside a foreach is only each individual array entry of the last pipeline item (which means the `commit` step in your example won't get raw numbers, so adding `ch` again is probably wrong). Also, in your example, the commits would only(?) run after all adds are done, which means the first commit would get everything... Alternatively, you could use multiple statements (separated by `;`) inside a single foreach step. – Bob Sep 25 '16 at 15:34
  • Thanks - your insights are very helpful and yes - everything got "ch1" as an update comment. – MmmHmm Sep 25 '16 at 15:48
  • 1
    I'll contribute a slightly more concise alternative: 1..9 | mkdir -Name { "ch$_" } – OldFart Sep 26 '16 at 18:35
2

Create multiple directories underneath the current directory:

mkdir ('abc','def','jkl') 

The above is a short-hand version of the following. Notice the at-sign included in front of the array of strings and the use of named parameters:

mkdir -Path @('abc','def','jkl')

And if you want to go all the way, the full native command would be:

New-Item -Path @('abc','def','jkl') -ItemType Directory

When using the PowerShell command-line, I use the short version.

When writing a script, especially one for others (who might be new to PowerShell), I tend to write the full native command.

Choose whatever works best for you.

user3785010
  • 121
  • 1
1

I would use for loop version as it is easy to remember and can be applied to many situations. Even it can be used for multiple command.

For an equivalent of this bash command:

for i in {1..9}; do
mkdir ch$i
done

...in PowerShell use:

for($i=1;
$i -le 10;
$i++)
{md ch$i}
MmmHmm
  • 738
  • 11
  • 24
Osman Mamun
  • 111
  • 4
  • mamum, could you clarify - I get an error when I run your code from the PowerShell command line, or, as a ps1 script: `Missing opening '(' after keyword 'for'.` This command: `foreach ($i in 1..9) {md ch$i}` gets me numbered "ch"apter directories, but I don't understand your "for...;do...done" loop. – MmmHmm Sep 26 '16 at 17:56
  • The for loop I used here is for bash shell (/bin/sh). I always change my shell to bash as I am used to this shell. For ps, you have to use different syntax for for loop (please see the attached link http://ss64.com/ps/for.html) – Osman Mamun Sep 26 '16 at 18:01
  • 1
    PowerShell: `for($i=1; $i -le 10; $i++){md ch$i}` thanks mamun, but I'm not yet "Bourne again..." ;) (nor do I know C++, but I think I sorta understand what "$i-le" (if less than or equal to?) and "$i++" (~"i+=1"?) mean and are doing in the command... – MmmHmm Sep 26 '16 at 18:16
  • 1
    Yes, the argument ca be read as for (initialize i from 1; as long as i less than or equal to 1; increase i by 1){do stuff iteratively} – Osman Mamun Sep 26 '16 at 18:19