Skip to content

Commit 9d0aff7

Browse files
committed
Add 'gdal vfs delete'
1 parent 5a8b7d3 commit 9d0aff7

File tree

9 files changed

+280
-0
lines changed

9 files changed

+280
-0
lines changed

apps/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ add_library(
7070
gdalalg_vector_write.cpp
7171
gdalalg_vfs.cpp
7272
gdalalg_vfs_copy.cpp
73+
gdalalg_vfs_delete.cpp
7374
gdalalg_vfs_list.cpp
7475
gdalinfo_lib.cpp
7576
gdalbuildvrt_lib.cpp

apps/gdalalg_vfs.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "gdalalgorithm.h"
1414

1515
#include "gdalalg_vfs_copy.h"
16+
#include "gdalalg_vfs_delete.h"
1617
#include "gdalalg_vfs_list.h"
1718

1819
/************************************************************************/
@@ -30,6 +31,7 @@ class GDALVFSAlgorithm final : public GDALAlgorithm
3031
GDALVFSAlgorithm() : GDALAlgorithm(NAME, DESCRIPTION, HELP_URL)
3132
{
3233
RegisterSubAlgorithm<GDALVFSCopyAlgorithm>();
34+
RegisterSubAlgorithm<GDALVFSDeleteAlgorithm>();
3335
RegisterSubAlgorithm<GDALVFSListAlgorithm>();
3436
}
3537

apps/gdalalg_vfs_delete.cpp

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/******************************************************************************
2+
*
3+
* Project: GDAL
4+
* Purpose: gdal "vfs delete" subcommand
5+
* Author: Even Rouault <even dot rouault at spatialys.com>
6+
*
7+
******************************************************************************
8+
* Deleteright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
9+
*
10+
* SPDX-License-Identifier: MIT
11+
****************************************************************************/
12+
13+
#include "gdalalg_vfs_delete.h"
14+
15+
#include "cpl_conv.h"
16+
#include "cpl_string.h"
17+
#include "cpl_vsi.h"
18+
19+
#include <algorithm>
20+
21+
//! @cond Doxygen_Suppress
22+
23+
#ifndef _
24+
#define _(x) (x)
25+
#endif
26+
27+
/************************************************************************/
28+
/* GDALVFSDeleteAlgorithm::GDALVFSDeleteAlgorithm() */
29+
/************************************************************************/
30+
31+
GDALVFSDeleteAlgorithm::GDALVFSDeleteAlgorithm()
32+
: GDALAlgorithm(NAME, DESCRIPTION, HELP_URL)
33+
{
34+
{
35+
auto &arg = AddArg("filename", 0, _("File or directory name to delete"),
36+
&m_filename)
37+
.SetPositional()
38+
.SetRequired();
39+
SetAutoCompleteFunctionForFilename(arg, 0);
40+
arg.AddValidationAction(
41+
[this]()
42+
{
43+
if (m_filename.empty())
44+
{
45+
ReportError(CE_Failure, CPLE_IllegalArg,
46+
"Filename cannot be empty");
47+
return false;
48+
}
49+
return true;
50+
});
51+
}
52+
53+
AddArg("recursive", 'r', _("Delete directories recursively"), &m_recursive)
54+
.AddShortNameAlias('R');
55+
}
56+
57+
/************************************************************************/
58+
/* GDALVFSDeleteAlgorithm::RunImpl() */
59+
/************************************************************************/
60+
61+
bool GDALVFSDeleteAlgorithm::RunImpl(GDALProgressFunc, void *)
62+
{
63+
bool ret = false;
64+
VSIStatBufL sStat;
65+
if (VSIStatL(m_filename.c_str(), &sStat) != 0)
66+
{
67+
ReportError(CE_Failure, CPLE_FileIO, "%s does not exist",
68+
m_filename.c_str());
69+
}
70+
else
71+
{
72+
if (m_recursive)
73+
{
74+
ret = VSIRmdirRecursive(m_filename.c_str()) == 0;
75+
}
76+
else
77+
{
78+
ret = VSI_ISDIR(sStat.st_mode) ? VSIRmdir(m_filename.c_str()) == 0
79+
: VSIUnlink(m_filename.c_str()) == 0;
80+
}
81+
if (!ret)
82+
{
83+
ReportError(CE_Failure, CPLE_FileIO, "Cannot delete %s",
84+
m_filename.c_str());
85+
}
86+
}
87+
return ret;
88+
}
89+
90+
//! @endcond

apps/gdalalg_vfs_delete.h

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/******************************************************************************
2+
*
3+
* Project: GDAL
4+
* Purpose: gdal "vfs delete" subcommand
5+
* Author: Even Rouault <even dot rouault at spatialys.com>
6+
*
7+
******************************************************************************
8+
* Deleteright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
9+
*
10+
* SPDX-License-Identifier: MIT
11+
****************************************************************************/
12+
13+
#ifndef GDALALG_VFS_DELETE_INCLUDED
14+
#define GDALALG_VFS_DELETE_INCLUDED
15+
16+
#include "gdalalgorithm.h"
17+
18+
//! @cond Doxygen_Suppress
19+
20+
/************************************************************************/
21+
/* GDALVFSDeleteAlgorithm */
22+
/************************************************************************/
23+
24+
class GDALVFSDeleteAlgorithm final : public GDALAlgorithm
25+
{
26+
public:
27+
static constexpr const char *NAME = "delete";
28+
static constexpr const char *DESCRIPTION =
29+
"Delete files located on GDAL Virtual file systems (VSI).";
30+
static constexpr const char *HELP_URL = "/programs/gdal_vfs_delete.html";
31+
32+
static std::vector<std::string> GetAliasesStatic()
33+
{
34+
return {"rm", "rmdir", "del"};
35+
}
36+
37+
GDALVFSDeleteAlgorithm();
38+
39+
private:
40+
std::string m_filename{};
41+
bool m_recursive = false;
42+
43+
bool RunImpl(GDALProgressFunc, void *) override;
44+
};
45+
46+
//! @endcond
47+
48+
#endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#!/usr/bin/env pytest
2+
# -*- coding: utf-8 -*-
3+
###############################################################################
4+
# Project: GDAL/OGR Test Suite
5+
# Purpose: 'gdal vfs delete' testing
6+
# Author: Even Rouault <even dot rouault @ spatialys.com>
7+
#
8+
###############################################################################
9+
# Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
10+
#
11+
# SPDX-License-Identifier: MIT
12+
###############################################################################
13+
14+
import sys
15+
16+
import pytest
17+
18+
from osgeo import gdal
19+
20+
21+
def get_alg():
22+
return gdal.GetGlobalAlgorithmRegistry()["vfs"]["delete"]
23+
24+
25+
def test_gdalalg_vfs_delete_empty_filename():
26+
27+
alg = get_alg()
28+
with pytest.raises(Exception, match="Filename cannot be empty"):
29+
alg["filename"] = ""
30+
31+
32+
def test_gdalalg_vfs_delete_file(tmp_vsimem):
33+
34+
gdal.FileFromMemBuffer(tmp_vsimem / "test", "test")
35+
36+
alg = get_alg()
37+
alg["filename"] = tmp_vsimem / "test"
38+
assert alg.Run()
39+
40+
assert gdal.VSIStatL(tmp_vsimem / "test") is None
41+
42+
43+
def test_gdalalg_vfs_delete_file_not_existing():
44+
45+
alg = get_alg()
46+
alg["filename"] = "/i_do/not/exist"
47+
with pytest.raises(Exception, match="does not exist"):
48+
alg.Run()
49+
50+
51+
def test_gdalalg_vfs_delete_dir(tmp_path):
52+
53+
gdal.Mkdir(tmp_path / "subdir", 0o755)
54+
55+
alg = get_alg()
56+
alg["filename"] = tmp_path / "subdir"
57+
assert alg.Run()
58+
59+
assert gdal.VSIStatL(tmp_path / "subdir") is None
60+
61+
62+
@pytest.mark.skipif(sys.platform == "win32", reason="incompatible platform")
63+
def test_gdalalg_vfs_delete_file_failed():
64+
65+
alg = get_alg()
66+
alg["filename"] = "/dev/null"
67+
with pytest.raises(Exception, match="Cannot delete /dev/null"):
68+
alg.Run()
69+
70+
71+
def test_gdalalg_vfs_delete_dir_recursive(tmp_path):
72+
73+
gdal.Mkdir(tmp_path / "subdir", 0o755)
74+
open(tmp_path / "subdir" / "file", "wb").close()
75+
76+
alg = get_alg()
77+
alg["filename"] = tmp_path / "subdir"
78+
alg["recursive"] = True
79+
assert alg.Run()
80+
81+
assert gdal.VSIStatL(tmp_path / "subdir") is None

doc/source/conf.py

+7
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,13 @@ def check_python_bindings():
546546
[author_evenr],
547547
1,
548548
),
549+
(
550+
"programs/gdal_vfs_delete",
551+
"gdal-vfs-delete",
552+
"Delete files located on GDAL Virtual file systems (VSI)",
553+
[author_evenr],
554+
1,
555+
),
549556
(
550557
"programs/gdal_vfs_list",
551558
"gdal-vfs-list",

doc/source/programs/gdal_vfs.rst

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ Available sub-commands
2424
----------------------
2525

2626
- :ref:`gdal_vfs_copy_subcommand`
27+
- :ref:`gdal_vfs_delete_subcommand`
2728
- :ref:`gdal_vfs_list_subcommand`
2829

2930
Examples
+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
.. _gdal_vfs_delete_subcommand:
2+
3+
================================================================================
4+
"gdal vfs delete" sub-command
5+
================================================================================
6+
7+
.. versionadded:: 3.11
8+
9+
.. only:: html
10+
11+
Delete files located on GDAL Virtual file systems (VSI)
12+
13+
.. Index:: gdal vfs delete
14+
15+
Synopsis
16+
--------
17+
18+
.. program-output:: gdal vfs delete --help-doc
19+
20+
Description
21+
-----------
22+
23+
:program:`gdal vfs delete` delete files and directories located on :ref:`virtual_file_systems`.
24+
25+
This is the equivalent of the UNIX ``rm`` command, and ``gdal vfs rm`` is an
26+
alias for ``gdal vfs delete``.
27+
28+
.. warning::
29+
30+
Be careful. This command cannot be undone. It can also act on the "real"
31+
file system.
32+
33+
Options
34+
+++++++
35+
36+
.. option:: -r, -R, --recursive
37+
38+
Delete directories recursively.
39+
40+
Examples
41+
--------
42+
43+
.. example::
44+
:title: Delete recursively files from /vsis3/bucket/my_dir
45+
46+
.. code-block:: console
47+
48+
$ gdal vfs delete -r /vsis3/bucket/my_dir

doc/source/programs/index.rst

+2
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ single :program:`gdal` program that accepts commands and subcommands.
8282
gdal_vector_sql
8383
gdal_vfs
8484
gdal_vfs_copy
85+
gdal_vfs_delete
8586
gdal_vfs_list
8687

8788
.. only:: html
@@ -143,6 +144,7 @@ single :program:`gdal` program that accepts commands and subcommands.
143144
- :ref:`gdal_vector_sql_subcommand`: Apply SQL statement(s) to a dataset
144145
- :ref:`gdal_vfs_command`: Entry point for GDAL Virtual file system (VSI) commands
145146
- :ref:`gdal_vfs_copy_subcommand`: Copy files located on GDAL Virtual file systems (VSI)
147+
- :ref:`gdal_vfs_delete_subcommand`: Delete files located on GDAL Virtual file systems (VSI)
146148
- :ref:`gdal_vfs_list_subcommand`: List files of one of the GDAL Virtual file systems (VSI)
147149

148150

0 commit comments

Comments
 (0)