Powershell Add-Content -Value with more than one parameter - powershell

Okay someone suggested that I start a new question because my original question was solved but I have another question which belongs to my problem from before.
My first problem was that I wanted to write text in a txt file using double quotes. That is solved. My next problem / question is how can I work with more than one parameter in Add-Content -Value?
Here is an example:
Add-Content -Value '"C:\Program Files (x86)\Fraunhofer IIS\easyDCP Creator+\bin\easyDCP Creator+.exe" "-i C:\Users\Administrator\Desktop\dcp_bearbeitet\$title\$title.txt" "-o" "C:\Users\Administrator\Desktop\output_dcp\$title"'
In this case parameter $title stands for the title of the video clip I am working on and I do not know the title when I am working on it, that is why I am using this parameter. But when I am running my script, power-shell totally ignores my parameter. So I tried it again with single quotes around the parameter for example:
... "-i C:\Users\Administrator\Desktop\dcp_bearbeitet\'$title'\'$title'.txt" ...
And then power-shell does not even perform my script. So maybe somebody knows how I can work with parameters in Add-Content -Value?

You can pass arrays into Add-Content.
$string_array = #(
"`n",
"line1",
"line2",
"line3",
'"line4"',
"li`"ne`"5"
)
$string_array | Add-Content file.ext
The parameter -Value can also take an array directly:
Add-Content -path cake.txt -value #('some "stuff"',"more `"backticked`" stuff","hello world")

You have two competing requirements:
you want to substitute a variable into the command you are building so you need to use double quotes
you want to include double quotes in the output, so you either have to escape them or use single quotes round the string.
One way is to use a here-string, i.e. #"..."# to delimit the string instead of just using ordinary quotes:
$value = #"
"C:\Program Files (x86)\Fraunhofer IIS\easyDCP Creator+\bin\easyDCP Creator+.exe" -i "C:\Users\Administrator\Desktop\dcp_bearbeitet\$title\$title.txt" -o "C:\Users\Administrator\Desktop\output_dcp\$title"
"#
Add-Content -Value $value
That should work for you. I separated it out from Add-Content not because it wouldn't work in-place, but because there is less scope for confusion to do one thing at a time. Also if you build the value up separately you can add a temporary Write-Host $value to quickly check you have the string exactly as you want it.
Another way is simply to escape the enclosed quotes:
$value = "`"C:\Program Files (x86)\Fraunhofer IIS\easyDCP Creator+\bin\easyDCP Creator+.exe`" -i `"C:\Users\Administrator\Desktop\dcp_bearbeitet\$title\$title.txt`" -o `"C:\Users\Administrator\Desktop\output_dcp\$title`""
Add-Content -Value $value
but that does get messy.
Other options would be to build up the string in small chunks, or to use a different character instead of the double quotes inside the string and then do a replace to turn them into the quotes, but either of these is messy. I would go for the "here-string" solution.

Related

Read value of variable in .ps1 and update the same variable in another .ps1

I'm trying to find an efficient way to read the value of a string variable in a PowerShell .ps1 file and then update the same variable/value in another .ps1 file. In my specific case, I would update a variable for the version # on script one and then I would want to run a script to update it on multiple other .ps1 files. For example:
1_script.ps1 - Script I want to read variable from
$global:scriptVersion = "v1.1"
2_script.ps1 - script I would want to update variable on (Should update to v1.1)
$global:scriptVersion = "v1.0"
I would want to update 2_script.ps1 to set the variable to "v1.1" as read from 1_script.ps1. My current method is using get-content with a regex to find a line starting with my variable, then doing a bunch of replaces to get the portion of the string I want. This does work, but it seems like there is probably a better way I am missing or didn't get working correctly in my tests.
My Modified Regex Solution Based on Answer by #mklement0 :
I slightly modified #mklement0 's solution because dot-sourcing the first script was causing it to run
$file1 = ".\1_script.ps1"
$file2 = ".\2_script.ps1"
$fileversion = (Get-Content $file1 | Where-Object {$_ -match '(?m)(?<=^\s*\$global:scriptVersion\s*=\s*")[^"]+'}).Split("=")[1].Trim().Replace('"','')
(Get-Content -Raw $file2) -replace '(?m)(?<=^\s*\$global:scriptVersion\s*=\s*")[^"]+',$fileversion | Set-Content $file2 -NoNewLine
Generally, the most robust way to parse PowerShell code is to use the language parser. However, reconstructing source code, with modifications after parsing, may situationally be hampered by the parser not reporting the details of intra-line whitespace - see this answer for an example and a discussion.[1]
Pragmatically speaking, using a regex-based -replace solution is probably good enough in your simple case (note that the value to update is assumed to be enclosed in "..." - but matching could be made more flexible to support '...' quoting too):
# Dot-source the first script in order to obtain the new value.
# Note: This invariably executes *all* top-level code in the script.
. .\1_script.ps1
# Outputs to the display.
# Append
# | Set-Content -Encoding utf8 2_script.ps1
# to save back to the input file.
(Get-Content -Raw 2_script.ps1) -replace '(?m)(?<=^\s*\$global:scriptVersion\s*=\s*")[^"]+', $global:scriptVersion
For an explanation of the regex and the ability to experiment with it, see this regex101.com page.
[1] Syntactic elements are reported in terms of line and column position, and columns are character-based, meaning that spaces and tabs are treated the same, so that a difference of, say, 3 character positions can represent 3 spaces, 3 tabs, or any mix of it - the parser won't tell you. However, if your approach allows keeping the source code as a whole while only removing and splicing in certain elements, that won't be a problem, as shown in iRon's helpful answer.
To compliment the helpful answer from #mklement0. In case your do go for the PowerShell abstract syntax tree (AST) class, you might use the Extent.StartOffset/Extent.EndOffset properties to reconstruct your script:
Using NameSpace System.Management.Automation.Language
$global:scriptVersion = 'v1.1' # . .\Script1.ps1
$Script2 = { # = Get-Content -Raw .\Script2.ps1
[CmdletBinding()]param()
begin {
$global:scriptVersion = "v1.0"
}
process {
$_
}
end {}
}.ToString()
$Ast = [Parser]::ParseInput($Script2, [ref]$null, [ref]$null)
$Extent = $Ast.Find(
{
$args[0] -is [AssignmentStatementAst] -and
$args[0].Left.VariablePath.UserPath -eq 'global:scriptVersion' -and
$args[0].Operator -eq 'Equals'
}, $true
).Right.Extent
-Join (
$Script2.SubString(0, $Extent.StartOffset),
$global:scriptVersion,
$Script2.SubString($Extent.EndOffset)
) # |Set-Content .\Script2.ps1

