null-valued expression error in simple powershell script - powershell

I have a small powershell script that looks in a patch for all files that contain a string and then replace that string.
Im sure this worked last week so im very confused why its now not working.
$filePath = "C:\my\file\path*"
# Get the files from the folder and iterate using Foreach
Get-ChildItem $filePath -Recurse | ForEach-Object {
# Read the file and use replace()
(Get-Content $_).Replace('oldString','NewString') | Set-Content $_
}
Im getting two errors i think, the first is:
Get-Content : Access to the path 'C:\my\file\path\YYY' is denied.
YYY is a folder in my path and im running the script as administrator, i was running as my own user who i confirmed has full access to this path.
The second is:
You cannot call a method on a null-valued expression.
Im guessing its the $_ but im really not sure. Ive tried replacing it with different name but no luck.

The You cannot call a method on a null-valued expression error is coming from calling the replace method when the result of Get-Content $_ is null.
This can happen for a few reasons:
First, inside of your ForEach-Object script block, $_ will essentially return the same value as $_.name which does not contain the full path to the folder or file being processed. Depending on the value of the working directory,$PWD, when you execute this code this could result in a bad file or folder path.
To address issues with the working directory try using $_.FullName. For debugging you can also write that value to the terminal to confirm where it is looking.
The issue could also be that the file has no contents. If the file is empty then the result of Get-Content will be $null.
Also attempting to get the content of a folder will result in an "access denied" error, after which the result of (get-content $_) will also be $null, resulting in the null error you received.
To summarize:
Double check your working directory.
Consider using $_.FullName.
Consider adding code to avoid calling Get-Content on folders.
Here is an example which I tested with several nested folders, some of which contained empty files and others which contained files with contents:
Get-ChildItem C:\testFolder -File -Recurse | ForEach-Object{
#this write host is just for debugging, to see what the file path is
Write-Host $_.FullName
$fileContents = Get-Content $_.FullName
if($fileContents -ne $null){
$fileContents.replace('oldString','newString') | Set-Content $_.FullName
}
}

Related

Trouble editing text files with powershell Get-Content and Set-Content

