Delete directory and their contents and log to custom function - powershell

Assume the following directory tree:
NATIVES
\VOL001
file1.txt
file2.txt
\VOL002
file3.txt
\OTHERDIR
file4.txt
I am trying to delete all folder VOL* folders and their children (only files for now) and log the results. Removing the folders and files is easy:
# Remove textfiles
Remove-Item -path C:\Sandbox\NATIVES\VOL* -Recurse
But I am unable to log this as-is. I am using a custom log Function:
Function Log-Write
{
Param([string]$logstring)
Add-Content "log.txt" -value $logstring
}
How can I use it to fill log.txt with every file and folder that has been deleted? So for instance:
Delete file C:\Sandbox\NATIVES\VOL001\file1.txt
Delete file C:\Sandbox\NATIVES\VOL001\file2.txt
Delete dir C:\Sandbox\NATIVES\VOL001\
Delete file C:\Sandbox\NATIVES\VOL002\file3.txt
...
I could write a bigger loop, but I am trying to keep the code as concise as possible (it's more of a learning thing).
Can anyone send me in the right direction?

Use foreach (aka %) instead of recursive Remove-Item. It is possible to redirect Remove-Item's -Verbose output to a file, but that requires very cryptic syntax unless you are on Powerhell 3.0 or higher.
The foreach way looks like so,
$files = #(gci -recurse c:\path\to\files)
$files | % {
rm $_.FullName
add-content c:\myLogFile.txt "Delete file $_.FullName"
}

Related

Powershell. Combine text files in folders across multiple directories

I have tried to do my research, but I can't fathom this one out.
I can combine multiple .txt files in a folder. no problem:
dir C:\Users\XXXX\AC1\22JUN *.txt | get-content | out-file C:\Users\XXXX\22JUN\AC1_22JUN.txt
however, I have 14 Directories each with subdirectories. (months of the year), and this will only ever grow. How can I write it so that it will go into each directory AC1 - AC14 and then look into each folder JAN-DEC and in each subdirectory create a combined file for AC1_22JUN, AC2_22JUN AC1_22JUL, AC2_22JUL and so on and so on?
is there also a way to rename the output file with data, such as the number of .txt files that have been combined. i.e. AC1_22JUN_314.txt
many thanks in advance
What you need to do is iterate over all your directories and their subdirectories and run a particular command in each of them. This is easy enough to achieve using the cmdlet Get-ChildItem and a pair of nested foreach loops.
In addition, you need to count the number of text files you've processed so that you can name your aggregate file appropriately. To do this you can break your pipeline using the temporary variable $files. You can later begin a new pipeline with this variable and use its count property to name the aggregate file.
The code for this is as follows:
$dirs = Get-ChildItem -Directory
foreach ($dir in $dirs)
{
$subdirs = Get-ChildItem $dir -Directory
foreach ($subdir in $subdirs)
{
$files = Get-ChildItem *.txt -Path $subdir.fullname
$name = "$($dir.name)_$($subdir.name)_$($files.count).txt"
$files | Get-Content | Out-File "$($subdir.fullname)/$name"
}
}
A few things to note:
The script needs to be run from the containing folder - in your case the parent folder for AC1-AC14. To run it from elsewhere you will have to change the first statement into something like $dirs = Get-ChildItem C:\path\to\container -Directory
Get-ChildItem is the same as the command dir. dir is an alias for Get-ChildItem. This is NOT the same as the variable $dir which I've used in the script.
Running the script multiple times will include any and all of your old aggregate files! This is because the output file is also a .txt file which is caught in your wildcard search. Consider refining the search criteria for Get-ChildItem, or save the output elsewhere.

How do I move a selection of files into several different directories in one loop?

First time toying seriously with Powershell. I'm running into the problem that my little loop doesn't do what I want it to; it creates a list of series names from the files found in a directory and creates the directories needed to hold the files.
[TAG]Series first.txt
[TAG]Series something else.txt
File.jpg
etc.
This should be sorted into
Series first [Sometag]\[TAG]Series first.txt
Series something else [Sometag]\[TAG]Series something else.txt
File.jpg
etc.
But I can't get Move-Item to actually move the files into the new directories. It leads to extensionless files or errors stating that the file (directory) already exists.
$details = ' [Sometag]'
$series = Get-ChildItem . -Name -File -Filter *.txt |
% {$_.Replace("[TAG]", "").Split("-")[0].Trim()} |
Get-Unique
$series | ForEach-Object {
New-Item -ErrorAction Ignore -Name $_$details -ItemType Directory
}
This is the command that should move the files that it finds using the $serie collection into the directories previously created.
foreach ($serie in $series) {
Get-ChildItem -File -Filter *$serie*.txt |
Move-Item -Destination '.\$serie$details'
}
Which results in it complaining that the file already exists. What would be the best way to deal with this, and can I optimize the first two lines?

Get Relative paths of all the folders, sub-folders and files in a folder

I have a folder named source. It's structure is like the following.
source\a.jpg
source\b.jpg
source\c.xml
source\d.ps1
source\subdir1\a.xml
source\subdir2\b.png
source\subdir3\subsubdir1\nothing.img
I want to list all the relative paths of folders, sub-folders and files in a text file say, out.txt. For above the output I expect is:
source\a.jpg
source\b.jpg
source\c.xml
source\d.ps1
source\subdir1\a.xml
source\subdir2\b.png
source\subdir3\subsubdir1\nothing.img
source\subdir1
source\subdir2
source\subdir3
source\subdir3\subsubdir1
You can see that the output includes individual folders and sub-folders too.
Note: I am in a folder just outside the source folder. I mean for example I am in fold folder which contains source folder -> fold/source but if your solution includes putting the script inside the source folder, thats fine too. Both solutions are fine. This may be easy but I am not familiar with powershell but can at least run scripts from it if given.
EDIT1: Okay, in the "duplicate question" the answer is for relative paths of individual files. But I also want the folders and sub-folders.
EDIT2: Okay, I wrote a command:
(gci -path source -recurse *.|Resolve-path -relative) -replace "","" | out-file -FilePath output.txt -Encoding ascii
Now, this command gives me the relative name of only subdirectory inside the source(in the actual source folder of mine with a different name; source is a dummy name obviously!). What should I change in this code to get other names of files inside the subdirectory in source.
This is clean one line answer,no loops on my part to write, which does the job perfectly. I produced it by luck "playing around" a bit.
(gci -Path source -Recurse *.*|Resolve-Path -Relative) -replace "\.","" |
Out-File -FilePath output.txt -Encoding ascii
Not very clean but this might be an option.
(Get-ChildItem -Recurse -Path "C:\ABC\source\").FullName|ForEach{[Regex]::Replace($_,'^C:\\ABC\\','')}
I would probably do something like this:
$source = 'C:\path\to\source'
$parent = [IO.Path]::GetDirectoryName($source) -replace '\\+$'
$pattern = '^' + [regex]::Escape($parent) + '\\'
Get-ChildItem -Path $source -Recurse | % { $_.FullName -replace $pattern }

Rename first 20 characters of every filename in a file

I am trying to write a script in powershell to remove the first 20 characters of every MP3 filename in a folder, I have created a file 'test.ps' and inserted the powershell code below into it,
gci *.mp3 | rename-item -newname { [string]($_.name).substring(20) }
When I run this file in powershell.exe nothing happens,
Can anyone help? Thanks.
This may get you started. (There are probably much more concise ways, but this works and is readable when you need to maintain it later. :-) )
I created a folder C:\TempFiles, and created the following files in that folder:
TestFile1.txt
TestFile2.txt
TestFile3.txt
TestFile4.txt
(I created them the old-fashioned way, I'm afraid. <g>. I used
for /l %i in (1,1,4) do echo "Testing" > TestFile%i.txt
from an actual command prompt.)
I then opened PowerShell ISE from the start menu, and ran this script. It creates an array ($files), containing only the names of the files, and processes each of them:
cd \TempFiles
$files = gci -name *.txt
foreach ($file in $files) {
$thename = $file.substring(4);
rename-item -path c:\TempFiles\$file -newname $thename
}
This left the folder containing:
File1.Txt
File2.Txt
File3.Txt
File4.Txt
File5.Txt
In order to run a script from the command line, you need to change some default Windows security settings. You can find out about them by using PowerShell ISE's help file (from the menu) and searching for about_scripts or by executing help about_scripts from the ISE prompt. See the sub-section How To Run A Script in the help file (it's much easier to read).
Your code actually works. Two things...
Rename the file to test.ps1.
Run it in the folder you have your MP3 files in. Since you didn't provided a path to Get-ChildItem it will run in the current directory.
I tested your line by making a bunch of mp3 files like this -
1..30 | % { new-item -itemtype file -Name (
$_.ToString().PadLeft(30, 'A') + ".mp3" )}
I would use a more "safer" way (you'll get an error if the file name is shorter than the length in question, you are also targeting the file extension as a part of the total characters). Check if the base name of each file is greater than 21 characters (if you remove the first 20 it can be still have a name with one character long). It can fail if the directory contains a file with same name after you removed the first 20, you can develop it further on your own):
gci *.mp3 | foreach{
if($_.BaseName.Length -ge 21)
{
$ext = $_.Extension
$BaseName = $_.BaseName.Substring(20)
Rename-Item $_ -NewName "$BaseName$ext"
}
}
// delete (replace with empty char) first 20 charters in all filename witch is started with "dbo."
// powershell
Get-ChildItem C:\my_dir\dbo -Recurse -Force -Filter dbo.* | Where-Object {!$_.PSIsContainer} | Rename-Item -NewName { ($_.name).Substring(20) }

PowerShell: how to copy only selected files from source directory?

I'm a Powershell newbie, trying to get a simple script to run.
I have a list of files that I want to copy from some src_dir to a dst_dir. I wrote a simple script (which is obviously wrong since it didnt do anything when I executed it).
Could someone please help check to see what I'm doing wrong?
# source and destionation directory
$src_dir = "C:\Users\Pac\Desktop\C4new"
$dst_dir = "C:\Users\Pac\Desktop\csci578-hw3\Prism\C4new"
# list of files from source directory that I want to copy to destination folder
# unconditionally
$file_list = "C4newArchitecture.java", "CustomerData.java"
# Copy each file unconditionally (regardless of whether or not the file is there
for($i=0; $i -le $file_list.Length - 1; $i++)
{
Copy-Item -path $src_dir+$file_list[$i] -dest $dst_dir -force
}
Assuming that the files are directly unde $src_dir you can do this a bit more simply ie the copy can be a one-liner:
$file_list | Copy-Item -Path {Join-Path $src_dir $_} -Dest $dst_dir -ea 0 -Whatif
-ea is the alias for the -ErrorAction parameter and 0 value corresponds to SilentlyContinue. This causes the Copy-Item to ignore errors like you would get if one of the source files didn't exist. However, if you are running into problems temporarily remove this parameter so you can see the error messages.
When typing this stuff interactively I tend to use shortcuts like this but in scripts it is better to spell it out for readability. Also notice that the -Path parameter can take a scriptblock i.e. script in curly braces. Technically the Copy-Item cmdlet doesn't ever see the scriptblock, just the results of its execution. This works in general for any parameter that takes pipeline input. Remove the -WhatIf to have the command actually execute.
Oh..that was actually pretty easy:
# source and destionation directory
$src_dir = "C:\Users\Pac\Desktop\C4new\"
$dst_dir = "C:\Users\Pac\Desktop\csci578-hw3\Prism\C4new"
# list of files from source directory that I want to copy to destination folder
# unconditionally
$file_list = "C4newArchitecture.java",
"CustomerData.java",
"QuickLocalTransState.java",
"QuickLocalTransState_AbstractImplementation.java",
"SaveSessionOK.java",
"SessionID.java",
"UserInterface.java",
"UserInterface_AbstractImplementation.java"
# Copy each file unconditionally (regardless of whether or not the file is there
foreach ($file in $file_list)
{
Copy-Item $src_dir$file $dst_dir
}
As a programmer, I love the foreach!