I have this bit of simple code where I am passed a variable for the username, I need to parse that and then use it in a path for a copy command.
I have used outputting the variables to a text file to help try to troubleshoot the problem.
I parse the variable, and it seems to output to the text file properly, but when I use it in my path variable it shows as empty.
The Code:
param ([String] $mdmUserName)
$mdmUserName | Out-File "C:\Windows\Temp\test.txt"
$FullUserSplit = $mdmUserName.Split("\")
$FullUserSplit | Out-File -append "C:\Windows\Temp\test.txt"
$localusername = $FullUserSplit[2]
$localusername | Out-File -Append "C:\Windows\Temp\test.txt"
$from = "C:\Windows\Temp\Normaltest.dotm"
$to = "C:\Users\$localusername\AppData\Roaming\Microsoft\Templates\"
$to | Out-File -Append "C:\Windows\Temp\test.txt"
Copy-Item $from $to -Force
The output of the test.txt file:
Win11\User
Win11
User
C:\Users\\AppData\Roaming\Microsoft\Templates\
You can see that it outputs the $localusername variable correctly to the test.txt, but then when added to the path it is not there. I feel like I am missing something simple.
I also tried manually setting the $mdmusername manually to "Win11/User" with the same result.
Arrays are zero based (they start with 0), thus: $FullUserSplit[1] or $FullUserSplit[-1] (which selects the last entry) – iRon
iRon's comment was the answer. I needed to use $FullUserSplit[1]
Outputting the $FullUserSplit to the troubleshooting file was throwing me off, as it outputted the full array on 2 lines.
Related
I recently started using Powershell and I'm trying out some code.
I have a .cfg file with several rules of code. The code is written like this:
ad.name=1
ad.virtual=active
ad.set=none
ad.partition=78
Now I want to export the value of ad.partition, which is 78, to a new file. I don't want to export ad.partition or = but only the number 78.
So far I got this:
Get-Content -Path C:\file.cfg | Where-Object {$_ -like 'ad.partition=78'}
But then I -obviously- just get the variable and the value. Not sure how to continue...
I hope someone has a way of achieving what I want.
After saving the value in a new file, would it be possible to add spaces? For example, the value consists out of 9 digits, e.g. 123456789. The desired output result would be 123 456 789.
Use ConvertFrom-StringData cmdlet to create a hash table from your file, then simply index the key you are after:
$h=(Get-Content -Path C:\file.cfg | ConvertFrom-StringData)
$h.("ad.partition") -replace ('^(\d{1,3})(\d{1,3})?(\d{1,3})?','$1 $2 $3') > C:\out.cfg
You can use the Select-String cmdlet to capture your desired value using a regex. Then just pipe the result to the Out-File cmdlet. To get your desired output with spaces, you can use a simple format string:
"{0:### ### ###}" -f [int](Select-string 'ad\.partition=(.*)' -Path C:\file.cfg).Matches.Groups[1].Value |
Out-File C:\result.cfg
I'm not able to find a reason why lines in emails are shortened when it is triggered by Task Scheduler(lines aren't shortened when the script is executed manualy from ISE!). I'd like to pass FullName to email and use it as link to document (when the path and file doesn't contain spaces, the link works great).
If I use "format-list" instead of "format-table" it looks better (even when triggered by Task Scheduler) and I have to add parameter "$body = $newdoc | Out-String -Width 255" to prevent breaking lines - but space in filenames still breaks links:
Next thing is the FullName contains spaces - I tried many ways (like $variable.replace; $var = $var -replace " ","` ", etc.)
#date and time formating
$culture = Get-Culture
$culture.DateTimeFormat.LongTimePattern = 'HH:mm'
$culture.DateTimeFormat.ShortDatePattern = 'dd-MM-yyyy'
Set-Culture $culture
#find files changed during last hour, sort descending
$newdoc = get-childitem -File -Path \\ottm09itoms01\OTA-IT_Operators\ -Recurse | ? {$_.LastWriteTime -gt (Get-Date).AddHours(-1)} | sort lastwritetime -Descending | Format-table -Property LastWriteTime, fullname
$body = $newdoc | Out-String
$enc = New-Object System.Text.utf8encoding
Send-MailMessage -From $sender -To $receiver2 -Subject "Documents updated" -body $body -Encoding $enc -SmtpServer $SMTPserver
After some digging, I suspect the problem is your use of Out-String which truncates output based on the -width parameter, which you have left unspecified. To quote the documentation:
-width
Specifies the number of characters in each line of output. Any additional characters are truncated, not wrapped. If you omit this parameter, the width is determined by the characteristics of the host program. The default value for the Windows PowerShell console is 80 (characters).
In other words, when you run this script in the ISE Out-String probably sets the width to whatever the buffer width of the ISE is, but when Task Scheduler runs it, it uses the default 80 character width.
So basically just add -width 120 (or the value of your choosing) to your Out-String and see if that fixes the problem.
To fix the links breaking on whitespace you might have to manually generate some HTML for them using -replace. Something like:
$body = $body -replace '(\\\\.*[^\s])','$1'
$body = $body.trim() -replace "`n","<br>`n"
This assumes that all your paths are UNC paths (i.e., paths starting with \\). You'd then need to throw the -BodyAsHtml on your Send-MailMessage command. This is kind of thrown together and I'm sure there's probably a better way of doing things, but it should work.
Is there any way to avoid passing parameters to a function, like "-Append $outfile" to Out-File, every time? I have a script which collects data from the system, something like:
... collect OS information ... | Out-File -Append $output
... collect local users ... | Out-File -Append $output
... collect logfile permissions ... | Out-File -Append $output
etc.
The last command in the pipe is most of the time Out-File -Append $output - can this be done more elegant? I had different ideas:
Create a wrapper function which passes the needed parameters to Out-File command - already tried, but I had problems to make it pipe-compatible
Write all output into a String-Variable and write the content at the end of all commands into the file - needs a lot of memory
Create something like an Output-Writer-Object which only receives once at initialization the necessary paramters - not tried yet
Thank you very much for your help!
You dont appear to be using a lot of arguments for this to be incredibly useful but a good suggestion would be to use splatting. I added some more parameters to illustrate how clean it can make code appear while still being functional.
$options = #{
Append = $True
FilePath = $output
Encoding = "Unicode"
Width = 400
}
Build a hastable of options and splat the cmdlet with them
... collect OS information ... | Out-File #options
... collect local users ... | Out-File #options
... collect logfile permissions ... | Out-File #options
Outside of that a wrapper function (of filter if it is easier) like you suggest would be another option. Look at the options in this answer. Specifically the filter
You want to use the $PSDefaultParameterValues preference variable. Something like this:
$PSDefaultParameterValues = #{
"Out-File:Encoding"="utf8";
"Out-File:Append"=$true;
"Out-File:FilePath"=$output
}
This feature is especially useful when you must specify the same alternate parameter value nearly every time you use the command or when a particular parameter value is difficult to remember, such as an email server name or project GUID.
Or put everything inside a function or scriptblock. Note that out-file defaults to utf16 encoding, and can mix encodings, as opposed to add-content.
& {
... collect OS information ...
... collect local users ...
... collect logfile permissions ...
} | add-content $output
I have a csv file that contains fields with values that begin with "$" that are representative of variables in different powershell scripts. I am attempting to import the csv and then replace the string version of the variable (ex. '$var1') with the actual variable in the script. I have been able to isolate the appropriate strings from the input but I'm having difficulty turning the corner on modifying the value.
Example:
CSV input file -
In Server,Out Server
\\$var1\in_Company1,\\$var2\in_Company1
\\$var1\in_Company2,\\$var2\in_Company2
Script (so far) -
$Import=import-csv "C:\temp\test1.csv"
$Var1="\\server1"
$Var2="\\server2"
$matchstring="(?=\$)(.*?)(?=\\)"
$Import| %{$_ | gm -MemberType NoteProperty |
%{[regex]::matches($Import.$($_.name),"$matchstring")[0].value}}
Any thoughts as to how to accomplish this?
The simplest way to address this that I could think of was with variable expansion as supposed to replacement. You have the variables set in the CSV so lets just expand them to their respective values in the script.
# If you dont have PowerShell 3.0 -raw will not work use this instead
# $Import=Get-Content $path | Out-String
$path = "C:\temp\test1.csv"
$Import = Get-Content $path -Raw
$Var1="\\server1"
$Var2="\\server2"
$ExecutionContext.InvokeCommand.ExpandString($Import) | Set-Content $path
This will net the following output in $path
In Server,Out Server
\\\\server1\in_Company1,\\\\server2\in_Company1
\\\\server1\in_Company2,\\\\server2\in_Company2
If the slashes are doubled up here and you do not want them to be then just change the respective variable values in your script of the data in the CSV.
Caveat
This has the potential to execute malicious code you did not mean too. Have a look at this thread for more on Expanding variables in file contents. If you are comfortable with the risks then this solution as presented should be fine.
Maybe I misunderstood, but do you need this?
$Import=import-csv "C:\temp\test1.cvs"
$Var1="\\server1"
$Var2="\\server2"
Foreach ($row in $Import )
{
$row.'In Server' = ($row.'In Server' -replace '\\\\\$var1', "$var1")
$row.'Out Server' = ($row.'Out Server' -replace '\\\\\$var2', "$var2")
}
$import | set-content $path
I've looked all around this site and can't quite seem to find anything that fits my situation. Basically, I am trying to write an addition to the NETLOGON file that will replace text in a text file on all of our users' desktops. The current text is static across the board.
The text I want it changed to will be unique to each user. I want to change the current text (user1) to the users AD username (i.e. johnd, janed, etc.). I am using Windows Server 2008 R2 and all the workstations are Windows 7 Professional SP1 64 bit.
Here's what I have tried so far (with a few variables, which none have worked for one reason or the other):
gc c:\Users\%USERNAME%\desktop\VPN.txt' -replace "user1",$env:username | out-file c:\Users\%USERNAME%\desktop\VPN.txt
I didn't get an error, but it also did not go back to the normal "PS C:>" prompt, just ">>>" and the file did not change as anticipated.
If that is how you have the code exactly then I suppose it is because you have an opening single quote without a closing one. You are still going to have two other problems and you have one answer in your code. The >>> is the line continuation characters because the parser knows that the code is not complete and giving you the option to continue with the code. If you were purposely coding a single line on multiple lines you would consider this a feature.
$path = "c:\Users\$($env:username)\desktop\VPN.txt"
(Get-Content $path) -replace "user1",$env:username | out-file $path
Closed the path in quotes and used a variable since you called the path twice.
%name% is used in command prompt. Environment variables in PowerShell use the $env: provider which you did you once in your snippet.
-replace is a regex replaced tool that can work against Get-Content but you need to capture the result in a sub expression first.
Secondly with -replace is for regex and your string is not regex based you could just use .Replace() as well.
Set-Content is generally preferred over Out-File for performance reasons.
All that being said...
you could also try something like this.
$path = "c:\Users\$($env:username)\desktop\VPN.txt"
(Get-Content $path).Replace("user1",$env:username) | Set-Content $path
Do you want to only replace the first occurrence?
You could use a little regex here with a tweak in how you get the use Get-Content
$path = "c:\Users\$($env:username)\desktop\VPN.txt"
(Get-Content $path | Out-String) -replace "(.*?)user1(.*)",('$1{0}$2' -f $env:username) | out-file $path
Regex will match the entire file. There are two groups which it captures.
(.*?) - Up until the first "user1"
(.*) - Everything after that
Then we use the format operator to sandwich the new username in between those capture groups.
Use:
(Get-Content $fileName) | % {
if ($_.ReadCount -eq 1) {
$_ -replace "$original", "$content"
}
else {
$_
}
} | Set-Content $fileName