If File Exists Just Change File Name - powershell-4.0

Am I missing the obvious here, or have I coded incorrectly? I simply want to when processing my syntax check if the file exists, if it does, save in the exact same location, but append the words "_RoundTwo" to the end of the second file. My syntax doesn't error, but the second file is never created. Can someone point out my err?
$SaveLocation = "C:\Completed\"
$WorkbookName = "Intro"
if ((Test-Path $SaveLocation\$WorkbookName + ".csv"))
{
[IO.Path]::GetFileNameWithoutExtension($WorkbookName) + "_RoundTwo" + [IO.Path]::GetExtension($WorkbookName)
}

[IO.Path]::GetFileNameWithoutExtension
That method will not create a file, it just returns a string containing the filename with its extension stripped off.
If you want to copy the file, then you need to copy, but there is a simpler way by making use of a pipeline without any objects does nothing:
dir $SaveLocation\$WorkbookName + ".csv" |
foreach-object {
$dest = $_.DirectoryName +
'\' +
[io.path]::GetFileNameWithoutExtension($_.FullName) +
$_.Extension
copy-item $_ $dest
}
If the dir does not match a file, then there is no object on the pipeline for foreach-object to process. Also the pipeline variable $_ contains lots of information to reuse (look at the results of dir afile | format-list *).

Related

Replacing numbered file names with different number without conflicting with other files in folder

