diff --git a/CHANGELOG.md b/CHANGELOG.md
index ba79c63..d3d3f56 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
### v0.1.2
+* New **dirname** command #6
* New **canonicalize** command #21
* New **basename** command #5
* New **mkdir** command #13
diff --git a/Makefile.toml b/Makefile.toml
index 2b3086d..077bedb 100644
--- a/Makefile.toml
+++ b/Makefile.toml
@@ -16,7 +16,7 @@ additional_profiles = [
[tasks.generate-sdk-docs]
workspace = false
command = "cargo"
-args = [ "run", "--bin", "duckscript", "--", "./sdkdocs.ds", ]
+args = [ "run", "--bin", "duckscript", "--", "./sdkdocs.ds" ]
[tasks.generate-readme]
script = [
diff --git a/docs/sdk.md b/docs/sdk.md
index 2d44558..87687c3 100644
--- a/docs/sdk.md
+++ b/docs/sdk.md
@@ -18,6 +18,7 @@
* [sdk::fs::CreateDirectory](#sdk__fs__CreateDirectory)
* [sdk::fs::GetCanonicalPath](#sdk__fs__GetCanonicalPath)
* [sdk::fs::GetFileName](#sdk__fs__GetFileName)
+* [sdk::fs::GetParentDirectory](#sdk__fs__GetParentDirectory)
* [sdk::fs::Print](#sdk__fs__Print)
* [sdk::fs::Read](#sdk__fs__Read)
* [sdk::fs::Write](#sdk__fs__Write)
@@ -819,6 +820,33 @@ file = basename ./dir/file.txt
#### Aliases:
basename
+
+## sdk::fs::GetParentDirectory
+```sh
+var = dirname path
+```
+
+This command will return the parent path of the provided path.
+If the parent path is empty, it will return none.
+
+#### Parameters
+
+The path to extract the parent path from.
+
+#### Return Value
+
+The parent path or none.
+
+#### Examples
+
+```sh
+directory = dirname ./dir/file.txt
+```
+
+
+#### Aliases:
+dirname
+
## sdk::fs::Print
```sh
diff --git a/duckscript_sdk/src/sdk/std/fs/dirname/help.md b/duckscript_sdk/src/sdk/std/fs/dirname/help.md
new file mode 100644
index 0000000..f56c995
--- /dev/null
+++ b/duckscript_sdk/src/sdk/std/fs/dirname/help.md
@@ -0,0 +1,20 @@
+```sh
+var = dirname path
+```
+
+This command will return the parent path of the provided path.
+If the parent path is empty, it will return none.
+
+#### Parameters
+
+The path to extract the parent path from.
+
+#### Return Value
+
+The parent path or none.
+
+#### Examples
+
+```sh
+directory = dirname ./dir/file.txt
+```
diff --git a/duckscript_sdk/src/sdk/std/fs/dirname/mod.rs b/duckscript_sdk/src/sdk/std/fs/dirname/mod.rs
new file mode 100755
index 0000000..3a86778
--- /dev/null
+++ b/duckscript_sdk/src/sdk/std/fs/dirname/mod.rs
@@ -0,0 +1,39 @@
+use crate::utils::{io, pckg};
+use duckscript::types::command::{Command, CommandResult};
+
+#[cfg(test)]
+#[path = "./mod_test.rs"]
+mod mod_test;
+
+struct CommandImpl {
+ package: String,
+}
+
+impl Command for CommandImpl {
+ fn name(&self) -> String {
+ pckg::concat(&self.package, "GetParentDirectory")
+ }
+
+ fn aliases(&self) -> Vec {
+ vec!["dirname".to_string()]
+ }
+
+ fn help(&self) -> String {
+ include_str!("help.md").to_string()
+ }
+
+ fn run(&self, arguments: Vec) -> CommandResult {
+ if arguments.is_empty() {
+ CommandResult::Error("Path not provided.".to_string())
+ } else {
+ let parent_path = io::get_parent_directory_name(&arguments[0]);
+ CommandResult::Continue(parent_path)
+ }
+ }
+}
+
+pub(crate) fn create(package: &str) -> Box {
+ Box::new(CommandImpl {
+ package: package.to_string(),
+ })
+}
diff --git a/duckscript_sdk/src/sdk/std/fs/dirname/mod_test.rs b/duckscript_sdk/src/sdk/std/fs/dirname/mod_test.rs
new file mode 100644
index 0000000..84fc059
--- /dev/null
+++ b/duckscript_sdk/src/sdk/std/fs/dirname/mod_test.rs
@@ -0,0 +1,31 @@
+use super::*;
+use crate::test;
+use crate::test::CommandValidation;
+
+#[test]
+fn common_functions() {
+ test::test_common_command_functions(create(""));
+}
+
+#[test]
+fn run_no_path_provided() {
+ test::run_script_and_fail(vec![create("")], "dirname");
+}
+
+#[test]
+fn run_provided() {
+ test::run_script_and_validate(
+ vec![create("")],
+ "out = dirname ./target/_duckscript/file.txt",
+ CommandValidation::Match("out".to_string(), "./target/_duckscript".to_string()),
+ );
+}
+
+#[test]
+fn run_file_without_directory_provided() {
+ test::run_script_and_validate(
+ vec![create("")],
+ "out = dirname file.txt",
+ CommandValidation::None,
+ );
+}
diff --git a/duckscript_sdk/src/sdk/std/fs/mod.rs b/duckscript_sdk/src/sdk/std/fs/mod.rs
index b2f25b9..de1f9e9 100755
--- a/duckscript_sdk/src/sdk/std/fs/mod.rs
+++ b/duckscript_sdk/src/sdk/std/fs/mod.rs
@@ -1,5 +1,6 @@
mod basename;
mod canonical;
+mod dirname;
mod mkdir;
mod print;
mod read;
@@ -16,6 +17,7 @@ pub(crate) fn load(commands: &mut Commands, parent: &str) -> Result<(), ScriptEr
commands.set(basename::create(&package))?;
commands.set(canonical::create(&package))?;
+ commands.set(dirname::create(&package))?;
commands.set(mkdir::create(&package))?;
commands.set(print::create(&package))?;
commands.set(read::create(&package))?;
diff --git a/duckscript_sdk/src/utils/io.rs b/duckscript_sdk/src/utils/io.rs
index 9638002..23c2aa0 100644
--- a/duckscript_sdk/src/utils/io.rs
+++ b/duckscript_sdk/src/utils/io.rs
@@ -26,6 +26,24 @@ pub(crate) fn get_base_name(path: &str) -> Option {
}
}
+pub(crate) fn get_parent_directory_name(path: &str) -> Option {
+ let file_path = Path::new(path);
+
+ let directory = file_path.parent();
+ match directory {
+ Some(directory_path) => {
+ let directory = directory_path.to_string_lossy().into_owned();
+
+ if directory.is_empty() {
+ None
+ } else {
+ Some(directory)
+ }
+ }
+ None => None,
+ }
+}
+
pub(crate) fn create_directory(directory: &str) -> Result<(), String> {
let directory_path = Path::new(directory);