How to use Powershell output in a pipeline - powershell

My purpose is to write some string into a file, and the file path is something like %SystemDrive%\temp.txt. The pipeline looks like
Write-Output "test" | Out-File -FilePath %SystemDrive%\temp.txt
I can get %SystemDrive% by (Get-ChildItem -Path Env:\SystemDrive).Value, but how can I put it into the pipeline?

You could use Join-Path wrapped in parenthesis to set the FilePath for Out-File.
Write-Output "test" | Out-File -FilePath (Join-Path -Path (Get-ChildItem -Path Env:\SystemDrive).Value -ChildPath "\temp.txt")
The path that this yields on my system is:
C:\temp.txt
You can add the -WhatIf switch to the end of the command to see that it works, without actually writing the file.

I'd opt for creating a temp folder on C then output the txt file to that folder.
Get-"something"| Out-File -FilePath C:\temp\temp.txt

Related

Is it possible to "-passthru" to Log file only and not the console when using "Start-Transcript -Path "$LogOutput"

I have a Powershell script where I want the console to output my custom messages only, but I would like to capture what the command is doing in a log only. Is this possible?
Just to make it clearer with an example below. I want to copy all the Sub Directories and Files as they are in their own directory to a new location:
Start-Transcript -Path "$LogOutput" -IncludeInvocationHeader -Append
Get-ChildItem -Path "$Source" | Copy-Item -Destination "$Destination" -Exclude "File*.xml" -Force -Recurse -PassThru
Stop-Transcript
I then use $? to get the success of the result on the console for my custom messages.
The above shows details I find helpful but it outputs the -PassThru on the console which I do not want to display there. If I don't specify it, it outputs nothing in the Transcript log either.
If I try appending to the copy command ...-PassThru | Out-File -FilePath "$LogOutput" -Append (The same Log used for Transcript) it fails as the log file is locked by the Transcript. Is there a way to make this possible, or will I have to append ...-PassThru | Out-File -FilePath "$LogOutput" -Append to all my commands individually?
Many thanks in advance.

Powershell Get -ChildItem: filtering csv files and -Recurse not working

I created a short powershell script to convert csv files from Unicode to UTF-8 encoding. My script outputs new files with the the original file name preceded by UTF8. I'm running into two issues:
I'm trying to only run the powershell script on csv files. Currently the script runs on every file in the directory, including the powershell script (it outputs a new file called UTF8pshell_script if the powershell script was called pshell_script for example). The other methods where I've tried to only run the script on csv files just end up making the script not do anything.
I'm trying to run the script on sub-directories. The first issue is that output files created from csv files in subdirectories have no content inside them whatsoever. If the script is ran in the same directory as the csv file this problem does not arise. This is not crucial but I am also uncertain how to get output files created from those in subdirectories to be outputted in the same subdirectories (currently they are outputted in the main directory where the powershell script is).
as
Get-Content -Encoding Unicode $_ | Out-File -Encoding UTF8
Get-ChildItem -Recurse | ForEach-Object {Get-Content -Encoding Unicode $_ | Out-File -Encoding UTF8 "UTF8$_"}
The desired output is the powershell script running on only csv files, and outputting files to the same subdirectories where the files they were created form are.
Get-ChildItem takes a -Filter parameter, which for files is the simple wildcard pattern. This will allow you to restrict your cmdlet to CSV files only:
Get-ChildItem -Filter *.csv
To process subdirectories, you may also use the -Recurse switch
Get-ChildItem -Filter *.csv -Recurse
Now, I'm never quite sure how $_ changes as you pass different objects through the pipe, so I'm probably not doing the next steps the most efficient way - but it will be clear what I'm trying to do:
Each file object that we find needs to be processed as follows:
Dissect it into a path and a filename: $filepath = $_.PSParentPath; $filename = $_.PSChildName
Load up the CSV: Import-CSV -Path $_
Output the new CSV with the proper encoding: Export-CSV -Path ("{0}\UTF8{1}" -f $filepath,$filename) -Encoding UTF8
So, we put it all together:
Get-ChildItem -Filter *.csv -Recurse -exclude UTF8* | ForEach-Object {
$filepath = $_.PSParentPath
$filename = $_.PSChildName
Import-CSV -Path $_ |
Export-CSV -Encoding UTF8 -Path ("{0}\UTF8{1}" -f $filepath,$filename) -NoTypeInformation
}
The -Exclude UTF8* in the Get-ChildItem ensures that when you create a file, it doesn't get picked up later and re-processed. The -NoTypeInformation on the Export-CSV compensates for a stupidity built in to the cmdlet that causes an extra line with a meaningless object type name at the beginning of the file.
Depending on the original encoding (and presence of a BOM) you might have to specify an encoding also on the input side.
ForEach($Csv in (Get-ChildItem -Filter *.csv -Recurse -Exclude UTF8*)){
(Get-Content $Csv.FullName -raw) |
Set-Content -Path {Join-Path $Csv.Directory ("UTF8"+$Csv.Name)} -Encoding UTF8
}
LotPings beat me to this by 10 minutes with a virtually identical answer, but I'm leaving this for the 'passing an empty file to the pipeline' bit that I have. I also realize after the fact that you don't need a pipeline variable for that same reason, as you only need it if you pass things through the pipeline within the loop.
If all you want to do is change the encoding I would use a ForEach($x in $y){} loop, or a ForEach-Object{} loop with a PipelineVariable on the Get-ChildItem. I'll show that since I think pipeline variables are under used. I would also not read the file and pipe it to something, since if the file is empty you won't create a new file as nothing is passed down the pipeline.
Get-ChildItem *.csv -Recurse -PipelineVariable File | ForEach-Object{
Set-Content -Value (Get-Content $File.FullName -Encoding Unicode) -Path {Join-Path $File.Directory "UTF8$($File.Name)"} -Encoding UTF8
}
if you specify the file extension at the end of Get-ChildItem.
This will get only the files with the .csv extension.
By specifying the File path in Out-File it will send it to the specified directory.
Get-ChildItem -Path C:\folder\*.csv -Recurse | ForEach-Object {Get-Content -Encoding Unicode $_ | Out-File -FilePath C:\Folder -Encoding UTF8 "UTF8$_"}

