Extra Space needs to remove while executing powershell - powershell

I am executing the below script on the windows server as a PowerShell script -
$today = (Get-Date).ToString('dd_MM_yyyy_HH_mm')
echo "Date & Time : $today"
powershell -Command "Copy-Item -Recurse 'C:\ABC' -Destination 'C:\ABC_"$today"'"
The above script is working fine but there is a blank space between ABC & Date while creating the directory. Please please help me on this how can I remove this blank space.
**Directory Name :** ModelFactoryProducti**on_ 28**_06_2021_11_05
**Directory Name Should be :** ModelFactoryProduction_28_06_2021_11_05

Since you're calling from PowerShell, the best option is to pass a script block to powershell.exe, the Windows PowerShell CLI.
However, this raises the question: why do you need to call the CLI from PowerShell to begin with, given that you could just call your Copy-Item command directly, without the overhead (and potential loss of type fidelity) that comes with creating another PowerShell session, via a child process.
if you still need to call the PowerShell CLI from PowerShell itself, use the following:
$today = (Get-Date).ToString('dd_MM_yyyy_HH_mm')
powershell -Command {
Copy-Item -Recurse 'C:\ABC' -Destination ('C:\ABC_' + $args[0])
} -args $today
As for what you tried:
Removing the " around $today in 'C:\ABC_"$today"' would have worked too - the outer "..." quoting would still have ensured that $today is expanded.
What you thought of as a single string argument,
"Copy-Item -Recurse 'C:\ABC' -Destination 'C:\ABC_"$today"'", was passed as two arguments:
Argument 1: Verbatim Copy-Item -Recurse 'C:\ABC' -Destination 'C:\ABC_, which, due to being a (double-)quoted token became its own argument - despite other characters immediately following it.
Argument 2: The value of $today, immediately followed by a verbatim ' (the value of "'"), e.g., verbatim 06_2021_11_05'
Not being able to compose a single argument from a mix of quoted and unquoted tokens if the first token happens to be quoted is a notable pitfall, discussed in detail in this answer.
When you use -Command and pass multiple arguments, PowerShell simply joins those arguments to form a single string by placing a space between them, before interpreting the resulting string as PowerShell code.
Therefore, your two verbatim arguments, Copy-Item -Recurse 'C:\ABC' -Destination 'C:\ABC_ and (e.g.) 06_2021_11_05' were ultimately interpreted as
Copy-Item -Recurse 'C:\ABC' -Destination 'C:\ABC_ 06_2021_11_05' - note the unwanted space.
See this answer for a comprehensive overview of the PowerShell CLI (covers both powershell.exe and pwsh).

Use the following:
$today = (Get-Date -format 'dd_MM_yyyy_HH_mm')
Copy-Item -Recurse 'C:\ABC' -Destination "C:\ABC_$today"
When possible, avoid using either of these kinds of syntax, as it's very simple to allow accidentally (or maliciously) inserting extra data into the command string. Plus, you're already in powershell - no need to execute powershell again unless you left it to run some cmd commands:
powershell -c "Write-Host $var"
Invoke-Expression -Command 'Write-Host "$var"'

Related

Ignore wildcard characters [] in Powershell strings

