Wrapping Powershell script and files together? - powershell

I'm currently using PS2EXE to compile my powershell script into an executable, works very well indeed!
My problem is that this script relies on other files/folders. So instead of having these out with the exe I want to also have these files 'wrapped' up into the exe along with the PS script. Running the exe will run the PS script then extract these files/folders and move them out of the exe...
Is this even possible?
Thanks for your help

A Powershell script that requires external files can be self-sustained by embedding the data within. The usual way is to convert data into Base64 form and save it as strings within the Powershell script. At runtime, create new files by decoding the Base64 data.
# First, let's encode the external file as Base64. Do this once.
$Content = Get-Content -Path c:\some.file -Encoding Byte
$Base64 = [Convert]::ToBase64String($Content)
$Base64 | Out-File c:\encoded.txt
# Create a new variable into your script that contains the c:\encoded.txt contents like so,
$Base64 = "ABC..."
# Finally, decode the data and create a temp file with original contents. Delete the file on exit too.
$Content = [Convert]::FromBase64String($Base64)
Set-Content -Path $env:temp\some.file -Value $Content -Encoding Byte
The full sample code is avalable on a blog.

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

Convert PDF to CSV via Powershell

I am trying to take data from a webform that is generated in pdf format and migrate it to csv so I can use my limited powershell knowledge to automate VM builds. This is all done on linux as well. This is what I have so far and that seems to work, but I need to automate it all so I need to use wildcards as the file names will change.
pdftotext -nopgbrk './AndyTest-2 - Linux - Debian 10_13.pdf' test.txt
Import-Csv test.txt | Export-Csv test.csv
Cool - it sounds like you've already got 90++% of the solution.
Q: Why not just call your PS script with parameters?
EXAMPLE:
$PdfFile=$args[0]
$TxtFile=$args[1]
$CsvFile=$args[2]
pdftotext -nopgbrk $PdfFile $TxtFile
Import-Csv test.txt | Export-Csv $CsvFile
One possible "gotcha" is that apparently have spaces in your .Pdf filenames. One solution is to quote your variable, e.g.:
"$PdfFile"
Look here for more ideas:
https://social.technet.microsoft.com/Forums/en-US/3a527307-5bb1-40fa-94b3-9af0a3e181f3/
https://social.technet.microsoft.com/Forums/en-US/8e51b6f4-4adf-4253-8228-c410032209f7/
'Hope that helps!

Assistance with importing MXTSESSIONS file into MobaXterm

I'm using a tool called MobaXterm to open up SSH sessions to Linux Virtual Machines. I'm attempting to create an import file of hostnames from a script so I can dynamically create the list of VM's I want to connect to without adding them manually in the MobaXterm Gui. To that end I've created the following PowerShell script that reads in the Hostname and the IP address from a .csv file. The script is working in that an .mxtsessions file is being created and the file appears to be what is exported from my testing an export of sessions file from MobaXterm. Here is my working script:
$csvFilename = 'C:\mobaxterm\mobaXterm.csv'
$outfile = 'C:\mobaxterm\MobaXterm_Sessions.mxtsessions'
$csv = Import-Csv -Path $csvFilename -Delimiter ','
#'
[Bookmarks]
SubRep=
ImgNum=42
'# | Out-File -FilePath $outfile
$output = foreach ($line in $csv) {
"$($line.hostname)= #109#0%$($line.ip)%22%[loginuser]%%-1%-1%%%22%%0%0%0%%%-1%0%0%0%%1080%%0%0%1#MobaFont%10%0%0%0%15%236,236,236%0,0,0%180,180,192%0%-1%0%%xterm%-1%0%0,0,0%54,54,54%255,96,96%255,128,128%96,255,96%128,255,128%255,255,54%255,255,128%96,96,255%128,128,255%255,54,255%255,128,255%54,255,255%128,255,255%236,236,236%255,255,255%80%24%0%1%-1%<none>%%0#0#"
}
$output | Out-File -FilePath $outfile -Append
The import file is simply a .csv file of two columns where column one has the hostname and column 2 has the IP address of each hostname.
As I said my script appears to be working in that it's creating a file that appears to be valid...but when I try to import this .mxtsessions file into MobaXterm it won't load. No errors are shown. Perhaps there's a log I can view for why the import fails?
to further triage this issue I then manually added some machines to my MobaXterm manually and exported the file. I've compared the exported file to the file I've created with my PowerShell script. I'm not seeing any differences between both files. The properties on both files look identical (except for the name of course). The data within each file are identical from my compare.
Can anyone provide some pointers for me on why my generated .mxtsessions file won't load into MobaXterm? I've looked in the MobaXterm.log file and I'm not seeing any errors related to my import? Has anyone else created an import sessions file and successfully imported it into MobaXterm?
Any advice or pointers this forum can provide me would be greatly appreciated.
Thank you.
From just testing it, I think it's a character encoding issue. MobaXTerm import works if I save as ASCII or UTF8-sans-BOM, doesn't work otherwise.
If you only have ASCII characters, try adding an encoding parameter when writing:
'# | Out-File -FilePath $outfile -Encoding ASCII
$output | Out-File -FilePath $outfile -Append -Encoding ASCII
If you need Unicode, there's no way to write it without BOM from PowerShell 5.1 or earlier, so you'll need:
$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
[System.IO.File]::WriteAllLines($outfile, $allyourtextcontent, $Utf8NoBomEncoding)

