Prefix every file in subdirectories with the folder name - bash

Preferably using bash or PowerShell, I would like to prefix every file in subdirectories of a directory with their respective folder name.
Each folder has a series of PDF forms with generic names. The folders possess the names of the individuals who are to sign the forms, so are to be renamed accordingly.
This PowerShell snippet seems similar to what I'll need,
dir | Rename-Item -NewName {$_.Directory.Name + "_" + $_.Name}
--but I couldn't figure out how to make it apply to all subdirectories.
Thank you deeply in advance for any assistance!

$ tree
.
└── foo
├── bar
│   ├── baz
│   │   └── file
│   └── file
└── file
3 directories, 3 files
$ find . -type f -exec sh -c 'f=${1//\//_}; mv $1 ${1%/*}/${f:2}' _ {} \;
$ tree
.
└── foo
├── bar
│   ├── baz
│   │   └── foo_bar_baz_file
│   └── foo_bar_file
└── foo_file
3 directories, 3 files

You need to use Get-ChildItem (alias dir) and specify parameters Recurse and File at least, so your code will traverse subdirectories and rename files only (not also folder objects)
$rootFolder = 'D:\Test' # the folder where the subfolders storing the PDF files are
(Get-ChildItem -Path $rootFolder -Filter '*.pdf' -File -Recurse) | Rename-Item -NewName {
'{0}_{1}' -f $_.Directory.Name, $_.Name
}
If you run the above several times, then each time the pdf files get their name prefixed with the directory name, so you'll end up with files like foo_foo_foo_file1.pdf.
To prevent that from happening, you can add a Where-Object clause like:
$rootFolder = 'D:\Test'
(Get-ChildItem -Path $rootFolder -Filter '*.pdf' -File -Recurse) |
Where-Object { $_.Name -notmatch "^$($_.Directory.Name)_" } |
Rename-Item -NewName { '{0}_{1}' -f $_.Directory.Name, $_.Name }
Note that the brackets around the Get-ChildItem cmdlet are needed to make sure it does not 'pick up' any file you have renamed earlier in the pipe

Try this:
Get-ChildItem -Recurse | Foreach {
$Parent = Split-Path "$_.Fullname" -Parent
$Name = "$($Parent)" + "_" + "$_.Name"
Renmae-Item -Path "$_.Fullname" -Newname "$Name"
}

Related

Powershell Get-ChildItem wildcard in path?

Can a wildcard be put in a folder name mask? Or is it necessary to run Get-ChildItem twice, one for the direcdtories and a second for the path? Even with the code below, I'm getting no directories returned.
I think this post shows how to do it with the older syntax:
$folderName = "c:\BizTalk\Vendors\*\AS2FilesReceived\"
$folderMask = "$folderName\*.*"
$dirs = Get-ChildItem -Path $folderName -Recurse -Directory
Write-Host "Number of Matching Directories = $($dirs.Count)"
$dirs
#$files = $dirs | %{ Get-ChildItem -Path $folderMask -Filter "*.*" -Exclude "*997*.*" -File} | Where-Object {$_.CreationTime -gt (Get-Date).AddDays(-6)} | Sort-Object LastWriteTime -Descending
Get-ChildItem supports wildcards in the path, but the wildcard applies only to a single folder level, i.e. a pattern C:\foo\*\bar will find folders C:\foo\abc\bar and C:\foo\xyz\bar, but won't find a folder C:\foo\abc\xyz\bar. For the latter you need something like this:
Get-ChildItem 'C:\foo' -Filter 'bar' -Directory -Recurse
If you have just a single variable folder level and want only files from the given path (without recursion) you can do something like this:
Get-ChildItem 'C:\foo\*\bar\*' -File
If you also want all files in subfolders below bar use this:
Get-ChildItem 'C:\foo\*\bar' -File -Recurse
Assuming that you want to find and return info about all files in all AS2FilesReceived directories for each folder under Vendors, then this might help you...
#Find all AS2FilesReceived directories
$dirs= Get-ChildItem -Path 'C:\BizTalk\Vendors\*\AS2FilesReceived\' -Directory
#Return number of directories
"$($dirs.Count) directories found"
#Return list of directory names
$dirs | % {$_.Parent.Name}
#Return the full name of every text file under each directory
$dirs | % {Get-ChildItem -Path $_ *.txt} | % {$_.FullName}
Which returns...
3 directories found
Vendor1
Vendor2
Vendor3
C:\BizTalk\Vendors\Vendor1\AS2FilesReceived\Vendor1AS2File.txt
C:\BizTalk\Vendors\Vendor2\AS2FilesReceived\Vendor2AS2File.txt
C:\BizTalk\Vendors\Vendor3\AS2FilesReceived\Vendor3AS2File.txt
Ultimately, I just wanted the files, not sure if I had a typo or what, but now this works. It's possible I swithced from $folderMask to $folderName, or didn't have the *.* on the end of the $folderMask.
$folderMask = "c:\BizTalk\Vendors\*\AS2FilesReceived\*.*"
$files = Get-ChildItem -Path $folderMask -File
Write-Host "Number of Matching Files = $($files.Count)"