Powershell Script wont add $PSDefaultParameterValues to $profile

I'm writing a quick Powershell script to import modules and update some default parameters on various machines. I'm running into an issue where in my script when I add $PSDefaultParameterValues to the $profile it changes to System.Management.Automation.DefaultParameterDictionary which then throws an error of not being recognized as the name of a cmdlet.
Here is the code in my ps1 script
Add-Content -Path $PROFILE -Value "$PSDefaultParameterValues = #{}"
Here is what gets added to the profile
System.Management.Automation.DefaultParameterDictionary = #{}
I've tried everything from using Set-Content to using variables to avoid quotation confusion.
I appreciate the help!
Use single quotes
Add-Content -Path $PROFILE -Value '$PSDefaultParameterValues = #{}'
These are literal strings so any variables will not expand.
To add to tonypags' helpful answer:
Double-quoted PowerShell strings ("...") are expandable strings, i.e they perform string interpolation of embedded variable references (e.g. $PSDefaultParameterValues) and subexpressions (e.g. $(1 + 2))
Single-quoted strings ('...') are verbatim strings, i.e. their content is used as-is (verbatim, literally).
Thus, the automatic $PSDefaultParameterValues variable was expanded in your "..." string, which in essence means it was replaced with its .ToString() representation - which in this case is simply the type name of the variable's value.
If your entire string is meant to be used verbatim, use '...' quoting, as shown in tonypags' answer.
If you do need expansions (string interpolation), but need to selectively suppress them, escape $ characters as `$, using ` (a backtick), PowerShell's escape character, as shown in the following example:
$enc = 'utf8'
# Note the backtick (`) before $PSDefaultParameterValues.
# NOTE: It is only the *outer* quoting that determines
# whether expansion is performed.
Add-Content -Path $PROFILE `
-Value "`$PSDefaultParameterValues = #{ '*:Encoding' = '$enc' }"

POWERSHELL: Simplest way to save string variable to csv file

