I want to create a PowerShell script that sets the system volume to a specified level, and then runs an audio file found in the same directory as the script. I've figured out the first part, but I can't seem to manage the second. It is important that the folder can be moved around and renamed, and the script should still work. This creates the problem that I cannot simply use Invoke-Item and then specify the filename, as the path is subject to change.
Edit:
My attempt:
$player = New-Object System.Media.SoundPlayer "$env:userprofile\SoundFile.wav"
$player.Play()
Start-Sleep -s 10
This has the problem that it doesn't work if the path is changed.
Found a solution: I used the automatically defined variable $PSScriptRoot, which had apparently been added in PS 3.0. So the line is now
$player = New-Object System.Media.SoundPlayer "$PSScriptRoot\SoundFile.wav"
Related
Is there a way to see if any PowerShell script has executed at-least once before, without the script itself creating any logs? I.e. is there some sort of a native record keeping mechanism for already executed scripts (an example would be an event generating and hence creating an event log), meaning a log of the script actually executed was made at runtime but externally?
For example : If script A was executed once today, check during the second execution (say 2 days later) of script A if it had already executed before.
Can this be done through any Event logs, or through environment variables?
EDIT: Please note, for this particular script, no text files or logs can be made. Is there a way to do this without actually leaving a trace "physically" but instead relying on any parameters being set when a script executes?
EDIT2: This script would be executing with the least of privileges, so not only an account which does not have admin permissions, but would also not have approved permissions to create text or log files.
How about self-modifying code? It's technically cheating, as the change is being done in the script file itself. No external logging is done, though.
write-host -nonewline "Script has started"
$src = get-content $MyInvocation.MyCommand
$header = $src[0]
if($header -notmatch "^#") {
write-host " ...first time!"
$newScript = #()
$newScript += "#"
$newScript += $src
set-content $MyInvocation.MyCommand $newScript
} else { write-host " ...nth time!" }
The script reads its own contents. If the first line doesn't start with a hash, it's the first invokation. The script then creates a new set of source code. The first line is a hash, original source comes after that. Then the original script is overwritten.
As a side note, the requirement to log-but-not-log is self-contradictory just as #Mathias R. Jessen pointed out. There is, however, process tracking audit available in Windows. Also, there is script block logging. In theory (and with proper permissions), one might search the Windows' event logs for previous run attempts.
I'd much rather pop the why stack and find out the underlying reason, as the requirement to eat and keep a cacke sounds very much like an XY problem.
I've tried several solutions to no avail, but am looking for a solution as to create a batch file that will run any commands located in a remote .txt file located on a website.
For example, somebody clicks on a .bat and the .bat retrieves a list of commands from www.example.com/command.txt, and then runs the contents of the .txt as if the commands were specified explicitly in the original .bat file. I am not opposed to using PowerShell or VBScript.
In PowerShell you can do this: iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
i.e. this downloads and runs the PowerShell script: https://chocolatey.org/install.ps1
Explanation
New-Object System.Net.WebClient creates a new instance of the .Net web client.
DownloadString('https://chocolatey.org/install.ps1') invokes the web client's Download method to pull back the text from this script (see https://msdn.microsoft.com/en-us/library/fhd1f0sw(v=vs.110).aspx).
iex is an alias for invoke-expression. This runs the downloaded string as if it were a command; i.e. causes PowerShell to execute the remote script.
Simpler Version
While the Chocolatey install script's an impressive 1 liner, the expanded version below may be simpler to understand (though ultimately does exactly the same as the above; just split over several lines).
$scriptUri = 'https://chocolatey.org/install.ps1'
$webClient = New-Object -TypeName 'System.Net.WebClient'
$scriptAsString = $webClient.DownloadString($scriptUri)
Invoke-Expression -Command $scriptAsString
The above example's taken from https://chocolatey.org/install
I'm pretty new to scripting (especially powershell) and new to Stack Overflow, so please, excuse my ignorance and please bear with me! I will do my best to specifically explain what I'm looking to do and hopefully someone can give a detailed response of what I could do to make it work..
Intended Process/Work Flow: A co-worker downloads "Install.zip" file that has all the necessary files. This "Install.zip" file contains "Setup.bat" file (for computer config), "Fubar.zip" file, 2 powershell scripts, and a custom powerplan (.pow) file. Once downloaded they will run the "Setup.bat" file and it will pretty much do all the work. Inside that batch file it calls 2 powershell scripts. 1)"Download.ps1" - Downloads some other files from the web. 2.) "Unzip.ps1" - Unzips "Fubar.zip" and places contents in another folder - C:\TEST\
Issue: I've recently gotten familiar with using %~dp0 in batch files. I want to make sure that the location where my co-worker initially downloads the Install.zip doesn't throw off my batch file. So for example.. some people will download .zip files to the "Downloads" folder, then extract contents to proper destination. Others will download the .zip to a specific folder, then extract it within that folder. [Ex: C:\Alex\Install.zip --Extraction-- C:\Alex\Install\((Content))] So I tried to not pre-define file locations due to the variables. I've gotten the %~dp0 to work everywhere I need it to in my batch file. The only issue I have is getting my powershell scripts to use same working directory that my batch file is in. *My batch file and my powershell scripts will always be in the same directory. (Wherever that may be)
Goal: I want my powershell script ("Unzip.ps1") to look for my "Fubar.zip" file in the same directory that its currently running in. (Again - Wherever that may be) I basically want to remove any variables that may throw off the powershell script. I want it to always use it's current working directory to locate Fubar.zip. I basically need powershell to either use its current working directory OR figure out a way to have it pull its current working directory and use that to look for "Fubar.zip".
my current "Unzip.ps1" powershell script is extremely basic.
Unzip.ps1:Expand-Archive -Force c:\ALEX\Install.zip\Fubar.zip -dest c:\TEST\
Batch File Command that calls the Unzip.ps1 script: Powershell.exe -executionpolicy remotesigned -File %~dp0UNZIP.ps1
Please keep in mind, I'm just learning scripting and I'm teaching myself. My knowledge is limited, but I've made it this far and this is the only part I'm stuck on. Please give clear responses. Any help or advice would be extremely appreciated! Using PowerShell 5.0
Thanks in advance!
The equivalent of cmd.exe's %dp0 in PowerShell v3+ is $PSScriptRoot:
Both constructs expand to the absolute path of the folder containing the batch file / PowerShell script at hand.
(The only difference is that %dp0 contains a trailing \, unlike $PSScriptRoot).
Thus, to make your Unzip.ps1 script reference Fubar.zip in the same folder that the script itself is located in, use:
Expand-Archive -Force $PSScriptRoot\Fubar.zip -dest c:\TEST\
Constructing the path as $PSScriptRoot\Fubar.zip - i.e., blindly joining the variable value and the filename with \ - is a pragmatic shortcut for what is more properly expressed as (Join-Path $PSScriptRoot Fubar.zip). It is safe to use in this case, but see the comments for a discussion about when Join-Path should be used.
The automatic $PSScriptRoot variable requires PS v3+; in v2-, use
Expand-Archive -Force ((Split-Path $MyInvocation.Mycommand.Path) + '\Fubar.zip') -dest c:\TEST\
From your description, I gather that Fubar.zip and Unzip.ps1 are in the same directory. We'll pretend this directory is C:\Users\Me\Temp; although I understand that may vary.
Powershell's working directory will be the directory you're in (if called from CMD) when you launch; otherwise, it'll be from $env:UserProfile. Since the .bat file always call Unzip.ps1 from the directory that it's in (C:\Users\Me\Temp), powershell.exe will find it with this command (you can still use %~dp0 here; it's not hurting anything):
powershell.exe -ExecutionPolicy RemoteSigned -File Unzip.ps1
Inside of Unzip.ps1, you'll use Get-Location:
Expand-Archive -Force "$(Get-Location)\Fubar.zip" -dest c:\TEST\
However, if the .bat file does a cd into another directory, this won't work. From your %~dp0UNZIP.ps1 example, I assume this isn't the case, but let's address it anyway. If this is the case, you need to process from where the location of the script is. So for this call the full/relational path to the .ps1:
powershell.exe -ExecutionPolicy RemoteSigned -File C:\Users\Me\Temp\Unzip.ps1
Then, your Unzip.ps1 will need to look like this:
Expand-Archive -Force "${PSScriptRoot}\Fubar.zip" -dest 'C:\TEST\'
Alternatively, you can also do some fancy path splitting, as #JosefZ suggested. The $PSCommandPath and $MyInvocation variables contain the full path to your script; which you should familiarize yourself with:
$location = Split-Path $PSCommandPath -Parent
$location = Split-Path $MyInvocation.MyCommand.Path -Parent
Expand-Archive -Force "${location}\Fubar.zip" -dest 'C:\TEST\'
Note: Of course, you wouldn't set $location twice. I'm just showing you two ways to set it.
I hope this helps!
Prior to Powershell 3
$currentScriptPath = Split-Path ((Get-Variable MyInvocation -Scope 0).Value).MyCommand.Path
Otherwise, $PsScriptRoot will work. If you're going to depend on it, though, I'd make sure you mark the script with #Requires -version 3
I'd advise against changing the PWD unless you must. Which is to say, reference the variable directly.
I am busy with creating a PowerShell script where a folder needs to be copied to another folder as a part of the script.
For this I would like to use the standard Windows copy interface and leverage from the prompts and selections like “There is already a file with the same name in this location.”, “Do you want to merge this folder”, “Do this for all”, etc. instead of programming this all myself.
I investigated a view directions:
Using the IFileOperation::CopyItem method as e.g. HowTo: Display progress dialog using IFileOperation but I could find any hint of how to embed this in PowerShell
Using Verbs() Copy/Paste but although the example Invoke-FileSystemVerb -Path "C:\TestDir -Verb Cut; Invoke-FileSystemVerb -Path "\\server\share" -Verb Paste” suggests otherwise, I can paste files that are manually copied/cut but could not copy/cut files with using the CmdLet or simply using the Verb.DoIt() method (I suspect this for a security reason).
Simulate a drag-drop?
But could not find any working solution.
Any suggestion how to do this from PowerShell?
I use this to extract from ZIP files which seems to come up with all the prompts and progress bar:
$shell = New-Object -Com Shell.Application
$folder = $shell.NameSpace(“$path\Folder”)
$shell.NameSpace($otherpath).CopyHere($folder)
A little background...I use Windows XP, Vista, and 7 quite frequently. As such, I constantly have to move my program settings from the %appdata% folder on each PC to the next. I figured that making a PowerShell script to do this for me and remove the folders after I finish would be something to ease my troubles. As I generally have my work on a flash drive, I was hoping to use relative paths, but it seems to be causing me a bit of trouble, but the biggest problem is that I don't seem to understand Powershell enough to know what mistake I'm making and how to fix it... So I came here.I figured that I could separate the task into two scripts; one for placing the directories and the second for copying them back to the original folder and removing any trace of them behind. I'll show you want I have so far. I figured retrieving them might be more difficult so I started there. Here's what I have so far. I'm using a txt file to make it easy to update the list of folders I want or need transferred so it's also being targeted by a variable.
$fldrtxt = Get-Content .\FolderList.txt
$dirget = -LiteralPath ="'%appdata%'\$_fldertxt"
$dirpost = "./Current"
# get-command | Add-Content .\"$today"_CommandList.txt
Set-Location c: {get-content $_dirget} | %{ copy-item $_dirpost}
I can't get PowerShell to recognize the same command that I use when I use the run utility. Since I'm sure I can use %appdata% to reference where I want the folders taken from and to, how can't I write this script to do what I want? I can use an absolute path, because I'd have to use a separate script for all three computers. And that I don't want.
How can I use PowerShell to do what I want and target the folders I need to use?
First: Accerss the Environment
Since I'm sure I can use %appdata% to reference where I want the folders take from and too
Wrong syntax for PowerShell, the %var% syntax for environment variables is specific to cmd scripts (and carried forward from MS-DOS batch files).
In PowerShell to access environment variables prefix their name with env:, so $env:AppData.
$_dirget = "$env:AppData\$_fldertxt"
Second: Passing parameters
Don't include the parameter name in the variable, a variable passed to a cmdlet will be passed as an argument not a parameter name. You need:
get-content -LiteralPath $_dirget
(There is something call "splat" that allows you to use a hash tables of parameter name-argument pairs as a hashtable, but that's unnecessary here.)