Iteration over inline-initialized array? - powershell

I'm new to PowerShell, and have stumbled across some behavior I can't explain while trying to do something fairly straightforward. I have a few variables containing paths within the file system, and I want to make sure that they all have trailing slashes (and add them if they're missing).
# append trailing slash if not present
$engineXCopyPath, $engineXBackupPath, $enlistmentBuildTargetPath | ForEach-Object
{
Write-Host "hi" #where I would check the last character and add a \ if it weren't
}
When running my script, this code keeps prompting for Process[_n_] until I give it no input, in which case it prints the entire contents of the line rather than executing it.
As far as I know, it should be iterating over the three items fed to it, printing "hi" for each one. I'm not sure why it's prompting for any input (not to mention why it stops when I give it blank input), nor do I know why it's printing "Write-Host "hi" #where I would check the last character and add a \ if it weren't" instead of just "hi".
Thanks for your help!

You need to include the opening brace on the same line as ForEach-Object:
$engineXCopyPath, $engineXBackupPath, $enlistmentBuildTargetPath | ForEach-Object {
Write-Host "hi" #where I would check the last character and add a \ if it weren't
}
Otherwise, PowerShell is prompting you for the Process input scriptblocks that are required for ForEach-Object. Then it is interpreting the braces as the creation of a ScriptBlock and printing the contents.

Related

How to get a list of contents in a directory, then wrap text around the list?

My aim is really simple: I want to list hundreds of ebooks on my site. Just words that you click on and it opens the pdf in a new tab. My issue lays in that doing this manually would take days for a single folder. In powershell, I've managed to find a way to list all contents in a directory and save them to a file.
cd "directory\to\file"
ls $search_dir > text.txt
This prints the contents and saves them in a text file. What I want to do is wrap text around it. In HTML it's obvious you need to make your tags like
<p>file</p>
This is how I want my printed list to look. So how do I get the list of contents to get wrapped around like this (and also printed again between the tags without the ".pdf" extention)?
Here's half the puzzle, solving the other half is more rewarding than being simply told the answer.
This will read the text file in ( ) and add the string Added to the end of every line item.
Thanks goes out to https://stackoverflow.com/a/4952679/4317867 #mjolinor
(gc C:\Temp\z.txt) | ForEach-Object { $_ + " Added" } | Set-Content C:\Temp\z.txt
In powershell, type in
cd "directory/to/file"
Then type in
ls $search_dir
ALT+left click to select a block of the 'name' section. Copy it to a text file and name it something like filename.txt. Then use the following line to apply the wrapping:
(gc C:\location\to\filename.txt) | ForEach-Object { "<p><a href=$([char]34)ebooks/Books/path/to/file/$_$([char]34) target=$([char]34)_blank$([char]34) class=$([char]34)downloadlink2$([char]34)>$_</a></p>" } | Set-Content C:\location\to\filename.txt
gc grabs the file in the directory next to it. ForEach-Object tells powershell to run what's in the curly brackets for each line in the document. $([char]34) is quotation marks inside the string and $_ is a line in the filename.txt file. Set-Content tells powershell to apply the changes made to the .txt file. When it's done the thing will look something like:
<p>file.pdf</p>
Hope this helps someone in the future.

Add quotes to each column in a CSV via Powershell

I am trying to create a Powershell script which wraps quotes around each columns of the file on export to CSV. However the Export-CSV applet only places these where they are needed, i.e. where the text has a space or similar within it.
I have tried to use the following to wrap the quotes on each line but it ends up wrapping three quotes on each column.
$r.SURNAME = '"'+$r.SURNAME+'"';
Is anyone able to share how to forces these on each column of the file - so far I can just find info on stripping these out.
Thanks
Perhaps a better approach would be to simply convert to CSV (not export) and then a simple regex expression could add the quotes then pipe it out to file.
Assuming you are exporting the whole object $r:
$r | ConvertTo-Csv -NoTypeInformation `
| % { $_ -replace ',(.*?),',',"$1",' } `
| Select -Skip 1 | Set-Content C:\temp\file.csv
The Select -Skip 1 removes the header. If you want the header just take it out.
To clarify what the regex expression is doing:
Match: ,(.*?),
Explanation: This will match section of each line that has a comma followed by any number of characters (.*) without being greedy (? : basically means it will only match the minimum number of characters that is needed to complete the match) and the finally is ended with a comma. The parenthesis will hold everything between the two commas in a match variable to be used later in the replace.
Replace: ,"$1",
Explanation: The $1 holds the match between the two parenthesis mention above in the match. I am surrounding it with quotes and re-adding the commas since I matched on those as well they must be replaced or they are simply consumed. Please note, that while the match portion of the -replace can have double quotes without an issue, the replace section must be surrounded in single quotes or the $1 gets interpreted by PowerShell as a PowerShell variable and not a match variable.
You can also use the following code:
$r.SURNAME = "`"$($r.SURNAME)`""
I have cheated to get what I want by re-parsing the file through the following - guess that it acts as a simple find and replace on the file.
get-content C:\Data\Downloads\file2.csv
| foreach-object { $_ -replace '"""' ,'"'}
| set-content C:\Data\Downloads\file3.csv
Thanks for the help on this.

How use write to stdout without a newline?

When using the Write-Output command, a new line is automatically appended. How can I write strings to stdout (the standard output) without a newline?
For example:
powershell -command "write-output A; write-output B"
Outputs:
A
B
(Write-Host is no good - it writes data to the console itself, not to the stdout stream)
Write-Output writes objects down the pipeline, not text as in *nix for example. It doesn't do any kind of text formatting such as appending newlines, hence no need for newline handling options. I see people very often not coming to grips with this.
If you are referring to the newlines printed on the console output, it's because the pipeline is always eventually terminated by Out-Default, which forwards to a default output target (usually Out-Host), which in turn, if it doesn't receive a formatted input, runs the objects through an appropriate default formatter (usually Format-List or Format-Table). The formatter here is the only one in the process responsible for formatting the output, e.g. printing each object on a new line for the console output.
You can override this default behavior by specifying the formatter of your liking at the end of the pipeline, including your own using Format-Custom.
Write-Output is not appending the newlines.
Try this:
filter intchar {[int[]][char[]]$_}
'123' | Write-Output | intchar
49
50
51
The filter is converting the string to the ASCII int representation of each character. There is no newline being appended.
Adding a couple of explicit newlines for comparison:
"1`n2`n3" | write-output | intchar
49
10
50
10
51
Now we see the additional newlines between the characters, but still no newline appended to the string.
Not sure what your application is, but if you're getting unwanted newlines in your output, I don't think it's Write-Output that's doing it.
mjolinor/famousgarkin explain why the output has a new line that is not itself generated by Write-Output. Simple approach to deal with this is to build your output string with Write-Output
$text = ("This","is","some","words") -join " ";
$string = Write-Output $text
$string += Write-Output $text
$string
Output
This is some wordsThis is some words

Powershell -match operator issue

I've written a script that compares two strings, both of which are path names. The first is the full path to a file and the second is a higher level path. I've replaced spaces and backslashes with underscores.
The variables are assigned correctly such as
$full = "__server_department_project_file.txt"
$part = "__server_department_"
The script uses
$testformatch = $full -match $part
In one of my environments this works perfectly (returning TRUE when appropriate). In another completely separate environment this fails constantly (always returns FALSE).
In the failing domain, when I type these things out manually it returns TRUE as expected, but the script always returns FALSE. I've added a testing line that displays the comparisons, copied those results dumped to the screen and manually cut and pasted them into variables in ps command line directly - and those return TRUE.
I'm at a complete loss for what might be causing this. Are there special characters or rules about -match that may be coming into play? Any ideas would be greatly appreciated. Thanks!

handling a CSV with line feed characters in a column in powershell

Currently, I have a system which creates a delimited file like the one below in which I've mocked up the extra line feeds which are within the columns sporadically.
Column1,Column2,Column3,Column4
Text1,Text2[LF],text3[LF],text4[CR][LF]
Text1,Text2[LF][LF],text3,text4[CR][LF]
Text1,Text2,text3[LF][LF],text4[CR][LF]
Text1,Text2,text3[LF],text4[LF][LF][CR][LF]
I've been able to remove the line feeds causing me concern by using Notepad++ using the following REGEX to ignore the valid carriage return/Line feed combinations:
(?<![\r])[\n]
I am unable however to find a solution using powershell, because I think when I get-content for the csv file the line feeds within the text fields are ignored and the value is stored as a separate object in the variable assigned to the get-content action. My question is how can I apply the regex to the csv file using replace if the cmdlet ignores the line feeds when loading the data?
I've also tried the following method below to load the content of my csv which doesn't work either as it just results in one long string, which would be similar to using -join(get-content).
[STRING]$test = [io.file]::ReadAllLines('C:\CONV\DataOutput.csv')
$test.replace("(?<![\r])[\n]","")
$test | out-file .\DataOutput_2.csv
Nearly there, may I suggest just 3 changes:
use ReadAllText(…) instead of ReadAllLines(…)
use -replace … instead of .Replace(…), only then will the first argument be treated as a regex
do something with the replacement result (e.g. assign it back to $test)
Sample code:
[STRING]$test = [io.file]::ReadAllText('C:\CONV\DataOutput.csv')
$test = $test -replace '(?<![\r])[\n]',''
$test | out-file .\DataOutput_2.csv