I've got a Powershell script that takes a filename as input.
param([string]$folder, [string]$input_filename)
$logfile=$folder + "\duplicate.log"
Write-Output "`nScript: `n$transformation_filename`nLogfile: " | Out-file $logfile -Append
This works fine for most filenames, but it breaks down when the filename or folder name contains [].
dupfile.ps1 "C:\foldername [w]" "filename.xml"
Line 3 throws this error:
Cannot perform operation because the wildcard path C:foldername [w]\duplicate.log did not resolve to a file.
So the variables have to be treated more literally.
$logfile='$folder' + "\duplicate.log"
Nope, that's too literal.
$logfile="$folder" + "\duplicate.log"
Nope, same error.
How can I tell Powershell to insert the variable value but don't try to interpret anything in that value as wildcards?
Edit: I'm using Powershell 5.
My dupfile.ps1 contains only the 3 lines above.
Using this from the PS shell to run the script:
C:_SCRIPTS_\dupfile.ps1 "C:\foldername [w]" "test.txt"
One more addition, if I may. I have a few lines that redirect their error messages to the logfile.
Copy-Item -LiteralPath $inputPath -Destination $outputPath 2>&1|%{ "$_" } >>$logfile
The redirection trips over the [] again. I don't think I can use -LiteralPath here.
tl;dr
To ensure that file-path argument $var in redirection operations > $var / >> $var is treated literally if it contains [ and ], use
| Out-File -LiteralPath $var / | Out-File -LiteralPath $var -Append instead.
Copy-Item -LiteralPath $inputPath -Destination $outputPath 2>&1 |
ForEach-Object { "$_" } |
Out-File -LiteralPath $logfile # literal alternative to >> $logfile
As in your case, you may have to combine it with a redirection in order to ensure that additional streams beside the success output stream are targeted:
# Literal equivalent of *>> $logfile
... *>&1 | Out-File -LiteralPath $logfile -Append
Unfortunately, it gets tricky with capturing a given other stream only, as you then need to use the appropriate -*Variable common parameter, such as the common -ErrorVariable parameter:
# Literal equivalent of 2>> $logfile
... -ErrorVariable errs
$errs | Out-File -LiteralPath $logfile -Append
Caveat:
It is tempting to try to bypass the above solutions by escaping the literal path so that when it is interpreted as a wildcard, it is treated literally, using [WildcardPattern]::Escape().
However, this does not work as expected as of PowerShell 7.2.6, because the escaped form of the literal path is then used as the literal file path - see GitHub issue #9475 and also the discussion about whether > / >>, ... should treat their arguments as wildcards to begin with.
# !! Arguably SHOULD work, but doesn't as of 7.2.6:
# The file that is created is LITERALLY named for the ESCAPED form:
# `[1`].txt
'hi' > ([WildcardPattern]::Escape('[1].txt'))
Background information:
The -Path (-FilePath) parameter of PowerShell's provider cmdlets expects wildcard expressions for targeting file-system items by a name or path pattern. This parameter is implicitly targeted by the first positional argument, i.e. if you don't explicitly name the target parameter - e.g., Get-ChildItem foo is implicitly the same as Get-ChildItem -Path foo.
Surprisingly, this doesn't just apply to file-reading cmdlets (e.g., Get-ChildItem, Get-Content), but also to file-writing cmdlets (e.g., Set-Content, Out-File).
Note that the parameter named -Path is called -FilePath in Out-File for historical reasons. In PowerShell (Core) 7+, -Path was introduced as an alias name, for consistency.
Arguably, in file-writing/creating cmdlets this behavior is inappropriate - given that you usually know exactly what literal path you want to target - and there are several GitHub issues discussing this; an overview of all relevant discussions can be found in this comment on GitHub issue #17106.
In order for such arguments to be treated literally (verbatim), you must
use the -LiteralPath parameter instead.
In effect, > $var and >> $var are aliases for | Out-File -FilePath $var and | Out-File -FilePath $var -Append, which implies that $var is interpreted as a wildcard.
Using explicit Out-File calls with -LiteralPath, as shown at the top, avoids this problem.

How do I pass the Gitlab {CI_COMMIT_SHORT_SHA} variable to powershell script