Gather similar files into separate folders based on keywords in the filenames of multi-part archives

I have a folder that contains many rar or zip files. I want put similar files (based on part word in filename if exist) to own folder.by default in parent folder there isn't any folder.maybe in future another part of file added to parent directory so this time it should move file to his own folder instead of create new folder.
For example assume the files are:
Visual_Studio_2015.part1.rar
Visual_Studio_2015.part2.rar
Visual_Studio_2015.part3.rar
SQL-Server-Enterprise-2016-SP1.part1.rar
SQL-Server-Enterprise-2016-SP1.part2.rar
VSCodeSetup x64 1.29.1.rar
Microsoft.Visual.Studio.Ultimate.2012.update.3.part1.rar
Microsoft.Visual.Studio.Ultimate.2012.update.3.part12.rar
after moving,become looks like this:
Parent Directory
├───Visual_Studio_2015
│ ├───Visual_Studio_2015.part1.rar
│ ├───Visual_Studio_2015.part2.rar
│ ├───Visual_Studio_2015.part3.rar
├───VSCodeSetup x64 1.29.1
│ ├───VSCodeSetup x64 1.29.1.rar
├───SQL-Server-Enterprise-2016-SP1
│ ├───SQL-Server-Enterprise-2016-SP1.part1.rar
│ ├───SQL-Server-Enterprise-2016-SP1.part2.rar
├───Microsoft.Visual.Studio.Ultimate.2012.update.3
│ ├───Microsoft.Visual.Studio.Ultimate.2012.update.3.part1.rar
│ ├───Microsoft.Visual.Studio.Ultimate.2012.update.3.part2.rar
i can't use any software or compiled programming language for this problem. sorry for weak English
update:
in powershell somthing like this:
Get-ChildItem -File |
Group-Object { $_.Name -replace '.part.*' } |
ForEach-Object {
$dir = New-Item -Type Directory -Name $_.Name
$_.Group | Move-Item -Destination $dir
}
can separating files that have part in filename but not work for without it, also i must mention that all filename end with .partX (X is a digit) if it is multi parted archives.
If all the files are in one root folder and have the naming convention you specify, then here is one way to move them into appropriate subfolders:
Get-Childitem -path "C:\Test" -File |
ForEach-Object {
if($_.Name -match "^(?<folder>.*)\.part\d+|(?<folder>.*)\.rar$") {
New-Item -Path "$($_.Directory)\$($matches.Folder)" -ItemType Directory -Force | Out-Null
Move-Item -Path $_.FullName -Destination "$($_.Directory)\$($matches.Folder)\$($_.Name)" -Force
}
}
Change the path in Get-Childitem as appropriate. Also, you can modify the paths for New-Item and Move-Item if you want them to be located somewhere else instead of as subfolders of the root directory.
Another way to do it would be this:
$parentFolder = '<THE PARENTFOLDER THAT HOLDS ALL .RAR AND .ZIP FILES>'
# Get all files inside the parent folder with extension '.rar' or '.zip'
# Because '-Filter' only accepts a single string, we need to use a 'Where-Object' clause.
# Another way would be to use the '-Include' parameter on Get-Childitem, but for that to work
# you must either also use '-Recurse' or append '\*' to the $parentfolder like this:
# Get-ChildItem -Path "$parentFolder\*" -File -Include *.rar, *.zip
Get-ChildItem -Path $parentFolder -File | Where-Object { $_.Extension -match '\.(rar|zip)$' } | ForEach-Object {
# create the name of the subfolder by removing the '.partX' from the basename if that exists
$subFolder = Join-Path -Path $parentFolder -ChildPath ($_.BaseName -replace '\.part\d+', '')
# create this subfolder if it does not already exist
if (!(Test-Path -Path $subFolder -PathType Container)) {
New-Item -Path $subFolder -ItemType Directory -Force | Out-Null
}
# move the file to the subfolder
$_ | Move-Item -Destination $subFolder
}