I have hundreds of images that were automatically saved on different occasions using different a scheme, and I'm trying to make them consistent. I feel like this should be really simple in Powershell, but I'm not familiar with it.
Currently, I have a bunch of .txt and .tif files named U6Co_AsFab_(####)_raw for indexes from 0118 to 0141 as follows:
U6Co_AsFab_(0118)_raw.tif
U6Co_AsFab_(0118)_raw.txt
U6Co_AsFab_(0119)_raw.tif
U6Co_AsFab_(0119)_raw.txt
*
*
*
U6Co_AsFab_(0141 )_raw.tif
U6Co_AsFab_(0141 )_raw.txt
and I need them to be shifted to range from 0139 to 0162 as follows:
U6Co_AsFab_(0139)_raw.tif
U6Co_AsFab_(0139)_raw.txt
U6Co_AsFab_(0140)_raw.tif
U6Co_AsFab_(0140)_raw.txt
*
*
*
U6Co_AsFab_(0162)_raw.tif
U6Co_AsFab_(0162)_raw.txt
In the mix are U6Co_AsFab_(####) (without raw) that are indexed correctly.
I ran the following in Powershell:
Get-ChildItem U6Co_AsFab_* -Include *raw* |
Where { $_ -match 'U6Co_AsFab_(\d+)' } |
Foreach { $num = [int]$matches[1] + 21; Rename-Item $_ (("U6Co_AsFab_{0:0000}" + $_.Extension) -f $num) }
And the filenames conflicted with ones that had yet to be renamed, giving this error:
Rename-Item : Cannot create a file when that file already exists.
I was thinking I could add an If-Else argument that would only rename the file if it didn't already exist in the directory. Then it seems like I could re-execute that code until all the files were re-numbered.
What is the best way to do this? I have a similar issue in other folders as well.
The problem is basically, that you increase each index one by one, but the files with the higher indexes already exist. You need to reverse the order.
Try this:
$files = #(Get-ChildItem U6Co_AsFab_* -Include *raw*)
[Array]::Reverse($files)
# your remaining code should work fine
$files | where { $_ -match 'U6Co_AsFab_(\d+)' } |
foreach {
$num = [int]$matches[1] + 21
Rename-Item $_ (("U6Co_AsFab_{0:0000}" + $_.Extension) -f $num)
}

Windows Powershell script to find and replace a string after a particular string

I am currently working to convert AS3 class to JavaScript using Powershell script.
Below is the sample code needs to be converted.
package somePackageName
{
class someClassName
{
// other codes
}
}
I need the entire package block to be removed and "class someClassName{" should be converted to "function someClassName(){".
The "someClassName" can be any string.
And I need the output like this.
function someClassName()
{
}
This is what I tried.
$l1 = Get-Content $dest | Where-Object {$_ -like 'class'}
$arr = $l1 -split ' '
$n1 = "function "+ $arr[1] + "() " +$arr[2]
(Get-Content $dest) -creplace $l1, $n1 | Set-Content $dest
I can able to achieve what I intended if the opening brace is in same line as the package declaration line. As Powershell checks line by line, I am stuck if the opening brace present in next line.
Regex based solution
Depending on your willingness to post process this or accept leading spaces you could use this regex to remove the block outside of the class and replace with a function declaration. This is messier than it needs to be but safer since we cannot guess what // other codes is. You could just match the whole class block outright but if there are other curly braces in there it would muddy the regex.
PS M:\> (Get-Content -Raw $dest) -replace "(?sm).*?class (\w+)(.*)}",'function $1()$2'
function someClassName()
{
// other codes
}
See Regex101 for more detail on what the regex is doing.
Basically dump everything until the word class (first time). Then keep everything until the last closing brace
Note the leading space in the greater portion. This is honoring the existing space. To account for this we need to calculate the indentation. Simply removing all leading space would break existing indentation in the class/function.
So a solution like this might be preferred:
# Read in the file as a single string
$raw = (Get-Content -Raw $dest)
# Using the line that has the class declaration measure the number of spaces in front of it.
[void]($raw -match "(?m)^(\s+)class")
$leadingSpacesToRemove = $Matches[1].Length
# Remove the package block. Also remove a certain amount of leading space.
$raw -replace "(?sm).*?class (\w+)(.*)}",'function $1()$2' -replace "(?m)^\s{$leadingSpacesToRemove}"
Less regex
Seems filtering the lines with no leading spaces is an easy way to narrow down to what you want.
Get-Content $dest | Where-Object{$_.StartsWith(" ")}
From there we still need to replace the "class" and deal with the leading spaces. For those we are going to use similar solutions to what I showed above.
# Read in the file as a single string. Skipping the package wrapper since it has no leading spaces.
$classBlock = Get-Content $dest | Where-Object{$_.StartsWith(" ")}
# Get the class name and the number of leading spaces.
$classBlock[0] -match "^(\s+)class (\w+)" | Out-Null
$leadingSpacesToRemove = $matches[1].Length
$className = $matches[2]
# Output the new declaration and the trimmed block.
# Using an array to start so that piping output will be in one pipe
#("function $className()") + ($classBlock | Select -Skip 1) -replace "^\s{$leadingSpacesToRemove}"
Both solutions try to account for your exact specifications and account for the presence of weird stuff inside the class block.
I'd suggest using regex:
#class myclass -> function myclass()
#(Get-Content $dest) -creplace 'class\s(.+)', 'function $1()' |
Set-Content $dest
This will capture the class declaration and replace it with a backreference to the class name capture.

Powershell - Read a single text file and sort contents to multiple files based on text within the line

I'm looking for some direction on how to read a file line by line, then copy the line based on a search criteria to a newly created file. Since my description is probably poor, I've tried to illustrate below:
Single Text File Sample:
Name=N0060093G
Name=N0060093H
Name=N400205PW
Name=N400205PX
Name=N966O85Q0
Name=N966O85Q1
The script would read each line and use the "###" after "Name=N", to create a new file name after the identifier, "###" to copy each appropriate line to the new file. So, lines "Name=N0060093G"and "Name=N0060093H" would go to "006.txt"; "Name=N400205PW" and "Name=N400205PX" would write to "400.txt", etc.
A RegEx style approach:
$File = 'test.txt'
Get-Content $File | ForEach {
If ($_ -match '^Name\=N(?<filename>\d{3}).*') {
$_ | Out-File -Append "$($Matches.Filename).txt" -WhatIf
}
}

How to remove partial path from Get-Location output?

I'm trying to write a custom prompt for PowerShell and I was wondering how I would filter out the 1...n directories in the output of Get-Location.
function prompt {
"PS " + $(get-location) + "> "
}
So, if the path is too long I would like to omit some of the directories and just display PS...blah\blah> or something. I tried (get-container) - 1 but it doesn't work.
Use Split-Path with the -Leaf parameter if you want just the last element of a path:
function prompt {
"PS {0}> " -f (Split-Path -Leaf (Get-Location))
}
I wanted to make a more dynamic function. I do just basic string manipulation. You could do some logic nesting Split-Path but the string manipulation approach is just so much more terse. Since what you want to be returned wont be a fully validated path I feel better offering this solution.
Function Get-PartialPath($path, $depth){
If(Test-Path $path){
"PS {0}>" -f (($path -split "\\")[-$depth..-1] -join "\")
} else {
Write-Warning "$path is not a valid path"
}
}
Sample Function call
Get-PartialPath C:\temp\folder1\sfg 2
PS folder1\sfg>
So you can use this simple function. Pass is a string for the path. Assuming it is valid then it will carve up the path into as many trailing chunks as you want. We use -join to rebuild it. If you give a $depth number that is too high the whole path will be returned. So if you only wanted to have 3 folders being shown setting the $depth for 3.
Ansgar Wiechers' answer will give you the last directory but if you want a way to do multiple directories at the end of the filepath (using the triple dot notation) you can cast the directory path to a uri and then just get and join the segments:
function prompt {
$curPath = pwd
$pathUri = ([uri] $curPath.ToString())
if ($pathUri.Segments.Count -le 3) {
"PS {0}>" -f $curPath
} else {
"PS...{0}\{1}>" -f $pathUri.Segments[-2..-1].trim("/") -join ""
}
}
Or using just a string (no uri cast)
function prompt {
$curPath = pwd
$pathString = $curPath.Tostring().split('\') #Changed; no reason for escaping
if ($pathString.Count -le 3) {
"PS {0}>" -f $curPath
} else {
"PS...{0}\{1}>" -f $pathString[-2..-1] -join ""
}
}
$a = prompt
Write-Host $a
Then just change -2 to whatever you want to be the first directory and -le 3 to match. I typically use the uri cast when I have to run stuff through a browser or over connections to Linux machines (as it uses "/" as a path separator) but there is no reason to not use the string method for normal operations.

Locating a file on the path

Does anybody know how to determine the location of a file that's in one of the folders specified by the PATH environmental variable other than doing a dir filename.exe /s from the root folder?
I know this is stretching the bounds of a programming question but this is useful for deployment-related issues, also I need to examine the dependencies of an executable. :-)
You can use the where.exe utility in the C:\Windows\System32 directory.
For WindowsNT-based systems:
for %i in (file) do #echo %~dp$PATH:i
Replace file with the name of the file you're looking for.
If you want to locate the file at the API level, you can use PathFindOnPath. It has the added bonus of being able to specify additional directories, in case you want to search in additional locations apart from just the system or current user path.
On windows i'd say use %WINDIR%\system32\where.exe
Your questions title doesn't specify windows so I imagine some folks might find this question looking for the same with a posix OS on their mind (like myself).
This php snippet might help them:
<?php
function Find( $file )
{
foreach( explode( ':', $_ENV( 'PATH' ) ) as $dir )
{
$command = sprintf( 'find -L %s -name "%s" -print', $dir, $file );
$output = array();
$result = -1;
exec( $command, $output, $result );
if ( count( $output ) == 1 )
{
return( $output[ 0 ] );
}
}
return null;
}
?>
This is slightly altered production code I'm running on several servers. (i.e. taken out of OO context and left some sanitation and error checking out for brevity.)
Using PowerShell on Windows...
Function Get-ENVPathFolders {
#.Synopsis Split $env:Path into an array
#.Notes
# - Handle 1) folders ending in a backslash 2) double-quoted folders 3) folders with semicolons 4) folders with spaces 5) double-semicolons i.e. blanks
# - Example path: 'C:\WINDOWS\;"C:\Path with semicolon; in the middle";"E:\Path with semicolon at the end;";;C:\Program Files;
# - 2018/01/30 by Chad#ChadsTech.net - Created
$NewPath = #()
$env:Path.ToString().TrimEnd(';') -split '(?=["])' | ForEach-Object { #remove a trailing semicolon from the path then split it into an array using a double-quote as the delimeter keeping the delimeter
If ($_ -eq '";') { # throw away a blank line
} ElseIf ($_.ToString().StartsWith('";')) { # if line starts with "; remove the "; and any trailing backslash
$NewPath += ($_.ToString().TrimStart('";')).TrimEnd('\')
} ElseIf ($_.ToString().StartsWith('"')) { # if line starts with " remove the " and any trailing backslash
$NewPath += ($_.ToString().TrimStart('"')).TrimEnd('\') #$_ + '"'
} Else { # split by semicolon and remove any trailing backslash
$_.ToString().Split(';') | ForEach-Object { If ($_.Length -gt 0) { $NewPath += $_.TrimEnd('\') } }
}
}
Return $NewPath
}
$myFile = 'desktop.ini'
Get-ENVPathFolders | ForEach-Object { If (Test-Path -Path $_\$myFile) { Write-Output "Found [$_\$myFile]" } }
I also blogged the answer with some details over at http://blogs.catapultsystems.com/chsimmons/archive/2018/01/30/parse-envpath-with-powershell
In addition to the 'which' (MS Windows) and 'where' (unix/linux) utilities, I have written my own utility which I call 'findinpath'. In addition to finding the executable that would be executed, if handed to the command line interpreter (CLI), it will find all matches, returned path-search-order so you can find path-order problems. In addition, my utility returns not just executables, but any file-specification match, to catch those times when a desired file isn't actually executable.
I also added a feature that has turned out to be very nifty; the -s flag tells it to search not just the system path, but everything on the system disk, known user-directories excluded. I have found this feature to be incredibly useful in systems administration tasks...
Here's the 'usage' output:
usage: findinpath [ -p <path> | -path <path> ] | [ -s | -system ] <file>
or findinpath [ -h | -help ]
where: <file> may be any file spec, including wild cards
-h or -help returns this text
-p or -path uses the specified path instead of the PATH environment variable.
-s or -system searches the system disk, skipping /d /l/ /nfs and /users
Writing such a utility is not hard and I'll leave it as an exercise for the reader. Or, if asked here, I'll post my script - its in 'bash'.
just for kicks, here's a one-liner powershell implementation
function PSwhere($file) { $env:Path.Split(";") | ? { test-path $_\$file* } }

Resources