Goal: Update text entry on one line within many files distributed on a server
Summary: As part of an application migration between datacenters the .rdp files on end-user desktops need to be updated to point to the new IP address of their Remote Desktop Server. All the .rdp files reside on Windows servers in a redirected folders SMB share where I have Administrative access.
Powershell experience: minimal. Still trying to wrap my head around the way variables, output and piping work.
Was originally trying to make a single line of powershell code to complete this task but got stuck and had to make script file with the two lines of code below.
-Line 1: Search for all .rdp files in the folder structure and store the full path with file name in a variable. Every file will be checked since the users tend to accidentally change file names, eliminating absolute predictability.
-Line 2: I want to make one pass through all the files to replace only instances of two particular IP addresses with the new address. Then write the changes into the original file.
$Path = ls 'C:\Scripts\Replace-RDP\TESTFILES\' -Include *.rdp -Recurse -Force -ErrorAction SilentlyContinue | foreach fullname
$Path | (Get-Content -Path $Path) -Replace 'IPserver1','newIPserver1' -Replace 'IPserver2','newIPserver2' | Set-Content $Path -Force
Have found most of the solution with Powershell but have a problem with the results. The second line of code when output to the screen changes contents correctly in memory. The content written to file however resulted in the new server IP address being written into ALL rdp files even if the source rdp file's target IP address doesn't match the -Replace criterion.
Text inside a .rdp on the relevant line is:
full address:s:192.168.1.123
changes to:
full address:s:172.16.1.23
Thank you for all assistance in reaching the endpoint. Have spent hours learning from various sites and code snippets.
You need to keep track of each file that you are reading so that you can save changes to that file. Foreach-Object makes this process easy. Inside of the Foreach-Object script block, the current object $_ is the FullName value for each of your files.
$CurrentIP1 = '192\.168\.1\.123'
$CurrentIP2 = '192\.168\.1\.124'
$NewIP1 = '172.16.1.23'
$NewIP2 = '172.16.1.24'
$files = (Get-ChildItem 'C:\Scripts\Replace-RDP\TESTFILES\' -Filter *.rdp -Recurse -Force -File -ErrorAction SilentlyContinue).FullName
$files | Foreach-Object {
if (($contents = Get-Content $_) -match "$CurrentIP1|$CurrentIP2") {
$contents -replace $CurrentIP1,$NewIP1 -replace $CurrentIP2,$NewIP2 |
Set-Content $_
}
}
Note that using the -File switch on Get-ChildItem (alias ls) outputs only files. Since -replace uses regex to do matching, you must backslash escape literal . characters.

Powershell error copy-item cannot bind argument to parameter 'path' because it is null

I am rather new to Powershell and have a question regarding the error I'm receiving. After browsing through stack overflow I have found that users have made errors in spelling and the like and so I haven't found a suitable answer to my problem.
I have one script that runs a backup of some data and compresses it and stores it as:
yyyyMMddsometext.7z
I have another script to get the latest backup (if it was created) and copy it to another location
I am receiving an error
copy-item cannot bind argument to parameter 'path' because it is null
Does this mean that the file is non-existent or is it an error in any of the below?
$c = $textBox.Text
$a = (Get-Date).AddDays(-1).ToString($c)
$b = Get-ChildItem "C:\BackupsRP1" -Filter *.7z | Where-Object BaseName -like "*$a*"
Copy-Item $b -Destination "C:\Users\user\Desktop"
Above the code is a simple GUI for the user to input the date in the format yyyyMMdd and it will locate the file one less day than the user inputs and copy it to the location.
Thank you,
J
$b might contain multiple files or even non at all, depending what your filter finds.
The correct why to do it:
# This will copy each of the files that
Get-ChildItem "C:\BackupsRP1" -Filter *.7z | where BaseName -like "*$a*" | Copy-Item -Destination "C:\Users\user\Desktop" -PassThru
This will copy all items that match the filter and output the copied files to the console afterwards.
Also, make sure that $a really contains what you want. (I cannot know since I don't know what is in your textbox.)
You have to make sure the values in the variables are as expected, You can add logging for debugging this.
$c = $textBox.Text
$c > c:\temp\Debug.log
$a = (Get-Date).AddDays(-1).ToString($c)
$a >> c:\temp\Debug.log
$b = Get-ChildItem "C:\BackupsRP1" -Filter *.7z | Where-Object BaseName -like "*$a*"
$b >> c:\temp\Debug.log
Copy-Item $b.FullName -Destination "C:\Users\user\Desktop"
$b will contain FileInfo object, you have to select the fullname property(the full path of the file) from the object.

INI editing with PowerShell

My problem is I want to change paths in INI Files wich are saved in a folder and its subfolders.
The path of the folder is C:\New\Path\.
Example INI file:
notAIniText = C:\A\Path\notAIniText
maybeAIniText = C:\A\Path\maybeAIniText
AIniText = C:\A\Path\AIniText
I read some other questions about PSini but I don't want to just id because I want to use the script on multiple PC and I don't want to install every time PSIni.
I tried:
$mabyIni = "C:\New\Path"
$AiniFile = Get-ChildItem - Path "C:\New\Path\*" -Include *.ini -Recurse
foreach ($file in $AiniFile) {
Select-String -Path $file -AllMatches "C:\A\Path\" | ForEach-Opject {
$file -replace [regex]:Escape('C:\A\Path'), ('$mabyIni')
} | Set-Content $mabyIni -Include *.ini
But this doesn't work. I tried it with Get-Content too, but that also doesn't work.
Is there any way whitout PSini?
The code in your comment is close, but just has a few syntax issues. It starts out strong:
$mabyIni = "C:\New\Path"
$AiniFile = Get-ChildItem - Path "C:\New\Path*" -include *.ini -recurse
ForEach($file in $AiniFile) {
So far, so good. You define the new path, and you get a list of .ini files in the old path, then you start to loop through those files. This is all good code so far. Then things start to go astray.
I see that you are trying to get the contents of each .ini file, replace the string in question, and then output that file to the new path with this:
(Get-Content $AiniFile.PSPath) | ForEach-Object {
$file -replace [regex]:Escape('C:\A\Path'),('$mabyIni')
}| Set-Content $mabyIni -include *.ini
Unfortunately you're using the wrong variables, and adding in an extra ForEach loop in there as well. Let's start with the Get-Content line. At this point in the script you are looping through files, with each current file being represented by $file. So what you really want to get the contents of is $file, and not $AiniFile.PSPath.
(Get-Content $file)
Ok, that got us the contents of that file as an array of strings. Now, I'm guessing you weren't aware, but the -Replace operator works on arrays of strings. Perfect, we just so happen to have gotten an array of strings! Since the Get-Content command is wrapped in parenthesis it completes first, we can actually just tack on the -Replace command right after it.
(Get-Content $file) -replace [regex]:Escape('C:\A\Path'),$mabyIni
Your -replace command that you had was super close! In fact, I have to give you props for using [regex]::escape() in there. That's totally a pro move, well done! The only issue with it is the replacement string didn't need to be in parenthesis, and it was single quoted, so it would not have expanded the string and your .ini files would have all had a line like:
AIniText = $mabyIni\AIniText
Not exactly what you wanted I'm guessing, so I removed the parenthesis (they weren't hurting anything, but weren't helping either, so for cleanliness and simplicity I got rid of them), and I got rid of the single quotes ' as well since we really just want the string that's stored in that variable.
So now we're looping through files, reading the contents, replacing the old path with the new path, all that's left is to output the new .ini file. It looks like they're already in place, so we just use the existing path for the file, and set the content to the updated data.
(Get-Content $file) -replace [regex]:Escape('C:\A\Path'),$mabyIni | Set-Content -Path $File.FullName
Ok, done! You just have to close the ForEach loop, and run it.
$mabyIni = "C:\New\Path"
$AiniFile = Get-ChildItem - Path "C:\New\Path*" -include *.ini -recurse
ForEach($file in $AiniFile) {
(Get-Content $file) -replace [regex]:Escape('C:\A\Path'),$mabyIni | Set-Content -Path $File.FullName
}

Powershell Delete used Variables

I appear to have a problem deleting used variables with are locking files, before reusing the same path/file.
First I am declaring a path for my SQL process to write each line into a database:
$Files = get-childitem $Folder -Include *COMMON* -Recurse;
Next append the file then move it, but the appending the file name fails:
Get-ChildItem –Path $Folder where-object { $_.Name -like "*COMMON*" }| rename-item -newname {$DateTime + $_.Name}
Using something line:
$Files.delete()
Before attempting to access the path gives me
Exception calling "Delete" with "0" argument(s): "The process cannot access the file because it is being used by another process."
At line:1 char:1
+ $Files.Delete()
If I check the file, it is locked by Powershell...
Are there any solutions to this? I need to process the files in the folder then rename and move them all in one execution of my script... :(
$datetime is not defined in the code that you've posted.
If you've defined it elsewhere, using code such as $datetime = get-date, then the append operation will fail because $datetime is not a string. By executing $datetime.gettype() you would see it is a System.DateTime object. In PS two easy ways to convert an object into a string are 1) to interpolate it: "$datetime$($_.name)" or 2) use ToString(): $datetime.tostring() + $_.name

Getting access error in powershell find and replace

I am trying to run a find and replace on all of the files in a directory. I am running windows 7 Enterprise, and the folder that is being searched is on the local machine. Whenever I run the statement on just the 5 .SQL files in the folder, the script runs fine. When I add another folder inside the first(so now there are 5 .sql's and one folder with nothing in it yet) and try to run the same statement, I get an error that access denied to the new folder. I have tried everything with setting permissions on the folder, files and everything else I could think of that might keep it from accessing that child folder. Any advise would be greatly appreciated. I have even tried making sure the attributes were set correctly on the folder in windows.
This is the script I am using, which works when all the files are in the root of the folder, but anything in child folders throws an error.
(get-Content D:\project\*) | Foreach-Object {$_ -replace ";", ""} | Set-Content D:\project\*
Rather do something like this:
$folder = 'D:\project'
foreach ($f in (Get-ChildItem $folder -Recurse | ? {-not $_.PSIsContainer})) {
(Get-Content $f.FullName) -replace ';' | Set-Content $f.FullName
}
I wouldn't feel comfortable with a wildcard at both beginning and end of the pipeline, even if it would work (which it doesn't).
Was able to achieve what I was trying to do with this code. Thanks for your input all. This gives me the option to change all of the files with the SQL extension.
Get-ChildItem D:\Projects *.SQL -recurse |
Foreach-Object {
$c = ($_ | Get-Content)
$c = $c -replace ";", ""
[IO.File]::WriteAllText($_.FullName, ($c -join "`r`n"))
}