Delete All But The Newest File of Pattern In Each Subdirectory

Requirements:
Iterate through all folders and files in each directory
Delete all files in each subdirectory except for the newest one (contains filename time stamp) and has a specific filename pattern or instead copy the one file in each subdirectory that is the newest and that is preceded with LIVE_DATA. (There are other files in each subdirectory I wish to ignore).
Directory: AISI-301.0000000001006165.2015-08-24_23-57-46-1
File: LIVE_DATA_2015-08-24_23-57-51.413+0000.MCA
How can I iterate through all of the files recursively while doing comparisons between files in each subdirectory only? I don't want to compare all of them together. Just the one's inside each subdirectory.
for $filename in Get-ChildItem -recurse {
for $filename in $directory {
if ($filename > $filename2) {
$temp = $filename
}
$filename2 = $filename1
}
}
This is what I ended up using to rename my file's after the directory names. It appends the file name to the end and the directory name comes first. I hope this helps somebody else that might be searching for this solution.
Get-ChildItem -include *LIVE_DATA_*.MCA -recurse | Rename-Item -NewName { $_.Directory.Name+'_'+$_.Name}
If you want to remove all files LIVE_DATA_* except the most recent one on a per-folder basis you could do something like this:
$root = 'C:\path\to\root\folder'
Get-ChildItem $root -Recurse | ? { $_.PSIsContainer } | ForEach-Object {
Get-ChildItem (Join-Path $_.FullName 'LIVE_DATA_*') |
Sort-Object Name -Desc |
Select-Object -Skip 1 |
Remove-Item -Force
}
Get-ChildItem $root -Recurse | ? { $_.PSIsContainer } lists all subfolders of $root. Then the ForEach-Object runs another Get-ChildItem statement (without recursion) for each subfolder separately. The Join-Path statement builds a wildcard path from the filename pattern and the full path to the folder (C:\path\to\root\folder\sub\folder\LIVE_DATA_*).
Basically the code lists all folders, then processes the files for each individual folder.

File/Directory Path should look like this - Powershell output

