How to execute powershell commands (not from ps1 file) from cmd.exe - powershell

I am trying to execute powershell if-else from cmd.
For example to check the number of files, that has "temp" in its name in D: drive,I used,
if(($i=ls D:\* | findstr /sinc:Temp).count -ne 0 ) {Write-Host $i}
This works fine from PS windows
But if want to do the same from cmd, How do i do it?
I tried
powershell -noexit if(($i=ls D:\* | findstr /sinc:Temp).count -ne 0 ) {Write-Host $i}
which did not work unfortunately.

Just put the command in double quotes:
powershell "if(($i=ls D:\* | findstr /sinc:Temp).count -ne 0 ) {Write-Host $i}"
I also think you don't need the -NoExit switch here. This switch prevents powershell from exiting after running the command. If you want to return back to cmd, remove this switch.

I know this doesn't answer how to run the command(others have covered it already), but why would you want to combine cmd and powershell, when both can do the job alone?
Ex powershell:
#Get all files on D: drive with temp in FILEname(doesn't check if a foldername was temp)
Get-ChildItem D:\ -Recurse -Filter *temp* | where { !$_.PSIsContainer } | foreach { $_.fullname }
#Check if fullpath includes "temp" (if folder in path includes temp, all files beneath are shown)
Get-ChildItem D:\ -Recurse | where { !$_.PSIsContainer -and $_.FullName -like "*temp*" } | foreach { $_.fullname }
Ex cmd:
#Get all files with "temp" in filename
dir d:\*temp* /s /a:-d /b

Just another solution for you problem without using powershell:
dir /b D:\*Temp* | find /v /c "::"
This will print just the number of files or folders on D: that have "Temp" in their names. The double-colon here is just a string that should not be in the output of dir /b, so find /v /c "::" counts all lines of the output of dir /b.

#utapyngo's double quote solution works.
Also another way for #utapyngo's another way to make it in cmd:
dir /b D:\* | find /c "*Temp*"
And to Bill: there should not be a opening double quote before & in your first code, I guess?

powershell -noexit "& "C:\........\run_script.ps1"
see these -- http://poshoholic.com/2007/09/27/invoking-a-powershell-script-from-cmdexe-or-start-run/
http://www.leeholmes.com/blog/2006/05/05/running-powershell-scripts-from-cmd-exe/
or for V2
Powershell.exe -File C:\.........\run_script.ps1

Related

Create multiple files with Powershell?