I have created a script section in Gitlab-CI.yaml file. The format is given below.
script:
- powershell -File script.ps1 folderpath="$env:${CI_PROJECT_DIR}" CommitID="$env:${CI_COMMIT_SHORT_SHA}"
and these variables are calling in my PowerShell script as follows. The script is given below.
$Files = Get-ChildItem $folderpath
foreach($eachFile in $Files)
{
if($eachFile.Name.Contains("myapp"))
{
Rename-Item -Path $eachFile.FullName -NewName "myapp-$CommitID.zip" -Force
}
}
This script is using for changing the name of a zip file. After execting this through gitlab I am getting error like this.
Variable reference is not valid. ':' was not followed by a valid variable name
character. Consider using ${} to delimit the name.
Is it the right format to pass the GitLab env variable to PowerShell script? Can you please suggest some inputs on this?
Assuming that ${CI_PROJECT_DIR} is the value of GitLab's CI_PROJECT_DIR (environment) variable, you can use it as-is in your command line - no need for the $env: indirection, which (a) requires the name of an environment variable and (b) wouldn't work anyway with PowerShell's -File CLI parameter.
Assuming your target script uses regular PowerShell parameter declarations (e.g., param($FolderPath, $CommitId), case doesn't matter), you must pass the values using
-<param> <value> or -<param>:<value> syntax.
To put it all together:
script:
- powershell -File script.ps1 -FolderPath "${CI_PROJECT_DIR}" -CommitID="${CI_COMMIT_SHORT_SHA}"
The alternative is not to use parameters at all, and instead directly access the environment variables from inside your script, using PowerShell's $env:<var_name> notation:
$Files = Get-ChildItem $env:CI_PROJECT_DIR
foreach($eachFile in $Files)
{
if($eachFile.Name.Contains("myapp"))
{
Rename-Item $eachFile.FullName -NewName "myapp-$env:CI_COMMIT_SHORT_SHA.zip" -Force
}
}

How powershell handles returns from & calls

We have field devices that we decided to use a powershell script to help us handle 'updates' in the future. It runs every 5 minutes to execute rsync to see if it should download any new files. The script, if it sees any file types of .ps1, .exe, .bat ,etc. will then attempt to execute those files using the & operator. At the conclusion of execution, the script will write the file executed an excludes file (so that rsync will not download again) and remove the file. My problem is that the return from the executed code (called by &) behaves differently, depending on how the main script is called.
This is the main 'guts' of the script:
Get-ChildItem -Path $ScriptDir\Installs\* -Include #("*.ps1","*.exe","*.cmd","*.VBS","*.MSI") | ForEach {
Write-Verbose "Executing: $_"
& $_
$CommandName = Split-Path -Leaf $_
Write-Verbose "Adding $CommandName to rsync excludes"
Write-Output "$CommandName" | Out-File -FilePath $ScriptDir\excludes -Append -Encoding ASCII
Write-Verbose "Deleting '$_'"
Remove-Item $_
}
When invoking powershell (powershell.exe -ExecutionPolicy bypass) and then executing the script (.\Update.ps1 -Verbose), the script runs perfectly (i.e. the file is written to excludes and deleted) and you can see the verbose output (writing and deleting).
If you run the following (similar to task scheduler) powershell.exe -ExecutionPolicy bypass -NoProfile -File "C:\Update.ps1" -Verbose, you can see the new script get executed but none of the steps afterwards will execute (i.e. no adding to excludes or removing the file or the verbose outputs).

Script to replace line that contains text

I found another discussion on this, where there were numerous different options available but most of them didn't seem to work for myself or the original poster. I did however find an example that is confirmed working, however I am struggling with getting it to work and hoping for help.
I need to find a line that begins with "ServerName=" and replace this with my own line. I have used the example that was found and modified it, but I am getting errors when using it.
PowerShell Command:
powershell -Command "(Get-Content 'KFGame\Config\PCServer-KFGame.ini') | Foreach-Object {$_ -replace '^ServerName.$', ('ServerName=Network BUF 12345 Normal')} | Set-Content 'KFGame\Config\PCServer-KFGame.ini'"
Error Message:
) was unexpected at this time
If you want to run complex PowerShell statements from CMD you need to put them in quotes so that CMD just sees a string and doesn't try to handle special characters (like pipes):
powershell -Command "(Get-Content 'C:\Host400.txt') | Foreach-Object {$_ -replace '^workstationID.*$', (""WorkstationID=$computerName""} | Set-Content 'C:\Host400.txt'"
Note that you need to either escape or replace double quotes within the command string.
A better approach is to put the PowerShell statement(s) into a .ps1 script and run that via the -File parameter:
powershell -File "C:\path\to\your.ps1"

Powershell script in Task Scheduler - spaces and timestamp issue

following script works in Powershell, but won't work when executed as an action in Task Scheduler:
Copy-Item -Path '\\dfs.com\Risk Manage\Daily_DB\Daily_DB.accdb' -Destination "\\dfs.com\Risk Manage\SharedReports\Database Backup 2014\Daily_DB_backup $(get-date -f yyyy-MM-dd).accdb"
Please note that I need to use double quotes in order to get $(get-date -f yyyy-MM-dd) translate into a timestamp. Doesn't work with single quotes.
Task Scheduler error message: Copy-Item : A positional parameter cannot be found that accepts argument 'Manage\SharedReports\Database'.
To sum it up, does anyone know how to execute Powershell script containing path with spaces and timestamp in it, in Task Scheduler?
Many thanks
You could either call a script or use the -encodedcommand parameter: Using Powershell -encodedcommand to pass parameters
Try this:
$Datestamp = (Get-date -f yyyy-MM-dd)
Copy-Item -Path '\\dfs.com\Risk Manage\Daily_DB\Daily_DB.accdb' -Destination "\\dfs.com\Risk Manage\SharedReports\Database Backup 2014\Daily_DB_backup_$Datestamp.accdb"