Function:
function scandir_recursive($dir) {
$items = scandir($dir);
foreach($items as $item) {
if ($item == '.' || $item == '..') {
continue;
}
$file = $dir . '/' . $item;
echo "$file<br />";
if (is_dir($file)) {
scandir_recursive($file);
}
}
}
scandir_recursive($path);
Output:
EEAOC/EN/1001/data
EEAOC/EN/1001/data/New Text Document.txt
EEAOC/EN/1001/data/troll.txt
EEAOC/EN/1002/data
EEAOC/EN/1002/data/New Text Document.txt
EEAOC/EN/1002/data/troll.txt
EEAOC/EN/1003/data
EEAOC/EN/1003/data/New Text Document.txt
EEAOC/EN/1003/data/troll.txt
EEAOC/EN/1004/data
EEAOC/EN/1004/data/New Text Document.txt
EEAOC/EN/1004/data/troll.txt
Is it possible to delete those empty directories and keep files?
such deleting EEAOC/EN/1001/data &EEAOC/EN/1002/data& EEAOC/EN/1003/data &EEAOC/EN/1004/data
I want to keep remaining how?
As shown from your function-output, the directories are not empty.
I suppose that the data is of low value and just needs to be backed up. I assume you are running Linux from your choice of a slash rather than a backslash. Naturally directories incur links or inodes, with metadata, which in linux can be checked via
df -hi
The Inode-Ids of files can be shown via the -i flag of ls
ls -i filename
ls -iltr
ls -ltri `echo $HOME`
Inodes take up space, and introduce overhead in file-system operations. So much to the motivation to remove the directories.
PHP's rmdir function to remove directories requires that The directory must be empty, and the relevant permissions must permit this. It is also non-recursive.
Approach 1: flatten filenames and move the files to a backup directory, then remove the empty directories
Approach 2: incrementally archive and remove the directories
#Approach 1
Your choice should depend on how easy and frequent your file access occurs.
function scandir_recursive($dir) {
$items = scandir($dir);
foreach($items as $item) {
if ($item == '.' || $item == '..') {
continue;
}
$file = $dir . '/' . $item;
$fnameflat = $dir . '_' . $item;
echo "$file<br />\n";
if (is_dir($file)) {
scandir_recursive($file);
}
if(is_file($file)){
rename($file, '~/backup/'.$fnameflat);
}
}
}
scandir_recursive($path);
Afterwards use this function by SeniorDev, to unlink files and directories, or run
`exec("rmdir -rf $path")`
This is not recommended.
#Approach 2
Archive the directory using exec("tar -cvzf backup".date().".tar.gz $path"); or using php.
Then remove the directory aferwards, as described in #1.
Related
I'm writing a json-file inside a generated folder. After an hour I want to delete the folder with its content automatically.
I tried:
$dir = "../tmpDir";
$cdir = scandir($dir);
foreach ($cdir as $key => $value)
{
if (!in_array($value,array(".","..")))
{
if (is_dir($dir.'/'.$value))
{
if(filectime($dir.'/'.$value)< (time()-3600))
{ // after 1 hour
$files = glob($dir.'/'.$value); // get all file names
foreach($files as $file)
{ // iterate files
if(is_file($file))
{
unlink($file); // delete file
}
}
rmdir($dir.'/'.$value);
/*destroy the session if the folder is deleted*/
if(isset($_SESSION["dirname"]) && $_SESSION["dirname"] == $value)
{
session_unset(); // unset $_SESSION variable for the run-time
session_destroy(); // destroy session data in storage
}
}
}
}
}
I get: rmdir(../tmpDir/1488268867): Directory not empty in /Applications/MAMP/htdocs/.... on line 46
if I remove the
if(is_file($file))
{
}
I get a permission error
Maybe someone knows why I get this error
It's much easier to use your native O/S to delete the directory when it comes to things like these, so you don't have to write a horrible loop that may have some edge cases that you could miss and then end up deleting things you shouldn't have!
$path = 'your/path/here';
if (PHP_OS !== 'WINDOWS')
{
exec(sprintf('rm -rf %s', $path));
}
else
{
exec(sprintf('rd /s /q %s', $path));
}
Of course, tailor the above to your needs. You can also use the backtick operator if you want to avoid the overhead of a function call (negligible in this case, anyway). It's also probably a good idea to use escape_shell_arg for your $path variable.
For a one-liner:
exec(sprintf((PHP_OS === 'WINDOWS') ? 'rd /s /q %s' : 'rm -rf %s', escape_shell_arg($path)));
Regardless, sometimes it's easier to let the O/S of choice perform file operations.
rmdir() removes directory if u want remove file then you should use unlink() function
Proper aporach would be using DirectoryIterator or glob() and looping through files then deleting them and after you do it you can remove empty directory.
You can also call system command rm -rf direcory_name with exec() or shell_exec()
useful links:
Delete directory with files in it?
How to delete a folder with contents using PHP
PHP: Unlink All Files Within A Directory, and then Deleting That Directory
I've found pretty useful function on php.net that removes hidden files as well
public function delTree($dir) {
$files = array_diff(scandir($dir), array('.','..'));
foreach ($files as $file) {
(is_dir("$dir/$file")) ? delTree("$dir/$file") : unlink("$dir/$file");
}
return rmdir($dir);
}
I have these files in /public_html/ directory :
0832.php
1481.php
2853.php
3471.php
index.php
and I want to move all those XXXX.php (always in 4 digits format) to directory /tmp/, except index.php. how to do it with reg-ex and loop?
Alternatively, how about moving all files (including index.php) first to /tmp/ then later on put only index.php back to /public_html/, which one you think is less CPU consuming?
Last thing, I found this tutorial to move file using PHP: http://www.kavoir.com/2009/04/php-copying-renaming-and-moving-a-file.html
But how to move ALL files in a directory?
You can use FilesystemIterator with RegexIterator
$source = "FULL PATH TO public_html";
$destination = "FULL PATH TO public_html/tmp";
$di = new FilesystemIterator($source, FilesystemIterator::SKIP_DOTS);
$regex = new RegexIterator($di, '/\d{4}\.php$/i');
foreach ( $regex as $file ) {
rename($file, $destination . DIRECTORY_SEPARATOR . $file->getFileName());
}
The best way would be to do it directly via the file system, but if you absolutely have to do it with PHP, something like this should do what you want - you'll have to change the paths so that they are correct, obviously. Note that this assumes that there could be other files in the public_html directory, and so it only get the filenames with 4 numbers.
$d = dir("public_html");
while (false !== ($entry = $d->read())) {
if($entry == '.' || $entry == '..') continue;
if(preg_match("#^\d{4}$#", basename($entry, ".php")) {
// move the file
rename("public_html/".$entry, "/tmp/".$entry));
}
}
$d->close();
in fact - I went to readdir manual page and the fist comment to read is:
loop through folders and sub folders with option to remove specific files.
<?php
function listFolderFiles($dir,$exclude){
$ffs = scandir($dir);
echo '<ul class="ulli">';
foreach($ffs as $ff){
if(is_array($exclude) and !in_array($ff,$exclude)){
if($ff != '.' && $ff != '..'){
if(!is_dir($dir.'/'.$ff)){
echo '<li>'.$ff.'';
} else {
echo '<li>'.$ff;
}
if(is_dir($dir.'/'.$ff)) listFolderFiles($dir.'/'.$ff,$exclude);
echo '</li>';
}
}
}
echo '</ul>';
}
listFolderFiles('.',array('index.php','edit_page.php'));
?>
Regexes are in fact overkill for this, as we only need to do some simple string matching:
$dir = 'the_directory/';
$handle = opendir($dir) or die("Problem opening the directory");
while ($filename = readdir($handle) !== false)
{
//if ($filename != 'index.php' && substr($filename, -3) == '.php')
// I originally thought you only wanted to move php files, but upon
// rereading I think it's not what you really want
// If you don't want to move non-php files, use the line above,
// otherwise the line below
if ($filename != 'index.php')
{
rename($dir . $filename, '/tmp/' . $filename);
}
}
Then for the question:
alternatively, how about moving all files (including index.php) first to /tmp/ then later on put only index.php back to /public_html/, which one you think is less CPU consuming?
It could be done, and it would probably be slightly easier on your CPU. However, there are several reasons why this doesn't matter. First off, you're already doing this in a very inefficient way by doing it through PHP, so you shouldn't really be looking at the strain this puts on your CPU at this point unless you are willing to do it outside PHP. Secondly, that would cause more disk access (especially if the source and destination directory aren't on the same disk or partition) and disk access is much, much slower than your CPU.
I am trying to list files in subdirectories and write these lists into separate text files.
I managed to get the directory and subdirectory listings and even to write all the files into a text file.
I just don't seem to manage to burst out of loops I am creating. I either end up with a single text file or the second+ files include all preceeding subdirectories content as well.
What I need to achieve is:
dir A/AA/a1.txt,a2.txt >> AA.log
dir A/BB/b1.txt,b2.txt >> BB.log
etc.
Hope this makes sense.
I've found the recursiveDirectoryIterator method as described in PHP SPL RecursiveDirectoryIterator RecursiveIteratorIterator retrieving the full tree being great help. I then use a for and a foreach loop to iterate through the directories, to write the text files, but I cannot break them into multiple files.
Most likely you are not filtering out the directories . and .. .
$maindir=opendir('A');
if (!$maindir) die('Cant open directory A');
while (true) {
$dir=readdir($maindir);
if (!$dir) break;
if ($dir=='.') continue;
if ($dir=='..') continue;
if (!is_dir("A/$dir")) continue;
$subdir=opendir("A/$dir");
if (!$subdir) continue;
$fd=fopen("$dir.log",'wb');
if (!$fd) continue;
while (true) {
$file=readdir($subdir);
if (!$file) break;
if (!is_file($file)) continue;
fwrite($fd,file_get_contents("A/$dir/$file");
}
fclose($fd);
}
I thought I'd demonstrate a different way, as this seems like a nice place to use glob.
// Where to start recursing, no trailing slash
$start_folder = './test';
// Where to output files
$output_folder = $start_folder;
chdir($start_folder);
function glob_each_dir ($start_folder, $callback) {
$search_pattern = $start_folder . DIRECTORY_SEPARATOR . '*';
// Get just the folders in an array
$folders = glob($search_pattern, GLOB_ONLYDIR);
// Get just the files: there isn't an ONLYFILES option yet so just diff the
// entire folder contents against the previous array of folders
$files = array_diff(glob($search_pattern), $folders);
// Apply the callback function to the array of files
$callback($start_folder, $files);
if (!empty($folders)) {
// Call this function for every folder found
foreach ($folders as $folder) {
glob_each_dir($folder, $callback);
}
}
}
glob_each_dir('.', function ($folder_name, Array $filelist) {
// Generate a filename from the folder, changing / or \ into _
$output_filename = $_GLOBALS['output_folder']
. trim(strtr(str_replace(__DIR__, '', realpath($folder_name)), DIRECTORY_SEPARATOR, '_'), '_')
. '.txt';
file_put_contents($output_filename, implode(PHP_EOL, $filelist));
});
$value can = a folder structure to the language file. Example: languages/english.php
$value can also = the files name. Example: english.php
So I need to get the current folder that $value is in and delete the folder ONLY if there are no other files/folders within that directory (after deleting the actual file as I am doing already, ofcourse).
foreach($module['languages'] as $lang => $langFile)
{
foreach ($langFile as $type => $value)
{
#unlink($module_path . '/' . $value);
// Now I need to delete the folder ONLY if there are no other directories inside the folder where it is currently at.
// And ONLY if there are NO OTHER files within that folder also.
}
}
How can I do this?? And wondering if this can be done without using a while loop, since a while loop within a foreach loop could take some time, and need this to be as quick as possible.
And just FYI, the $module_path should never be deleted. So if $value = english.php, it should never delete the $module_path. Ofcourse, there will always be another file in there, so checking for this is not necessary, but won't hurt either way.
Thanks guys :)
EDIT
Ok, now I'm using this code here and it is NOT working, it is not removing the folders or the files, and I don't get any errors either... so not sure what the problem is here:
foreach($module['languages'] as $lang => $langFile)
{
foreach ($langFile as $type => $value)
{
if (#unlink($module_path . '/' . $value))
#rmdir(dirname($module_path . '/' . $value));
}
}
NEVERMIND, this works a CHARM!!! Cheers Everyone!!
The easyest way is try to use rmdir. This don't delete folder if it is not empty
rmdir($module_path);
also you can check is folder empty by
if(count(glob($module_path.'*'))<3)//delete
2 for . and ..
UPD: as I reviewed maybe you should replace $module_path by dirname($module_path.'.'.$value);
Since the directory you care about might be part of the $value, you need to use dirname to figure out what the parent directory is, you can't just assume that it's $module_path.
$file_path = $module_path . '/' . $value;
if (#unlink($file_path)) {
#rmdir(dirname($file_path));
}
if (is_file($value)) {
unlink($value);
} else if (is_dir($value)) {
if (count(scandir($value)) == 2) }
unlink($value)
}
}
http://php.net/manual/en/function.is-dir.php
http://www.php.net/manual/en/function.scandir.php
The code below will take a path, check if it is a file (i.e. not a directory). If it is a file, it will extract the directory name, then delete the file, then iterate over the dir and count the files in it, if the files are zero it'll delete the dir.
Code is as an example and should work, however privileges and environment setup may result in it not working.
<?php
if(!is_dir ( string $filename )){ //if it is a file
$fileDir = dirname ( $filename );
if ($handle = opendir($fileDir)) {
echo "Directory handle: $handle\n";
echo "Files:\n";
$numFiles=0;
//delete the file
unlink($myFile);
//Loop the dir and count the file in it
while (false !== ($file = readdir($handle))) {
$numFiles = $numFiles + 1;
}
if($numFiles == 0) {
//delete the dir
rmdir($fileDir);
}
closedir($handle);
}
}
?>
I am using this peice of code:
$target = 'extracted/' . $name[0];
$scan = scandir($target);
To scan the directory of a folder which is used for zip uploads. I want to be able to find all the folders inside my $target folder so I can delete them and their contents, leaving only the files in the $target directory.
Once I have returned the contents of the folder, I don't know how to differentiate between the folders and the files to be able to delete the folders.
Also, I have been told that the rmdir() function can't delete folders which have content inside them, is there any way around this?
Thanks, Ben.
To determine whether or not you have a folder or file use the functions is_dir() and is_file()
For example:
$path = 'extracted/' . $name[0];
$results = scandir($path);
foreach ($results as $result) {
if ($result === '.' or $result === '..') continue;
if (is_dir($path . '/' . $result)) {
//code to use if directory
}
}
Better to use DirectoryIterator
$path = 'extracted'; // '.' for current
foreach (new DirectoryIterator($path) as $file) {
if ($file->isDot()) continue;
if ($file->isDir()) {
print $file->getFilename() . '<br />';
}
}
First off, rmdir() cannot delete a folder with contents. If safe mode is disabled you can use the following.
exec("rm -rf folder/");
Also look at is_dir()/is_file() or even better the PHP SPL.
$directories = scandir('images');
foreach($directories as $directory){
if($directory=='.' or $directory=='..' ){
echo 'dot';
}else{
if(is_dir($directory)){
echo $directory .'<br />';
}
}
}
a simpler and perhaps faster version
scandir will scan the entire directory, you can manually filter.
but if you are lazy like I am, then use glob
$scan = glob($target."/*",GLOB_ONLYDIR);
and it will output an array of all your directories of your target.
To get all the files in all the sub, sub folders
function myfunction($dir){
foreach ($dir as $dirname => $file) {
if(is_dir($file) && $file != '.' && $file != '..' ) {
// echo $file;
$newDir = scandir($file);
myfunction($newDir);
}elseif($file !='.' && $file != '..'){
echo "<br>File name is ---";
echo $file;
}
} // end foreach
} //function ends
$dirpass = scandir($mypath3); // set directory
echo myfunction($dirpass); // pass directory
We will get the result like below (plz ignore file names )
File name is ----->index.PHP
File name is -----> 100000045 Invoices Sales Magento Admin.png
File name is -----> 100000142 Orders Sales Magento Admin(1).png
File name is -----> 100000142 Orders Sales Magento Admin.png
File name is ----->hrc-siberian-tiger-2-jpg_21253111.jpg
File name is ----->images (3rd copy).jpeg
File name is ----->images (4th copy).jpeg
File name is ----->images (5th copy).jpeg
File name is ----->images (another copy).jpeg
File name is ----->images (copy).jpeg
File name is ----->images.jpeg
File name is ----->JPEG_example_JPG_RIP_100.jpg
File name is ----->preload
File name is ----->Stonehenge (3rd copy).jpg
File name is ----->Stonehenge (4th copy).jpg
File name is ----->Stonehenge (5th copy).jpg
File name is ----->Stonehenge (another copy).jpg
File name is ----->Stonehenge (copy).jpg
File name is ----->Stonehenge.jpg
You also wanted to remove items if they were inside that directory. rmdir does not allow you to remove directories containing files. But there is a simple sollution.
array_map('unlink', glob($target.'/*/*'));
array_map('rmdir',glob($target."/*",GLOB_ONLYDIR));
First it will unlink all the files in all sub-directories.
Secondly it will remove all directories, because they contain no files.
If you got sub-sub-directories, then you should add another 2 lines like this:
array_map('unlink', glob($target.'/*/*/*')); //remove sub-sub-files
array_map('rmdir',glob($target."/*/*",GLOB_ONLYDIR)); //remove sub-sub-directories
array_map('unlink', glob($target.'/*/*')); //remove sub-files
array_map('rmdir',glob($target."/*",GLOB_ONLYDIR)); //remove sub-directories
The quick and dirty way:
$folders = glob("<path>/*", GLOB_ONLYDIR);
A more versatile and object-oriented solution, inspired by earlier answers using DirectoryIterator but slightly more concise and general purpose:
$path = '<path>';
$folders = [];
foreach (new \DirectoryIterator($path) as $file)
{
if (!$file->isDot() && $file->isDir())
{
$folders[] = $file;
}
}
here is one function i used mostly to parse archives and directories
function scan_full_dir($dir,$child=false){
$dir_content_list = scandir($dir);
foreach($dir_content_list as $value)
{
if($value === '.' || $value === '..') {continue;}
// check if we have file
if(is_file($dir.'/'.$value)) {
echo '<br> File Name --> '.$value;
continue;
}
// check if we have directory
if (is_dir($dir.'/'.$value)) {
if(!$child){
echo '<br> parent --> '.$value;
}else{
echo '<br> child of '.$child.' --> '.$value;
}
scan_full_dir($dir.'/'.$value,$value);
}
}
}
output sample
parent --> fonts
File Name --> standard
parent --> steps
child of steps --> pcb
File Name --> .attrlist.sum
File Name --> .profile.sum
File Name --> .stephdr.sum
File Name --> attrlist
child of pcb --> netlists
child of netlists --> cadnet
File Name --> .netlist.sum
File Name --> netlist
File Name --> profile
File Name --> stephdr
// to scan dir
scan_full_dir('path/of/myfolder');
// to scan archive without opening it , supported formats : gz, zip, tar and bz files.
scan_full_dir('phar://uploads/youarchive.zip);