I know a little bit of Bash scripting, but I am very new to PowerShell. When I execute below code using bash, everything is fine. But, when I use PowerShell, each entry per echo is saved only in a single cell in Excel. Why is it like this? How can I accomplish my objective in the simplest way?
echo "1,2,3" > file.csv
echo "A,B,C" >> file.csv
UNDESIRED:
DESIRED:
I tried to Google it. But, in my understanding, they are converting the string type variables to something like PS Object and convert to CSV format. I tried it and it worked. But I had to force include a header.
New-Object -Type PSObject -Property #{
'X' = $A
'Y' = $B
'Z' = $C
} | Export-Csv 'C:\Temp\test.csv' -NoType
When I also opened the csv file using notepad, every word has double quotation marks (which I don't prefer to have)
You see, that is way more complicated compared to Linux Scripting. Can someone teach me the simplest way to do what I want? Thank you very much!
If in your system locale the ListSeparator character is NOT the comma, double-clicking a comma-delimited csv file will open Excel with all values in the same column.
I believe this is what happens here.
You can check by typing
[cultureinfo]::CurrentCulture.TextInfo.ListSeparator
in PowerShell
To have Excel 'understand' a CSV when you double-click it, add -UseCulture switch to the cmdlet:
Export-Csv 'C:\Temp\test.csv' -UseCulture -NoTypeInformation
As for the quotes around the values:
They are not always necessary, but sometimes essential, for instance if the value has leading or trailing space characters, or if the value contains the delimiter character itself.
Just leave them as-is, Excel knows how to handle that.
If you really can't resist on having a csv without quotes, please first have a look at the answers given here about that subject.
Edit
If you are absolutely sure all fields can do without quoting, you can do this:
$sep = [cultureinfo]::CurrentCulture.TextInfo.ListSeparator
"1,2,3", "A,B,C" -replace ',', $sep | Out-File -FilePath 'D:\Test\file.csv' -Encoding utf8

Replace the name of environment variable by it's actual value from environment in a text file

I am trying to automate Active Directory installation on Windows Server 2008 using windows powershell. I created a text file with .tmpl extension and added:
[DCINSTALL]
ReplicaOrNewDomain=_ReplicaOrNewDomain__
Then I created an answer file in a text format:
[DCINSTALL]
ReplicaOrNewDomain=$env:ReplicaOrNewDomain
Now I want to be able to write a script in PowerShell which will use the template file to get the value of variable ReplicaOrNewDomain from environment and replace $env:ReplicaOrNewDomain by that value in the text file so that I can use that answer file for AD installation.
You have a few options to do this. One is Environment.ExpandEnvironmentVariables. This uses a %variable% syntax (instead of $env:variable), so it would be simpler if you only want to substitute environment variables:
gc input.tmpl | foreach { [Environment]::ExpandEnvironmentVariables($_) } | sc out.ini
A more complete expansion of PowerShell expressions can be achieve via ExpandString. This is more useful if you want to insert actual PowerShell expressions into the template:
gc input.tmpl | foreach { $ExecutionContext.InvokeCommand.ExpandString($_) } | sc out.ini
A third option would be something like a customized templating scheme that uses Invoke-Expression, which I implemented here.
You can do that with a simple replacement like this:
$f = 'C:\path\to\your.txt'
(Get-Content $f -Raw) -replace '\$env:ReplicaOrNewDomain', $env:ReplicaOrNewDomain |
Set-Content $f
or like this:
$f = 'C:\path\to\your.txt'
(Get-Content $f -Raw).Replace('$env:ReplicaOrNewDomain', $env:ReplicaOrNewDomain) |
Set-Content $f
Note that when using the -replace operator you need to escape the $ (because otherwise it'd have the special meaning "end of string"). When using the Replace() method you just need to use single quotes to prevent expansion of the variable in the search string.
However, why the intermediate step of replacing the template parameter _ReplicaOrNewDomain__ with a different template parameter $env:ReplicaOrNewDomain? You would make your life easier if you just kept the former and replaced that with the value of the environment variable ReplicaOrNewDomain.
One thing that I like to do with my template files is something like this.
[DCINSTALL]
ReplicaOrNewDomain={0}
OtherVariable={1}
Then in my code I can use the format operator -f to make the changes.
$pathtofile = "C:\temp\test.txt"
(Get-Content $pathtofile -Raw) -f $env:ReplicaOrNewDomain, "FooBar" | Set-Content $pathtofile
It can help if you have multiple things that you need to update at once. Update your file with as many place holders as you need. You can use the same one multiple times if need be in the file.
[DCINSTALL]
ReplicaOrNewDomain={0}
SimilarVariable={0}
Caveat
If your actual file is supposed to contain curly braces you need to double them up to the are escaped.
You can use the ExpandString function, like this:
$ExecutionContext.InvokeCommand.ExpandString($TemplVal)
(assuming $TemplVal has the template string).

powershell add-content contains ""

I have a question about the syntax in powershell for Add-Content. My problem is, I want to add text into a textfile and that this text contains "" which is not working. For example:
Add-Content -Value "c:\Users\Administrator\Desktop\"foobar.exe""
Now the output should look like this:
c:\Users\Administrator\Desktop\"foobar.exe"
and that does not work because of these "".
Is there a way to get these "" in the -value parameter?
You can do it like this
Add-Content -Value "c:\Users\Administrator\Desktop\`"foobar.exe`"" -Path .\AA.TXT
escaping the inner ""
Or using single quote to enclose the double quote
Add-Content -Value 'c:\Users\Administrator\Desktop\"foobar.exe"' -Path .\AA.TXT
You can use a few different tricks:
Include the doubly quoted items within a singly quoted string. This makes PowerShell ignore variables, too. For instance: 'I Said "Something"'
Include the doubly quoted items with a preceding backtick. Backticks are the escape character in PowerShell, like \ in javascript or C, and this will make the " part of the string.
Use a here document . They start like this: #" and must end with a "# at the start of a line. This will still let you use variables within the quotes, and still let regular double quotes be.