I am using a Powershell Script which should create a file that includes the Directory-Order (folder, subfolder, files, etc.):
$path = "golf.de/dgv"
Get-ChildItem -Path $folder -recurse | sort Directory, Name| format-Table -auto $path, Directory, Name | Out-File C:\Users\J.Kammermeier\Desktop\Johannes\testtext.txt
until now the output looks like this
C:\Users\J.Kammermeier\Desktop\Johannes Test-Datei1.txt
C:\Users\J.Kammermeier\Desktop\Johannes Test-Datei2.txt
C:\Users\J.Kammermeier\Desktop\Johannes\Sonstige Datein\Musik WACKEN.txt
but I need it in this order:
.../Johannes Test-Datei1.txt
...Johannes\Sonstige Datein\Musik WACKEN.txt
How to achieve it?
You'll have to mangle the Directory property a bit, using Select-Object and calculated properties:
# Set the path and folder property
$path = "golf.de/dgv"
$folder = "C:\Users\J.Kammermeier\Desktop\Johannes"
# Get the name of the parent folder (the part we want to remove)
$basePath = (Get-Item $folder).Parent.FullName
# Retrieve the files
$files = Get-ChildItem -Path $folder -Recurse
# Select the Name property and then two calculated properties, "Directory" and "Path"
$files = $files |Select-Object #{Name="BaseURL";Expression={"$path"}},
#{Name="Directory";Expression={$_.Directory.FullName.Substring($basePath.Length - 1)}},
Name
# Sort them
$files = $files |Sort-Object Directory, Name
# Formatted output to file
$files | Format-Table -AutoSize | Out-File C:\Users\J.Kammermeier\Desktop\Johannes\testtext.txt
From the details, I guess that you're trying to audit the files for a website, you could combine the Path and Directory properties and fix the back slashes with -replace:
#{Name="URLPath";Expression={"$path/" + $($_.Directory.FullName.Substring($basePath.Length - 1) -replace "\\","/")}}

How to use "find" to prune some directories while keep some subdirectories

There's an example:
#tree
.
├── dir1
│ ├── file1
│ ├── subdir1
│ │ └── file11
│ ├── subdir2
│ │ └── file12
│ ├── subdir3
│ ├── subdir4
│ ├── subdir5
│ └── subdir6
├── dir2
├── dir3
│ └── dir1
│ └── file11
├── dir4
├── dir5
└── dir6
The following command find all files except those under dir1 and dir2.
find -not \( -path './dir1' -prune -o -path './dir2' -prune \)
My question is how to find all files except dir1, dir2 while still search into subdirectory: ./dir/subdir1 ?
I tried some like this, but doesn't work. I don't know what's the right way to combine logical expressions.
find -not \( \
\( -path './dir1' -a -not -path './dir1/subdir1' \) -prune -o \
-path './dir2' -prune \)
Forgot to mention that I just wanna know how to use the logical combination in command find: -o -a -not, etc to achieve this. Using filters (grep,sed,awk) after the output of find works, but beyonds my question. Thanks to everyone has posted their answer.
Excepted Output:
.
./dir6
./dir5
./dir4
./dir3
./dir3/dir1
./dir3/dir1/file11
./dir1/subdir1
./dir1/subdir1/file11
Maybe you can try:
find . \( -not -path './dir1/*' -and -not -path './dir2/*' -or -path './dir1/subdir1/*' \) -type f
Edit:
Considering prune, I think you should use:
find . \( -path './dir1/*' -and -not -path './dir1/subdir1*' -or -path './dir2' \) -prune -or -type f -print
Try this:
find . -type f |sed '/.\/dir[12]\/[^/]*$/d'
man find
-mindepth levels
Do not apply any tests or actions at levels less than levels (a non-negative integer).-min-depth 1 means process all files except the command line arguments.
this is not test!
find dir1 -mindepth N -type f
>tree
.
├── dir1
│   ├── file1
│   ├── subdir1
│   │   └── file11
│   ├── subdir2
│   │   └── file12
│   ├── subdir3
│   ├── subdir4
│   ├── subdir5
│   └── subdir6
├── dir2
├── dir3
│   └── dir1
│   └── file11
├── dir4
├── dir5
└── dir6
13 directories, 4 files
>find | awk '
{
if($1 !~ /^\.\/dir1$/ && $1 !~ /^\.\/dir2$/) {
if ($1 ~ /^\.\/dir1\//) {
if ($1 ~ /^\.\/dir1\/subdir1(\/|$)/) {
print($1)
}
} else if ($1 !~ /\.\/dir2\//) {
print($1)
}
}
} '
.
./dir6
./dir5
./dir4
./dir3
./dir3/dir1
./dir3/dir1/file11
./dir1/subdir1
./dir1/subdir1/file11

Resources