How to do a copy /b in a powershell script to insert a BOM marker, but as a batch for files that match a filter and changes the ext on output?

REASONS WHY THIS IS NOT A DUPLICATE
Since 3 people have already voted to close, I guess I should explain why this question is not a duplicate:
I cannot use cat or >> as these mess up the encoding of the files, which are UTF8 on input and need to be UTF8-BOM on output.
The linked question does not show how to loop through all files that match a given pattern in a directory, and concatenate a single file to each of the matching files on output, plus give the new file a different extension.
Using Set-Content is not Powershell 6 future-proof, since Set-Content will NOT add a BOM marker. In Powershell 5 and below, it sometimes adds a BOM marker and sometimes not, depending on the configuration settings of the executing user. See 'quick note on encoding' at the end of this article.
So in conclusion I am looking for a solution that uses copy (hence the question title) and does NOT use Cat or Set-Content.
I need to loop through certain files in a given directory and run the following on each file:
copy /b BOMMarker.txt+InputFile.dat OutputFile.txt
This inserts the contents of the BOMMarker.txt file at the start of the InputFile.dat and writes the output to OutputFile.txt
I found this question which explains how I can loop through the folder to load each file into Powershell, but how do I apply the "copy /b" command so that I can get the BOM marker at the start of each file?
EDIT
The comment from Jeroen indicates I can just do Set-Content on the output file, as Powershell will automatically add the BOM at the start.
But I also need to change the extension. So the output filename needs to be the same as the input filename, just with a changed extension (from .dat to .txt) and including the BOM.
I am guessing I can use Path.ChangeExtension somehow to do this, but not sure how to combine that with also adding the BOM.
EDIT - for Bounty
The example answer I posted does not work in all environments I tested it, and I do not know why (possibly different default Powershell setttings) but also, it is not future proof since Powershell 6 will not output BOM by default.
From the given directory, I need to process all files that match the filter (DIL_BG_TXN*.dat).
For each of those files, I need to copy it with a BOM at the start but the resultant new file needs to be the same name but with the extension .txt instead of .dat.
This solutions uses streams, that reliably read and write as-is:
$bomStream = [IO.File]::OpenRead('BOMMarker.txt')
$location = "" # set this to the folder location
$items = Get-ChildItem -Path $location -Filter DIL_BG_TXN*.dat
foreach ($item in $items) {
$sourceStream = [IO.File]::OpenRead($item.FullName)
$targetStream = [IO.File]::OpenWrite([IO.Path]::ChangeExtension($item.FullName, '.txt'))
$bomStream.CopyTo($targetStream)
$sourceStream.CopyTo($targetStream)
$targetStream.Flush()
$targetStream.Close()
$sourceStream.Close()
$bomStream.Position = 0
}
$bomStream.Close()
Of course please write the absolute path of BOMMarker.txt (1st line) according to its location.
This finally worked:
$Location = "C:\Code\Bulgaria_Test"
$items = Get-ChildItem -Path $Location -Filter DIL_BG_TXN*.dat
ForEach ($item in $items) {
Write-Host "Processing file - " $item
cmd /c copy /b BOMMarker.txt+$item ($item.BaseName + '.txt')
}
Description:
Set the directory location where all the .dat files are.
Load only those files that match the filter into the array $items.
Loop through each $item in the array.
With each $item, call cmd shell with the copy /b command and concatenate the bom marker file with the $item file and write the result to the basename of $item plus the new extension.

Replace all in multiple CSV - Powershell

I'm attempting to replace the hostname from inside a CSV file with a blank space " ", but I am struggling to understand how this works, as when I attempt to call $env:computername it takes it as a string rather than the variable as per below:
$var = $env:computername
[io.file]::readalltext("C:\test\test.csv").replace("$var"," ") | Out-File c:\test\test-new.csv -Encoding ascii –Force
I have tested this and can see that the script is looking for the string $var in the CSV rather than the variable. Ultimately I would like to run this against a directory of CSV files that contain different hostnames in them and output the newly edited files to a separate location also.
I removed the quotes and tested this script and it worked for my test csv file
The quotes should be expanding the variable but if not you can simply pass the environmental variable directly like so:
[io.file]::readalltext("C:\test\test.csv").replace($env:ComputerName,"") | Out-File c:\test\test-new.csv -Encoding ascii –Force