Very Basic query on logging in powershell

I have no programming background and I'm working on doing some very basic tasks with Powershell. Moving files and logging which files moved
I've tried using out-file and write-output none of which are working
Move-Item -Path $sourceDir\*.* -destination $destinationDir -force | Out-File -filepath $Mylogpath -Append
I'm trying to move files from one server to another which I have working however I'd like to log the files I'm moving into a text file and date/time stamp them.
Any pointer gratefully accepted
Move-Item by default does not output anything. To get an output of which files are moved, you can use the -PassThru parameter on Move-Item. This will then tell Move-Item to output the files it is moving:
Move-Item -Path $sourceDir\*.* -destination $destinationDir -force -PassThru | Out-File -filepath $Mylogpath -Append

Copy files in a folder structure to their respective sub-folder with Powershell

I want to move the file "file_to_move.txt"in each folder to their respective "done"-folder.
so the file_to_move.txt in C:\Temp\test\folder1 is moved to C:\Temp\test\folder1\done
and file_to_move.txt in C:\Temp\test\folder2 is moved to C:\Temp\test\folder2\done
...and so on, preferably with a %date%_%time% added to the file-name.
if a folder (like folder4 in the example below) does not have a file_to_move.txt, the script should just ignore it and move on.
folder structure example:
C:\Temp\test\DONE
C:\Temp\test\folder1
C:\Temp\test\folder1\done
C:\Temp\test\folder1\some_other_folder
C:\Temp\test\folder1\some_other_file.txt
C:\Temp\test\folder1\file_to_move.txt
C:\Temp\test\folder2
C:\Temp\test\folder2\done
C:\Temp\test\folder2\some_other_folder
C:\Temp\test\folder2\some_other_file.txt
C:\Temp\test\folder2\file_to_move.txt
C:\Temp\test\folder3
C:\Temp\test\folder3\done
C:\Temp\test\folder3\some_other_folder
C:\Temp\test\folder3\some_other_file.txt
C:\Temp\test\folder3\file_to_move.txt
C:\Temp\test\folder4
C:\Temp\test\folder4\done
C:\Temp\test\folder4\some_other_folder
C:\Temp\test\folder4\some_other_file.txt
I have experimented with a Powershell script even if I'm not very good at it and I dont know it can be done in a standard batch-script.
I have tried this so far:
In a batch-script:
SET ThisScriptsDirectory=%~dp0
SET PowerShellScriptPath=%ThisScriptsDirectory%bin\movescript.ps1
PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& '%PowerShellScriptPath%'"
in the movescript.ps1:
Move-Item C:\Temp\test\*\file_to_move.txt C:\Temp\test\*\done\file_to_move_$(get-date -f yyyyMMdd_HHmmss).txt
But this is not working.
I guess it's not precise enough to work.
As a bonus, can the whole thing be done within the basic script or must we use the external .PS1-file?
You can use the Get-ChildItem cmdlet with a filter to retrieve all file_to_move.txt files recursively from a path. Use the Foreach-Object (alias foreach) to iterate over them and combine the new path using the Join-Path cmdlet. To Copy the Item, you can use the Copy-Item cmdlet:
$itemsToCopy = Get-ChildItem -Path c:\Temp\Test -Filter file_to_move.txt -Recurse
$itemsToCopy | foreach {
$newPath = Join-Path $_.DirectoryName 'done'
New-Item -Path $newPath -ItemType directory -Force | out-null
$_ | Copy-Item -Destination $newPath
}
If you want to add a Timestamp, you could use the Get-Date cmdlet and invoke the ToString method with your desired format on it, example:
(Get-Date).ToString("yyyy-dd-M_HH-mm-ss")
Output:
2016-05-4_15-06-02
You can now concat the filenames using a format string and the $_.Basename and $_.Extension property within your foreach loop. I will leave this as an exercise to you.

Piping out to "New-Items" in PowerShell

I'm trying to output to a file that is created on the fly, but I can't seem to get either to work. Here's that portion of my code-
New-Item -Path $LogPath -Name $InfoLog -Type File -Force
New-Item -Path $LogPath -Name $ErrorLog -Type File -Force
"Script started at: $DateStamp_${TimeStamp}" | $InfoLog
I've also tried just ">>" instead of the pipe. The script runs fine, it just doesn't pipe the output into the file. Instead it pipes it out to a a files called "0" in the directory the script ran from.
Three things.
First, New-Item outputs the item it creates. So unless you do something with the output objects, New-Item will output the new file items to the pipeline. So I think you might want to say:
New-Item -Path $LogPath -Name $InfoLog -Type File -Force | Out-Null
Second, since you're not specifying the path to the file you want to write to, PowerShell will assume the current location.
Finally, if you want to write output to a log file, you probably want to use Out-File. Perhaps something like this:
$infoLogPath = Join-Path $LogPath $InfoLog
"Script started at: $DateStamp_${TimeStamp}" | Out-File $infoLogPath
Join-Path combines the directory and filename into a fully-qualified path name.