Converting batch commands with Powershell that finds files and create parameters - powershell

In a cmd window I search for a specific file then pass it into an executable. I want to redo this using PowerShell, with similar constraints on being on a single line or two independent and separate lines, but being new I can't figure it out.
cmd /r dir /s /b RunnerUnitTest.dll | findstr /r bin\\ > tests_list.txt
cmd /r for /f %f in (tests_list.txt) do vstest.console.exe "%f"
The first command looks for a file, it finds two:
RunnerUnitTest\bin\Debug\RunnerUnitTest.dll
RunnerUnitTest\obj\Debug\RunnerUnitTest.dll
then it narrows it down to one (bin in the path) and sticks it into a file:
RunnerUnitTest\bin\Debug\RunnerUnitTest.dll
The second line takes this output and passes it into an executable as a parameter.
This is for a gitlab runner, btw

You can do something like this without an intermediate file, since Get-ChildItem will return a FileInfo object for files, and DirectoryInfo objects for directories (both of which are derived from FileSystemInfo):
Get-ChildItem -Recurse bin\Debug\RunnerUnitTest.dll | Select-Object -First 1 | Foreach-Object {
vstest.console.exe $_.FullName
}
What this will do is recursively search for all files found named RunnerUnitTest.dll with parent folders Debug and bin, and then for each file returned (I've added a Select-Object -First 1 to ensure we only get one assembly back but this is optional) pass the full path to the assembly to the vstest.console.exe program.
The way it's written above, the assembly under the obj folder won't be returned at all for execution consideration.
As an FYI, piping with | works much in the same way as it does with other shells like Bash or cmd, but it supports passing around complex objects instead of strings. As a general rule, you can pipe any output on the output stream to other cmdlets or save the output to variables. Redirection operators work similarly as well.

Related

Using PowerShell to pass all files of a folder as arguments to a command line program

I'm trying to create a file listing of a folder for a secure file transfer tool. This is what I do:
Get-ChildItem c:\files | % {$_.FullName} > c:\temp\list1.csv
$csv = Import-Csv C:\TEMP\list1.csv -Header Path
The output holds every file in a new line, but I need it in one line.
Required output
"C:\files\Alpha" "C:\files\Beta" "C:\files\Gamma" "C:\files\Delta"
Actual output
C:\files\Alpha
C:\files\Beta
C:\files\Gamma
C:\files\Delta
The csv file is just what came to my mind first. A variable containing the files formatted like mentioned above would be sufficient. Do you have an idea?
Edit: Thank you #Matthias R. Jessen and #WaitingForGuacamole, you gave me exactly what I wanted.
(Get-ChildItem C:\scripts -File).ForEach({'"{0}"' -f $_.FullName.Replace('"','\"')}) -join " "
However, somehow my tool (written in java) is interpreting the output as one file instead of multiple files in a line.
Below the error message:
Java : Error: The file 'C:\files\Alpha C:\files\Beta C:\files\Delta C:\files\Gamma' was not found and is excluded from the transfer.
I know, that I have to handover the paths differently when using a properties file instead of entering the command manually in PowerShell.
Is there a way on letting the output look like:
"C:\\files\Alpha" "C:\\files\Beta" "C:\\files\Gamma" "C:\\files\Delta"
To pass the file paths of all children of a specific folder to a command line program as separate arguments, just pass the results of
(Get-ChildItem -File).FullName
to the program. Example:
$files = (Get-ChildItem C:\MyFolder -File).FullName
# Expected: myprogram.exe -arg1 -arg2 C:\MyFolder\file1.txt C:\MyFolder\file2.txt ...
myprogram.exe -arg1 -arg2 $files

Powershell looping through files to pass to a program as a parameter

I have the following two cmd.exe commands, but I need to convert them to Powershell, and I've failed miserably trying to figure it out. Line 1 is finding a dll, but only when in a bin folder and line two then takes all the entries it finds and runs a command with it, e.g. bin\Debug\file, bin\Release\file
Can anyone help? The only limitation is this is inside a yaml runner file so I don't think I can split lines for each part, e.g. I don't think a Foreach-Object will work.
dir /s /b RunnerUnitTest.dll | findstr /r bin\\ > tests_list.txt
for /f %f in (tests_list.txt) do vstest.console.exe "%f"
I got as far as this
(gci -r RunnerUnitTest.dll).FullName | select-string bin
thanks.
Write a multi-line powershell script to do the work and then call that script from your yaml runner.
powershell -file "c:\myscripts\runtests.ps1" "c:\mydlls\RunnerUnitTest.dll" "c:\mytests\tests_list.txt"
A single command (pipeline), spread across 3 lines for readability, using built-in command aliases for brevity (but the parameter names are spelled out, for long-term robustness):
gci -Recurse -Filter RunnerUnitTest.dll |
? FullName -match bin\\ |
% { vstest.console.exe $_.FullName }
gci -Recurse -Filter RunnerUnitTest.dll finds all RunnerUnitTest.dll in the current directory's subtree; -Filter makes for faster matching than using the (positionally implied) -Path parameter.
? FullName -match bin\\ uses ? (Where-Object) to test the .FullName (full path) property values of the input file-info objects for matching regex bin\\, i.e. for a literal bin\ substring, and only passes matching file-info objects on.
% { vstest.console.exe $_.FullName } uses % (ForEach-Object) to invoke vstest.console.exe with each matching file's full path.
Note that no intermediate file with a list of DLLs to process is created, because it isn't necessary.
If you need to pass the above to an explicit invocation of the PowerShell CLI, you'd do:
powershell -noprofile -command "gci -Recurse -Filter RunnerUnitTest.dll | ? FullName -match bin\\ | % { vstest.console.exe $_.FullName }"
If you're using PowerShell [Core] 6+, substitute pwsh for powershell.

using "CON" as filename

I was copying a huge number of png and txt files using Copy-Item cmdlet, and sadly I discovered that a funny programmer decided to use "CON" as file name to recap connection information.
Given that "con" is a reserved word and Copy-Item returns:
Copy-Item : Cannot process path 'xxx\con.txt' because the target represents a reserved device name.
and given that this name can't be changed and it's used in every folder I need to copy,
Is there a way to copy all these "con.cfg" and "con.txt" files using Powershell?
I googled but I found only advice like "Don't use con!" or "Don't use Powershell to copy these files".
I haven't been able to find a solution for PowerShell yet, but you should be able to rename the files via command prompt using something like this:
ren \\.\<absolute path> <new name>
So for example:
ren \\.\C:\stuff\con.cfg stuff.cfg
You could invoke the command prompt through PowerShell, of course:
cmd /c "ren \\.\C:\stuff\con.cfg stuff.cfg"
And obviously you could use PowerShell variables in there if you wanted
$dir = "C:\stuff"
cmd /c "ren \\.\$dir\con.cfg stuff.cfg"
You could try referring to them using a wildcard: *on ?
Example:
ls | ? {$_.Name -match "*on.cfg"} | del
Regex example:
ls | ? {$_.Name -match "^\won\.cfg"} | del

PowerShell: read lines from text file, construct source and destination file names, then copy files

I'm a PowerShell novice, and I'd love to be able to script this. I have a text file where each line is part of a file name without the path or extension. I'd like a one-liner that loops through each line of the file (with a gc - Get-Content, right?), takes the content of the line, constructs the source path (the network drive and extension are static), constructs a destination path, and then copies each file. My file content is like this:
12345678
98765432
32145698
12365782
And my source folder is a UNC path like this:
\\server\share
And my destination folder is:
c:\temp\files
I would like to do the equivalent of this DOS command, using $_ as the text from each line of the file:
copy \\server\share\$_.ext c:\temp\files\$_.ext
I'm pretty sure I can use gc and $_ to access each line of the file, and that I need to use cp to copy the files, but I'm having trouble constructing the source and destination file names.
Try the following
gc theFileName |
%{ "{0}.ext" -f $_ } |
%{ copy "\\server\share\$_" "c:\temp\files\$_" }
It can actually be done on one line but it looks better formmated as multiple lines for this answer :)
Copy-Item can take a script block directly in this case so the Foreach-Object stages are unnecessary:
gc theFileName | cpi -l {"\\server\share\$_.exe"} c:\temp\files -whatif
Remove the -WhatIf parameter once you're satisfied it works. The -l is short for -LiteralPath which helps PowerShell determine which parameterset is in use. Also better to use literal path here so that wildcard characters don't get globbed (unless you want that - if so then use -path).
Essentially pipeline bound parameters can be specified via scriptblocks and PowerShell will attempt to resolve the result of the scriptblock to the type expected by the pipeline bound parameter.

