title: Breaking out of a foreach loop
body: <p>Currently using the following code to search a directory and copy any .msi that is found to another directory. I don't want to copy a .msi if it is in use by another program. I saw other questions on StackExchange that showed other ways to check if a file was in use but using a try/catch block around a <code>Rename-Item</code> command seemed like a simpler solution. If the script can't rename the item I want it go to the next <code>$file</code> in <code>$listoffiles</code>. The only way that I could get it to work was by using the code listed below. However I can't seem to format the resulting error message the way I want and it seems like <code>-ErrorAction Stop</code> shouldn't be necessary. If an error occurs in the <code>try</code> block shouldn't the error just go to the <code>catch</code> block? Also, only 2 of the 3 variables in my error message are written out to the log file.</p>
  
  <p>Current code:</p>
  
  <pre><code>$listofFiles=(Get-ChildItem -Path $outputPath -Filter "*.msi" | where {$_.Name -notlike "*.Context.msi" -and $_.LastAccessTime -gt (Get-Date).AddMinutes(-15)})
  
  foreach ($file in $listofFiles){
      $y = ($file -split ("\\"))
      $msiFolder = $y[4]
  
      #Tests to see if $file currently in use
      try{
          Rename-Item -Path $file -NewName $file -ErrorVariable renameError -ErrorAction Stop 
      }
      catch{
          "$logTime ERROR Could not copy $msiFolder: $renameError.Exception.Message()" &gt;&gt; $copyLog
          continue
      }   
  
      #Code that copies files that are not in use.
  </code></pre>
  
  <p>Currently the error is displayed like so:</p>
  
  <pre><code>02/18/13 13:43:25 ERROR Could not copy  System.Management.Automation.ActionPreferenceStopException: Command execution stopped because the preference variable "ErrorActionPreference" or common parameter is set to Stop: The process cannot access the file because it is being used by another process.
     at System.Management.Automation.StatementListNode.ExecuteStatement(ParseTreeNode statement, Array input, Pipe outputPipe, ArrayList&amp; resultList, ExecutionContext context)
     at System.Management.Automation.StatementListNode.Execute(Array input, Pipe outputPipe, ArrayList&amp; resultList, ExecutionContext context)
     at System.Management.Automation.TryStatementNode.Execute(Array input, Pipe outputPipe, ArrayList&amp; resultList, ExecutionContext context).Exception.Message()
  </code></pre>
  
  <p>I would like for it to look like:</p>
  
  <pre><code>02/18/13 13:34:34 ERROR Could not copy ABC_CLIENT: The process cannot access the file because it is being used by another process.
  </code></pre>
  
  <p><strong>Questions</strong></p>
  
  <ol>
  <li>Why does $msiFolder not appear in the log message?</li>
  <li>How do I get simplify the error message?</li>
  <li>Is there a better way to use a try/catch block to break out of an iteration of the foreach loop?</li>
  </ol>
  
  <p><strong>Update 1:</strong></p>
  
  <p>I cleared <code>$error</code> and ran:</p>
  
  <pre><code>Rename-Item -Path $file -NewName $file -ErrorAction SilentlyContinue
  if (!$?){"$logTime ERROR Could not copy $file: $error[0].Exception.Message()" &gt;&gt; $copyLog}
  </code></pre>
  
  <p>The result was:</p>
  
  <pre><code>02/18/13 14:52:59 ERROR Could not copy  The process cannot access the file because it is being used by another process.[0].Exception.Message()
  </code></pre>
  
  <p>Why does <code>$logTime</code> get printed but the other variables do not?</p>
  
  <p><strong>Update 2:</strong>
  Final code that I used:</p>
  
  <pre><code>Rename-Item -Path $file -NewName $file -ErrorAction SilentlyContinue 
  if (!$?){
      "$logTime ERROR Could not copy ${msiFolder}: $($error[0].Exception.Message)" &gt;&gt; $copyLog
      continue
  }
  </code></pre>