Using ruby variable in a PowerShell script with winrm - ruby

EDIT:
I would like to count files/folders of storage containers via virtual machine manager PowerShell cmdlet.
I went over similar questions, but still am struggling with syntax.
I have a ruby script that is executing a PowerShell script on a remote server.
I want to use a ruby variable within the Powershel script.
For example
path_str = "\\XXX\YYY\" #This is the ruby var
PSoutput = shell.run(" #This part is executing the PS script
$Files = Get-ChildItem -Recurse -File -Path #{path_str} | Measure-Object | %{$_.Count}" | stdout
How do I use the ruby variable path_str with the PS script?
I have tried
# {path_str}
\" " + path_str + " \"
Double quotes and single quotes
Nothing worked for me.

There are a few things that I see causing the issues.
# is a reserved character in powershell. When you use #, anything after that is a comment.
You are assigning the output of Get-ChildItem to $files. There will be no output from shell.run() to assign to PSoutput because output from cmdlet is getting assigned to $files.
Get-ChildItem is a powershell specific command, not a command line / dos command that you can execute from within shell, without first calling powershell executable. (this, I am a little doubtful on but quite sure its correct).
What you can do from ruby is, should work,
PSoutput = system("powershell get-childitem -Recurse -File -Path " + #{path_str} + " | Measure-Object | % {$_.Count}")
Once the command executes, you should have a total number of files under the #path_str directory.

Related

Having issue outputting the results of script to .txt file in Powershell

New to scripting, currently trying to write a script that will Invoke command to every computer located within domain. My issue is I am trying to direct the output of each computer to a text file.
# Name:
# Date: 02/10/2023
# Desc: Runs remote commands for every computer within domain to collect general information
#Defining variable that reads the computer names from the .txt file
$ADCS = Get-Content -Path "C:\computers.txt"
# Loop through each computer name in the list
foreach ($ADC in $ADCS) {
# Run the Invoke-Command cmdlet for each computer
Invoke-Command -ComputerName $ADC -ScriptBlock {
# Write message to indicate which computer commands are being run on.
Write-Output "Running command on $ADC"
#systeminfo | find "Host Name"
Get-computerinfo
out-file -FilePath C:\Users\aembrey\Documents\ComputerInfo.txt
}
} | out-file -FilePath C:\Users\aembrey\Documents\ComputerInfo.txt
This is what I am currently working with. I have tried multiple difference ways of using Out-file and have failed to redirect the output.
I tried formatting the command like you would as a standard command
"get-computerinfo | out-file -filepath C:\Users\X\X"
But I receive "An empty pipe element is not allowed." error.
I am sure this is a simple issue, but I am stumped.
Basically just trying to get the computer info of all computers, then save it to a text file.

Windows batch file: download many small files fast

I have a list of urls (urls.txt):
https://example.com/1.webp
https://example.org/bar2.webp
... 10k more
Files vary in size from 1kb to 100kb.
How can I download these files quickly on a Windows 10 machine without installing any third-party software?
I need it to be in a single file that user can double-click without installing any additional software.
It should work on any decently up-to-date Windows 10 PC. AFAIK it means the PowerShell version is 5.1.
Additional information.
I tried this:
powershell -Command "Invoke-WebRequest https://example.com/1.webp -OutFile 1.webp"
but it extremely slow due to sequential execution.
So far this works in PowerShell fast enough:
Get-Content .\urls.txt |ForEach-Object {
$FileName = Split-Path -leaf $_
Invoke-WebRequest $_ -OutFile $FileName
}
But I can't figure out how to invoke this script with a double-click on a file.
Invoking .ps1 file from a .bat file doesn't work. Error:
download.ps1 cannot be loaded because running scripts is disabled on this system.
Asking user to adjust permissions is not an option.
This works in a clickable .bat file:
powershell -command ^
Invoke-WebRequest https://example.com/1.webp -OutFile 1.webp;
But this script fails silently:
powershell -command ^
Get-Content .\urls.txt |ForEach-Object { ^
$FileName = Split-Path -leaf $_ ^
Invoke-WebRequest $_ -OutFile $FileName ^
} ^
"...how do I iterate over a file lines with it? Sry, I never used Windows" (that must feel like me after a Linux machine).
Open a PowerShell prompt (Start → Run → PowerShell) or just type PowerShell.exe on the command prompt.
At the PowerShell prompt, to run the task in parallel using ForEach-Object -Parallel:
1..9 |ForEach-Object -Parallel { "Invoke-WebRequest https://example.com/$_.webp" -OutFile "$_.webp" }
Where "$_" is the current item (1to9`), you might also use a list here, like:
'One', 'Two', 'Three' |ForEach-Object -Parallel { ...
In case you "need to read it directly from the file", (presuming that you want use the name in the url as your filename) you might do something like this:
Get-Content .\urls.txt |ForEach-Object -Parallel {
$FileName = Split-Path -leaf $_
"Invoke-WebRequest $_ -OutFile $FileName
}
Update
(based on the additional information in your question and comments in this answer)
Final steps to making you command line easy to launch for novice user, taking in account that passing "complex" commands with special characters (as newlines, spaces and double quotes) from a batch file interpreter to PowerShell is quiet a hassle as there are a lot of exceptions on the exceptions. See: these stackoverflow questions
In your case it might be simply putting your commands in a single (quoted) command line and separate each syntax with a semi-colon (;):
powershell -command "Get-Content .\urls.txt |ForEach-Object { $FileName = Split-Path -leaf $_; Invoke-WebRequest $_ -OutFile $FileName }"
But to be on the safe side (in case e.g. a powershell command/parameter requires to be quoted by itself), I would rather supply robust solution which is encoding your command line to base64 and use the -EncodedCommand parameter. See also these answers:
running powershell as shell command having error in StartTime variable for FilterHashtable
Pass complex arguments to powershell script through encoded command
Encoding
To encode your command line to base64:
$Command = {
Get-Content .\urls.txt |ForEach-Object {
$FileName = Split-Path -leaf $_
Invoke-WebRequest $_ -OutFile $FileName
}
}.ToString()
$Bytes = [System.Text.Encoding]::Unicode.GetBytes($Command)
[Convert]::ToBase64String($Bytes)
Download.bat
Including the encoded command line in a (single) batch file add the following command in you batch file where the base64 string is copied from the above ToBase64String conversion:
PowerShell -EncodedCommand CgAgACAAIAAgACAAIAAgACAARwBlAHQALQBDAG8AbgB0AGUAbgB0ACAALgBcAHUAcgBsAHMALgB0AHgAdAAgAHwARgBvAHIARQBhAGMAaAAtAE8AYgBqAGUAYwB0ACAAewAKACAAIAAgACAAIAAgACAAIAAgACAAIAAgACQARgBpAGwAZQBOAGEAbQBlACAAPQAgAFMAcABsAGkAdAAtAFAAYQB0AGgAIAAtAGwAZQBhAGYAIAAkAF8ACgAgACAAIAAgACAAIAAgACAAIAAgACAAIABJAG4AdgBvAGsAZQAtAFcAZQBiAFIAZQBxAHUAZQBzAHQAIAAkAF8AIAAtAE8AdQB0AEYAaQBsAGUAIAAkAEYAaQBsAGUATgBhAG0AZQAKACAAIAAgACAAIAAgACAAIAB9AAoAIAAgACAAIAA=
You could try the foreach-object -parallel method for this case, i tried something simular once with multiple process starts for robocopy to get like 1000 small files (5-10kb) on another harddrive.
I will look up if i can find it again.
Edit 1: you can go over like this for example.
$allmylinks = import-csv -path "path to your csv"
foreach -parallel($link in $allmylinks){
Invoke-WebRequest $link
}

Calling a command line application outtputing to stderr with piping

I'm looking to get both std-err and std-out streams from a binary I have called by powershell piped to a powershell cmdlet. I cant seem to get the syntax or find the right command.
I have a little Write-Smartly cmdlet that I want to process line-by-line of combined standard-error and standard output. It is itself a wrapper on Write-Host that adds some indenting.
My first attempt, I want to:
use -ErrorAction to indicate to powershell not to treat standard-error messages as exceptions
use 2>&1 to combine standard-error and standard-output streams into a pipelineable output
use ... | MyCmdlet to format the combined output & error streams to their ultimate output
giving me:
& $myApp $args --% -ErrorAction 'SilentlyContinue' 2>&1 | Write-Smartly
Unfortunatly this ended up passing -ErrorAction SilentlyContinue and 2>&1 into my application as arguments. Standard-error did show up as regular output, but it was written directly to host, rather than piped to WriteSmartly.
So I made a few modifications to try to correct this:
$ErrorActionPreference = 'SilentlyContinue'
&{ & $myApp $args } 2>&1 | Write-Smartly
This simply causes my error output to disappear.
I also played around with Start-Process, but it seems too heavyweight to allow me to get things out of it with a pipe. I think that Invoke-Command or Invoke-Expression might be helpful here but I'm not sure what their use cases are.
How can I capture both standard-error and standard-output as a pipelined value in powershell?

How to execute Windows command (Get-Content) from PowerShell in Windows Server

My server is Windows Server. I would like to replicate the Unix tail command in Windows Server.
Unix Server: tail -f test.txt
PowerShell: Get-Content test.txt
How to execute in Windows Server?
Below command is not working:
powershell -File "Get-Content test.txt"
Error message:
Unable to execute program 'powershell -File "\"Get-Content...
Any idea?
Get-Content is not a file; it is a cmdlet. The -file parameter to Powershell.exe instructs Powershell to read the file supplied and execute the commands in it, as a script.
You can pass commands directly to Powershell by using the -command parameter; the parameter can be a quoted string, which is interpreted as a Powershell command. You would therefore want to use
powershell -command "Get-Content test.txt"
in the simplest case.
Note that Powershell.exe must be in your system path; if it is not, you would need to supply the full path to powershell, e.g.,
C:\WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe -command "Get-Content text.txt"
This question is very similar - perhaps essentially identical - to Unix tail equivalent command in Windows Powershell; I would recommend reading that question and its answers as well.
Additionally, exploring the help for Get-Content will provide useful information.
Working fine after setting full path of powershell.exe and without any quotes
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -Command Get-Content test.txt
Within a powershell window :
Get-Content test.txt
command returns :
hello world.
i'm inside test.txt.
bye.

Powershell output doesn't log properly when called from Task Scheduler

I need to log my powershell output. My ps file is something like this:
#Set-ExecutionPolicy Unrestricted
$ErrorActionPreference="SilentlyContinue"
Stop-Transcript | out-null
$ErrorActionPreference = "Continue"
$date = (Get-Date).tostring("MMddyy HHmmss")
$filename = 'C:\apierror\logs\' + $date + '.txt'
Start-Transcript -path $filename -append
$python = "C:\Python34\python.exe"
$python_path = "C:\script.py"
cd (split-path $python_path)
& $python $python_path
Stop-Transcript
Now, when I run this file directly from powershell, the output is logged correctly. But when I try to run it from taskscheduler - only some portion of the console output is stored in the file.
Any ideas why that might be?
Using transcript only stored partial output for some reason. I ended up using logs directly into the python file as opposed to powershell. Seems to be working correctly.

Resources