Test-Path behavior in Powershell

I'm trying to write a script in powershell to batch convert video files.
The way I intend to use it is to go to some folder full of video files and run it. It uses a conversion program that can be run in "command-line mode" (named handbrake) and saves the converted files with "-android" appended to them before the file extension. For example, if I have a file named video1.avi in the folder, after running the script the folder has 2 files: video1.avi and video1-android.avi
The reason I want to do this this way is so that I can check if, for each video file, there is a converted version of it (with -android appended to the name). And if so, skip the conversion for that file.
And this is where I'm having touble. The problem is the Test-Path's behavior (the cmdlet I'm using to test if a file exists).
What happens is, if the video file has an "unusual" name (for example in my case it's video[long].avi) Test-Path always returns False if you try to check if that file exists.
An easy way for you to test this is for example to do this:
Go to an empty folder,
run notepad to create a file with "[" in its name:
&notepad test[x].txt
Save the file
then do this:
Get-ChildItem | ForEach-Object {Test-Path $_.FullName}
It does not return true! It should right? Well it doesn't if the file has "[" in its name (I didn't check for any other special characters)
I've realized that if you escape the "[" and "]" it works
Test-Path 'test`[x`].txt'
returns true.
How can I go around this issue? I want to be able to: given a BaseName of a file, append it "-android.avi" and check if a file with that name exists.
Thanks,
Rui
Many PowerShell cmdlets have Path parameters that support wildcarding. As you have observed, in PowerShell not only is * a wildcard but [ and ] are also considered wildcard characters. You can read more about this in the help topic about_Wildcards.
For your issue, if you don't need wildcarding then I would recommend using the -LiteralPath parameter. This parameter doesn't support wildcarding and accepts [ and ] as literal path characters e.g.:
Get-ChildItem | ForEach {Test-Path -LiteralPath `
"$([io.path]::ChangeExtension($_.FullName,'avi'))"}
FYI, the reason piping the output of Get-ChildItem directly into Test-Path works is because the LiteralPath parameter has an alias "PSPath" that maps to the PSPath property on the FileInfo object output by Get-ChildItem. That property gets bound to the LiteralPath (er PSPath) parameter "by property name".
dir | % {test-path "$($_.Name)-android.avi"}