From this answer I can create multiple files a.txt, b.txt, ... , z.txt. in Bash with:
touch {a..z}.txt
Or 152 with:
touch {{a..z},{A..Z},{0..99}}.txt
How can I do this in Powershell?
I know New-Item a.txt, but If I want multiple files as above?
For curiosity, what are the equivalent commands in Command Prompt (cmd.exe)?
For Powershell:
1..5 | foreach { new-item -path c:\temp\$_.txt }
The foreach loop will run for each number in 1 to 5, and generate a file in the desired path with the name of that number passed to the command (represented by the $_)
You could also write it as:
%{1..5} | new-item c:\temp\$_.txt
For cmd:
for /L %v in (1,1,5) do type nul > %v.txt
More information here: cmd/batch looping
Not quite as concise as bash, but it can be done.
#(97..(97+25)) + #(48..(48+9)) |
ForEach-Object { New-Item -Path "$([char]$_).txt" -WhatIf }
Another way...
#([int][char]'a'..[int][char]'z') + #([int][char]'0'..[int][char]'9') |
ForEach-Object { New-Item -Path "$([char]$_).txt" -WhatIf }
And one more...
function rng { #($([int][char]$args[0])..$([int][char]$args[1])) }
(rng 'a' 'z') + (rng '0' '9') |
ForEach-Object { New-Item -Path "$([char]$_).txt" -WhatIf }
If you are desperate to do this in a cmd.exe shell, this might work. When it looks like the correct commands are produced, delete or comment out the echo line and remove the rem from the next line.
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "CLIST=abcdefghijklmnopqrstuvwxyz0123456789"
FOR /L %%i IN (0,1,35) DO (
CALL SET "S=%%CLIST:~%%i,1%%.txt"
echo TYPE NUL ^>"!S!"
rem TYPE NUL >"!S!"
)
#Emilson Inoa Your solution is very ingenuous; there's a typo in the names, am sure you meant to exclude the extensions in the array. You'll end up with
("Text1", "Text2", "Text3") | % {ni -Path "/path/to/dir" -Name "$_.txt"}
For letters, in PowerShell, use:
97..( 97+25 ) | foreach { new-item $env:temp\$( [char]$_ ).txt }
Very simple, take a look:
("Text1.txt","Text2.txt", "Text3.txt") | foreach { New-Item -Path "X" -Name "$_.txt" }
You will replace X of course with the path where you want the files to be created.
If further explanation is required, let me know.
The following command in powershell will create multiple files (20) named Doc_.txt in the directory that the command is executed.
new-item $(1..20 | %{"Doc$_.txt"})
I think this command is the closest to the bash equivalent:
touch Doc{1..20}.txt
This doesn't directly answer your question (as this method requires each file extension be provided), but a more rudimentary way is of course:
New-Item {a..z}.txt, {A..Z}.txt, {0..9}.txt
After a few failed attempts, I mashed a couple of the answers above to create files titled [char][int].txt: 1..5 | foreach {New-Item -Path 'X' -Name "abc$_.txt"}, where X is the path. Also, just to thank the original writer of the question, as it described really succinctly exactly the problem I was trying to solve.
1..1000000 | foreach {new-item -path C:\Testdata$_.txt}
This worked fine for me. Created 1M files in the C:\Testdata folder.
In the PowerShell, you can use New-Item
New-Item a.txt, b.txt, c.txt
then hit Enter

Delete whole line in html file recursively with PowerShell

i'm trying to delete the "unwanted" class lines from an HTML file using power shell script
<a class="unwanted" href="http://www.mywebsite.com/rest/of/url1" target="_blank">my_file_name1</a><br>
<a class="mylink" href="http://www.mywebsite.com/rest/of/url2" target="_blank">my_file_name2</a><br>
<a class="unwanted" href="http://www.mywebsite.com/rest/of/url3" target="_blank">my_file_name3</a><br>
Currently i'm replacing strings using this script
$s = "old string"
$r = "new string"
Get-ChildItem "C:\Users\User\Desktop\Folder" -Recurse -Filter *.html | % {
(Get-Content $_.FullName) `
| % { $_ -replace [regex]::Escape($s), $r } `
| Set-Content $_.FullName
}
Since you tagged your question also with cmd and batch-file, I want to contribute a related answer.
cmd.exe/batch scripting does not understand HTML file format, but if your HTML file(s) look(s) like the sample data you provided (the <a> tag and the corresponding </a> tag are in a single line, and there is nothing else (than <br>)), the following command line could work for you -- supposing a HTML file to process is called classes.html and the modified data is to be written to file classes_new.html:
> "classes_new.html" findstr /V /I /L /C:"class=\"unwanted\"" "classes.html"
This only works if the string class="unwanted" occurs only in the <a> tags that need to be removed.
To process multiple files, the following batch script could be used, based on the above command line:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "ARGS=%*"
setlocal EnableDelayedExpansion
for %%H in (!ARGS!) do (
endlocal
call :SUB "%%~H"
setlocal
)
endlocal
endlocal
exit /B
:SUB file
if /I not "%~x1"==".html" if /I not "%~x1"==".htm" exit /B 1
findstr /V /I /L /C:"class=\"unwanted\"" "%~f1" | (> "%~f1" find /V "")
exit /B
The actual removal of lines is done in the sub-routine :SUB, unless then file name extension is something other than .html or htm. The main script loops through all the given command line arguments and calls :SUB for every single file. Note that this script does not create new files for the modified HTML contents, it overwrites the given HTML files.
Removing lines is even easier than replacing them. When outputting to Set-Content, simply omit the lines that you want removed. You can do this with Where-Object in place of your Foreach.
Adapting your example:
$s = "unwanted regex"
Get-ChildItem "C:\Users\User\Desktop\Folder" -Recurse -Filter *.html | % {
(Get-Content $_.FullName) `
| where { $_ -notmatch $s } `
| Set-Content $_.FullName
}
If you want literal matching instead of regex, substitute the where clause
where { -not $_.Contains($s) } `
Note this is using the .NET function [String]::Contains(), and not the PowerShell operator -contains, as the latter doesn't work on strings.
Try using multiline strings for your $s and $r. I tested with the HTML examples you posted as well and that worked fine.
$s = #"
old string
"#
$r = #"
new string
"#
Get-ChildItem "C:\Users\User\Desktop\Folder" -Recurse -Filter *.html | % {
(Get-Content $_.FullName) `
| % { $_ -replace $s, $r } `
| Set-Content $_.FullName
}

Batch Script for filtering rows from textfile

first of all: I'm on a Windows 7 machine ;).
I got a folder with several dozens files. Each file contains about 240.000 rows. But only half of that rows are needed.
What I would like to do is: have a script that runs over these files, filters out every row that contains the string "abcd" and have it either saved in a new directory or just saved in the same file.
I would try using Powershell as below:
$currentPath = "the path these files currently in"
$newPath = "the path you want to put the new files"
$files = Get-ChildItem $currentPath
foreach ($item in $files) {
Get-Content $item | Where-Object {$_ -notmatch 'abcd'} |Set-Content $newPath\$item
}
#echo off
setlocal enableextensions
set "_where=c:\some\where\*.txt"
set "_filter=abcd"
rem find files which needs filter
for /f "tokens=*" %%f in ('findstr /m "%_filter%" "%_where%"') do (
rem generate a temporary file with the valid content
findstr /v /c:"%_filter%" "%%~ff" > "%%~ff.tmp"
rem rename original file to .old
ren "%%~ff" *.old > nul
rem rename temporary file as original file
ren "%%~ff.tmp" "%%~nxf" > nul
)
rem if needed, delete *.old files
you might use sed for Windows
sed -i.bak "/abcd/!d" *.txt
Find all abcd containing rows in .txt files, make a backup file .bak and store the the detected lines in the original file.
#echo on
For %%a in (*.txt) do (CALL:FILTER "%%a")
echo/Done.&pause>nul&exit/b
:FILTER
type "%~1"|find "abcd" 1>nul 2>nul
if %errorlevel% EQU 0 find /n "abcd" "%~1">"Found abcd in %~1.txt"
The command Find returns error level = 0 if him find something
If the files are that big, I'd so something like this:
$Folder = 'C:\MyOldFiles'
$NewFolder = 'C:\MyNewFiles'
New-Item -ItemType Directory -Path $NewFolder -Force
foreach ($file in Get-ChildItem $Folder)
{
Get-Content $file -ReadCount 1500 |
foreach { $_ -notmatch 'abcd' } |
Add-Content "$NewFolder\$($file.name)"
}

Executing external command from PowerShell is not accepting a parameter

I am executing the following code attempting to execute the 7z.exe command to unzip files.
$dir contains the user input of the path to the zip file which can contain spaces of course! And $dir\temp2 below is a directory that I previously created.
Get-ChildItem -path $dir -Filter *.zip |
ForEach-Object {
$zip_path = """" + $dir + "\" + $_.name + """"
$output = " -o""$dir\temp2"""
&7z e $zip_path $output
}
When I execute it I get the following from 7z.exe:
7-Zip [64] 9.20 Copyright (c) 1999-2010 Igor Pavlov 2010-11-18
Processing archive: C:\test dir\test.zip
No files to process
Files: 0
Size: 0
Compressed: 50219965
If I then copy the value from $zip_path and $output to form my own cmd line it works!
For example:
7z e "c:\test dir\test.zip" -o"c:\test output"
Now, I can reproduce the same message "no files to process" I get when I execute within PowerShell by using the following cmd in cli.
7z e "c:\test dir\test.zip" o"c:\test output"
So, it seems that PowerShell is removing the dash char from my -o option. And yes, it needs to be -o"C:\test output" and not -o "c:\test output" with 7z.exe there is no space between the -o parameter and its value.
I am stumped. Am I doing something wrong or should I be doing this a different way?
I can never get Invoke-Expression (alias = &) to work right either, so I learned how to use a process object
$7ZExe = (Get-Command -CommandType Application -Name 7z )
$7ZArgs = #(
('-o"{0}\{1}"' -f $dir, $_.Name),
('"{0}\{1}"' -f $dir, 'temp2')
)
[Diagnostics.ProcessStartInfo]$7Zpsi = New-Object -TypeName:System.Diagnostics.ProcessStartInfo -Property:#{
CreateNoWindow = $false;
UseShellExecute = $false;
Filename = $7ZExe.Path;
Arguments = $7ZArgs;
WindowStyle = 'Hidden';
RedirectStandardOutput = $true
RedirectStandardError = $true
WorkingDirectory = $(Get-Location).Path
}
$proc = [System.Diagnostics.Process]::Start($7zpsi)
$7ZOut = $proc.StandardOutput
$7ZErr = $proc.StandardError
$proc.WaitForExit()
I was able to duplicate the exact issue and tried numerous combinations escaping the -o switch and escaping quotes " and what not.
But as one answer mentioned Sysinternals, and I used Process Monitor to find out the format it was passing to 7z.exe. Things that work on a plain commandline doesn't work inside PowerShell the same way.
For example, if I tried to construct parameters inside PowerShell just like cmdline it would fail. I.e., -o"C:\scripts\so\new folder" doesn't work. But if you include the -o switch inside quotes then PowerShell passes the string "-oC:\scripts\so\new folder" which 7z.exe is happy to accept. So I learned that 7z.exe would accept both the formats such as
"C:\Program Files\7-zip\7z.exe" e "C:\scripts\so\new folder.zip" -o"C:\scripts\so\new folder"
and
"C:\Program Files\7-zip\7z.exe" e "C:\scripts\so\new folder.zip" "-oC:\scripts\so\new folder"
And both examples contain spaces in them.
[string]$pathtoexe = "C:\Program Files\7-Zip\7z.exe"
$dir = "C:\scripts\so"
$output = "$dir\new folder"
Get-ChildItem -path $dir -Filter *.zip | % {
[array]$marguments = "e",$_.FullName,"-o$output";
& $pathtoexe $marguments
}
Another approach in PowerShell V3 is to escape the PowerShell parsing feature. You can use the --% command to tell PowerShell to stop parsing any more commands like this.
$zipfile = "C:\scripts\so\newfolder.zip"
$destinationfolder = "C:\scripts\so\New Folder"
[string]$pathtoexe = "C:\Program Files\7-Zip\7z.exe"
& $pathtoexe --% e "C:\scripts\so\newfolder.zip" -o"C:\scripts\so\new folder"
Using the --% syntax, you type commands just like you would type them on the command line. I tested this logic, and it extracts files to the destination folder.
To learn more about --%, check PS> help about_parsing.
The issue with this approach is after --% it is not possible to include a variable. The solution to this issue is to just include the --% as another string variable and pass it like this. And this approach is similar to the commandline approach which wasn't working originally.
[string]$pathtoexe = "C:\Program Files\7-Zip\7z.exe"
$dir = "C:\scripts\so"
$output = "$dir\new folder"
Get-ChildItem -path $dir -Filter *.zip | % {
$zipfile = $_.FullName;
[string]$formatted = [System.String]::Concat("e ", """$zipfile"""," -o""$output""");
[string]$stopparser = '--%';
& $pathtoexe $stopparser $formatted;
}
Using the excellent Process Explorer from the Windows Sysinternals suite I was able to observe some very interesting behavior. I simplified your command line a little as seen below:
dir -Path $dir -Filter *.zip |
select FullName |
% { & 7za.exe e $_ "-o$dir\tmp" }
This was actually invoking the following command line according to Process Explorer:
C:\temp\7za.exe #{FullName="C:\temp\test.zip"} -oC:\temp\test
Telling PowerShell to expand the FullName property forces it out of the hashmap and treats it as a regular string which 7-Zip can deal with:
dir -Path $dir -Filter *.zip |
select -ExpandProperty FullName |
% { & 7za.exe e $_ "-o$dir\tmp" }
There may still be other issues like dealing with spaces in file names that I really didn't consider or account for, but I thought it was worth adding a note that PowerShell (v2 in this case) wasn't quite passing the parameters as you might expect.

Powershell Strip all directory paths from file

OK what I am trying to do is make a script that will read each line of a text file, "directory.txt" and export every line that is to a file and not to a directory. Example below.
I'm just trying to remove the paths to directories like "C:\users\"
and keep any path that is to a file like "C:\users\file.txt"
In the test file, Direcory.txt" there will be the following:
C:\path\path\folder\
C:\path\path\file.ext
C:\path\path\path\path\folder
The script will need to read the text file above and export the following line to a new text file.
C:\path\path\file.ext
The batch script equivalent would be the following:
#ECHO OFF
FOR /F %%A IN (directory.txt) DO CALL:NoDir "%%A"
pause
EXIT /B
:NoDir
IF "%~x1" NEQ "" ECHO %~1>>nodir.txt
EXIT /B
Batch script can't handle a file of 400mb so need to use powershell to do it o.0
FTR: The condition if "%~x1" neq "" does not do what you seem to expect. It will match not only folders, but also files without an extension.
Anyway, in PowerShell you'd probably do something like this to list only items that are not directories:
Get-Content \PATH\TO\directory.txt `
| Get-Item `
| Where-Object { -not $_.PSIsContainer } `
| Select-Object FullName
I'm not sure I understand the question (batch example shows all paths), maybe that's what you're looking for:
Get-Content directory.txt | Where-Object {$_ -eq 'C:\path\path\file.ext'}
You didn't say whether you're concerned if they are valid paths or not. The following outputs to the host the lines that are either valid files (using the '-PathType leaf' parameter of the Test-Path cmdlet), or if the last 4 characters of the last item in the path are a dot followed by 3 letters.
$Lines = Get-Content C:\Path\to\file.txt
foreach ( $line in $Lines )
{
if ( (Test-Path $line -PathType leaf) -OR ($line -match "\.\w{3}$") )
{
Write-Host $line
}
}
If you find your file extensions are longer than 3 letters, you can change the regex appropriately ("\.\w{3,4}$" for 4 character extensions, or "\.[\w\d]{3,4}$ to match extensions that are 3 or 4 characters long and might include numbers)
And the one-liner:
Get-Content C:\Path\to\file.txt | % { if ((Test-Path $_ -PathType Leaf) -OR ($_ -match "\.\w{3}$")) { $_ } }