Windows/Scripting

From Segfault
Jump to navigation Jump to search

CMD

while loop

@echo off
setlocal enableextensions enabledelayedexpansion
set /a "x = 0"
:while1
    if %x% leq 5 (
        echo %x%
        set /a "x = x + 1"
        goto :while1
    )
endlocal

for loop

for /l %a in (1, 1, 100) do echo %a

Or, when operating on files:

for /f %a in ('dir /b /d') do move %a dir\%a

Move all files in all subdirectories into one single directory:

mkdir single_dir
cd subdir_structure
for /r %d in (*) do move "%d" ..\single_dir

forfiles

There's also forfiles available for Windows Vista and later versions (or via the Windows Resource Kit[1]), that can do things like delete files older than N days[2]:

forfiles /p "c:\what\ever" /s /m *.* /d <number of days> /c "cmd /c del @path"

To expand on that, find recent files greater than 200 MB:[3][4]

$ forfiles /p c:\    /s /d +"08/01/2021" /c "cmd /c if @fsize gtr 209715200 echo @path @fsize @fdate @ftime"
"C:\pagefile.sys"                                                1073741824 8/28/2021 5:03:32 PM
"C:\Users\root\AppData\Local\VirtualStore\Windows\DataStore.edb"  604045312 8/28/2021 5:11:51 PM
"C:\Windows\SoftwareDistribution\DataStore\DataStore.edb"         604045312 8/28/2021 5:19:22 PM
"C:\Windows\winsxs\ManifestCache\702349c5b78f9a04_blobs.bin"      211158729 8/28/2021 5:44:16 PM

/dev/null

echo "hello" > NUL

date/time

$ echo %date%%time%
27.07.2011 4:44:21,50

tee

This really just redirects stdout to a file and then[5] displays it:

dir > a.txt && type a.txt

wmic

Gather version information with wmic[6]:

$ wmic datafile where name='c:\\Program Files\\7-Zip\\7z.exe' get version
Version
9.20.0.0
  • The double backslashes are important.
  • Only single quotes will work, double quotes will produce an error.

Gather installed software packages:[7]

$ wmic softwarefeature get productname,version | find /i "zip"
7-Zip 9.34                      9.34.00.0

PowerShell

After PowerShell is started, find out where our profile[8] is stored, as this varies[9] between Windows versions:

% powershell
PS C:\temp> $profile
C:\Documents and Settings\alice\My Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1

We can now create scripts in Microsoft.PowerShell_profile.ps1 that will get loaded the next time PowerShell starts. E.g., setting a prompt[10] that will carry the current directory:

function prompt
{
   "PS " + $(get-location) + "> "
}

netcat

A very simple netcat substitute, for UDP and TCP:

PS> (New-Object Net.Sockets.UdpClient).Connect("some-host",1234)
PS> (New-Object Net.Sockets.TcpClient).Connect("some-host",1234)

tail

Following a (log) file[11]:

PS> Get-Content file.log -Wait
PS> Get-Content file.log -Wait -Tail 10             # print the last 10 lines, then follow (PowerShell 3 only)

Tail, and search for something:

PS> Get-Content file.log -wait | where { $_ -match "foo" }

rename

Based on an overcomplicated workflow, we had the following problem to solve: rename PDF files based on their file content. In lieu of awk,[12][13] we used a few lines of PowerShell (courtesy of someone else) and pdftotext.exe and arrived at the following:

$confirmation = Read-Host "Press Y to proceed, any other key to cancel!"
if ($confirmation -eq 'y') {
    $files = Get-ChildItem "*.pdf"
    
    foreach ($f in $files){ $newname = 
        ( .\pdftotext.exe $f - | 
        findstr "For:" | 
        %{ $_.Split(' ')[-1];} |
        %{ $_.Split('[()]')[1];} )
    
    $newname = $newname + ".pdf"
    "$f => $newname"
    Rename-Item $f $newname
    }
    pause
}

The Windows installation did not allow[14] to execute the .ps1 file with a double-click, so we needed a small wrapper script (.bat) to help here:

powershell -ExecutionPolicy ByPass -File pdf_rename.ps1

Links

References