My English may not be perfect but I do my best.
I'm trying to write a Powershell script where the filename has a number at the end and it should print exactly that often.
Is this somehow possible ?
With the script it prints it only 1 time.
For whatever reason..
param (
[string]$file = "C:\Scans\temp\*.pdf",
[int]$number_of_copies = 1
)
foreach ($onefile in (Get-ChildItem $file -File)) {
$onefile -match '\d$' | Out-Null
for ($i = 1; $i -le [int]$number_of_copies; $i++) {
cmd /C "lpr -S 10.39.33.204 -P optimidoc ""$($onefile.FullName)"""
}
}
There is no need for parameter $number_of_copies when the number of times it should be printed is taken from the file's BaseName anyway.
I would change your code to:
param (
[string]$path = 'C:\Scans\temp'
)
Get-ChildItem -Path $path -Filter '*.pdf' -File |
# filter only files that end with a number and capture that number in $matches[1]
Where-Object { $_.BaseName -match '(\d+)$' } |
# loop through the files and print
ForEach-Object {
for ($i = 1; $i -le [int]$matches[1]; $i++) {
cmd /C "lpr -S 10.39.33.204 -P optimidoc ""$($_.FullName)"""
}
}
Inside the ForEach-Object, on each iteration, the $_ automatic variable represents the current FileInfo object.
P.S. Your script prints each file only once because you set parameter $number_of_copies to 1 as default value, but the code never changes that to the number found in the file name.
BTW. Nothing wrong with your English
Related
I am writing an IIS log parser and having trouble wrapping a variable value in quotes while doing some string processing.
Here is a truncated log file, as an example:
#Fields: date time s-ip cs-method ...
2021-08-09 19:00:16.367 0.0.0.0 GET ...
2021-08-09 19:01:42.184 0.0.0.0 POST ...
Here is how I am executing the code below:
.\Analyse.ps1 cs-method -eq `'POST`'
If the line marked with #PROBLEM is executed as is, the output looks like this:
> .\Analyse.ps1 cs-method -eq `'POST`'
"""""G""E""T""""" ""-""e""q"" ""'""P""O""S""T""'""
"""""P""O""S""T""""" ""-""e""q"" ""'""P""O""S""T""'""
But if I replace $quoted with $value, so that the code reads like this:
$thisInstruction = $thisInstruction -replace $key , $value #PROBLEM
The output looks like this:
> .\Analyse.ps1 cs-method -eq `'POST`'
GET -eq 'POST'
POST -eq 'POST'
The problem is that I want the first value on each line of the output (the GET and the POST before the -eq) to be wrapped in quotes.
How can I achieve this?
Here is my code:
# compile cli args into single line instruction
$instruction = $args -join " "
# define key array
$keys = #('date','time','s-ip','cs-method','cs(Host)','cs-uri-stem','cs-uri-query','s-computername','s-port','cs-username','c-ip','s-sitename','cs(User-Agent)','cs(Referer)','sc-status','sc-substatus','sc-win32-status','TimeTakenMS','x-forwarded-for')
# <#
# get current execution folder
$currentFolder = Get-Location
# define string splitter regex https://www.reddit.com/r/PowerShell/comments/2h5elx/split_string_by_spaces_unless_in_quotes/ckpkydh?utm_source=share&utm_medium=web2x&context=3
$splitter = ' +(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)'
# process *.log files in folder
Get-Childitem -Path $currentFolder *.log1 | ForEach-Object {
# process each line in the file
Get-Content $_.Name | ForEach-Object {
# duplicate instruction
$thisInstruction = $instruction
# exclude comment lines
if (!$_.StartsWith('#')) {
# split line into array
$logEntryArr = $_ -Split $splitter
# populate dictionary with contents of array
For ($i=0; $i -le $keys.length; $i++) {
# get key
$key = $keys[$i]
# get value
$value = $logEntryArr[$i]
$quoted = "`""+$value+"`""
# replace mention of key in instruction with dictionary reference
$thisInstruction = $thisInstruction -replace $key , $quoted #PROBLEM
}
# process rule from command line against dictionary
echo $thisInstruction
}
}
}
#>
I do know why, thanks to #mathias-r-jessen commenting
I don't know why, but altering the For loop to iterate one fewer fixed the problem and does not appear to leave out any keys. The only significant change is this:
For ($i=0; $i -le $keys.length-1; $i++) {
This PowerShell script can be used to query a folder of log files echo out matching rows, eg:
.\Analyse.ps1 cs-method -eq 'GET'
The above would print out all log entries with a cs-method value of GET.
Here's the code:
# compile cli args into single line instruction
$instruction = $args -join " "
# define key array
$keys = #('date','time','s-ip','cs-method','cs(Host)','cs-uri-stem','cs-uri-query','s-computername','s-port','cs-username','c-ip','s-sitename','cs(User-Agent)','cs(Referer)','sc-status','sc-substatus','sc-win32-status','TimeTakenMS','x-forwarded-for')
# get current execution folder
$currentFolder = Get-Location
# define string splitter regex https://www.reddit.com/r/PowerShell/comments/2h5elx/split_string_by_spaces_unless_in_quotes/ckpkydh?utm_source=share&utm_medium=web2x&context=3
$splitter = ' +(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)'
# process *.log files in folder
Get-Childitem -Path $currentFolder *.log | ForEach-Object {
# process each line in the file
Get-Content $_.Name | ForEach-Object {
# duplicate instruction
$thisInstruction = $instruction
# exclude comment lines
if (!$_.StartsWith('#')) {
# split line into array
$logEntryArr = $_ -Split $splitter
# populate dictionary with contents of array
For ($i=0; $i -lt $keys.length; $i++) {
# get key
$key = $keys[$i]
# get value
$quoted = "`'"+$logEntryArr[$i]+"`'"
# replace mention of key in instruction with dictionary reference
$thisInstruction = $thisInstruction -replace $key , $quoted
}
# process rule from command line against dictionary
$answer = Invoke-Expression $thisInstruction
if ($answer) {
echo $_
}
}
}
}
I am trying to create a Release definition in TFS 2017 with powershell script
I am runing a powershell script that has to find a path of a setParameters.xml file which it does and it suppose to over ride all the tokens but the it just ignores the loop and the arguments stay the same.
Script code:
$vars = Get-ChildItem -path env:*
# read in the setParameters file
$contents = Get-Content -Path $paramsFilePath
# perform a regex replacement
$newContents = "";
$contents | % {
$line = $_
if ($_ -match "__(\w+)__") {
$setting = Get-ChildItem -path env:* | ? { $_.Name -eq $Matches[1] }
if ($setting) {
Write-Verbose -Verbose ("Replacing key {0} with value from environment" -f $setting.Name)
$line = $_ -replace "__(\w+)__", $setting.Value
}
}
$newContents += $line + [Environment]::NewLine
}
Why is the loop being skiped and how could i fix it so it overwrites the arguments in SetParameters.xml file?
For those interested I have ditched this powershell and used Replace Token step in Release definition.
I think its a more efficient way to replace tokens
I have written a PowerShell script that will:
grab all txt files from a directory
perform a line-by-line assessment of the first file (grabbing headers and appending, appending data to each line in file, saving to an output file)
for subsequent files, grab body (excluding header), append data, then add to output file
The problem is in the use of Add-Content where the process hangs so certain files don't get written because the output file is in use. I added a function (based on recommendations found in various places on StackExchange) that test the output file to determine if it is available for read-write. This seems like a 'brute-force' approach.
Is there a way to monitor the actual Add-Content process launched by PowerShell to identify when it is complete? Or is there some other way to disaggregate the code as written to use the process control commands in PowerShell?
Sample:
function IsFileAccessible([String]$FullFileName) {
[Boolean]$IsAccessible = $false
try {
[IO.File]::OpenWrite($FullFileName).Close();
$IsAccessible = $true
} catch {
$IsAccessible = $false
}
return $IsAccessible
}
cd '[filepath]'
del old_output.type
$filearray = #()
$files = Get-ChildItem '[filepath]' -Filter "*.txt"
$outfile = 'new_output.type'
for ($i=0; $i -lt $files.Count; $i++) {
# Define variables
$lastWriteTime = $files[$i].LastWriteTime
# Define process steps for appending data
filter Add-Time {"$_$lastWriteTime"}
if ($i -eq 0) {
$lines = Get-Content $files[$i]
for ($j=0;$j -lt $lines.Count; $j++) {
if ($j -eq 0) {
$appended_txt = 'New_Header'
filter Add-Header{"$_$appended_txt"}
$lines[$j] | Add-Header | Add-Content $outfile
} else {
$lines[$j] | Add-Time | Add-Content $outfile
}
}
} else {
do {
$ErrorActionPreference = 'SilentlyContinue'
$test = IsFileAccessible('[filepath-new_output.type]')
echo 'file open'
} until ($test -eq 'True')
$ErrorActionPreference = 'Continue'
echo 'okay'
(Get-Content $files[$i].FullName | Select-Object -Skip 1) |
Add-Time | Add-Content $outfile
}
}
I have a directory that contains millions of files in binary format. Some of these files were written to the disk wrong (no idea how). The files are not empty, but they only contain zeros. Heres an example http://pastebin.com/5b7jHjgr
I need to search this directory, find the files that are all zeros and write their path out to a file.
I've been experimenting with format-hex and get-content, but my limited powershell experience is tripping me up. Format-Hex reads the entire file, when I only need the first few bytes, and Get-Content expects text files.
Use IO.BinaryReader:
Get-ChildItem r:\1\ -Recurse -File | Where {
$bin = [IO.BinaryReader][IO.File]::OpenRead($_.FullName)
foreach ($byte in $bin.ReadBytes(16)) {
if ($byte) { $bin.Close(); return $false }
}
$bin.Close()
$true
}
In the old PowerShell 2.0 instead of -File parameter you'll need to filter it manually:
Get-ChildItem r:\1\ -Recurse | Where { $_ -is [IO.FileInfo] } | Where { ..... }
You can use a System.IO.FileStream object to read the first n bytes of each file.
The following code reads the first ten bytes of each file:
Get-ChildItem -Path C:\Temp -File -Recurse | ForEach-Object -Process {
# Open file for reading
$file = [System.IO.FileStream]([System.IO.File]::OpenRead($_.FullName))
# Go through the first ten bytes of the file
$containsTenZeros = $true
for( $i = 0; $i -lt $file.Length -and $i -lt 10; $i++ )
{
if( $file.ReadByte() -ne 0 )
{
$containsTenZeros = $false
}
}
# If the file contains ten zeros then add its full path to List.txt
if( $containsTenZeros )
{
Add-Content -Path List.txt -Value $_.FullName
}
}
In powershell i am writing a script using 'if' condition to check a folder for files received in last 2 hours. The code works fine and the output is written to the screen, instead i want it to write to a file which can be emailed.
Request for kind help.
Regards
Abhijeet
EDIT: Code
$f = 'D:\usr\for_check'
$files = ls $f
Foreach ($file in $files)
{
$createtime = $file.CreationTime
$nowtime = get-date
if (($nowtime - $createtime).totalhours -le 2)
{
"$file"
}
}
You can either use the redirection operator > or Out-File
Examples:
"abc" > c:\out.txt
"abc" | Out-File c:\out.txt
Your code is way too complicated. Something like this would be more PoSh:
$src = "D:\usr\for_check"
$out = "C:\output.txt"
$append = $false
Get-ChildItem $src | ? {
$_.CreationTime -ge (Get-Date).AddHours(-2)
} | % { $_.Name } | Out-File $out -Append:$append
You will want to use the >> operator instead of > or out-file operators as they will overwrite the file every time it's used. Whereas the >> operator will write to the file on the next line.
Example:
$file >> c:\out.txt
Writing each line to the file inside the loop can cause a lot of disk I/O.
You can wrap the loop in a script block, and then output all the lines to the file in one write operation.
$f = 'D:\usr\for_check'
$files = ls $f
&{Foreach ($file in $files)
{
$createtime = $file.CreationTime
$nowtime = get-date
if (($nowtime - $createtime).totalhours -le 2)
{
"$file"
}
}
} | set-content c:\outfile.tx