Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions brush-core/src/shell/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,14 @@ impl<SE: crate::extensions::ShellExtensions> crate::Shell<SE> {
path: impl AsRef<Path>,
params: &ExecutionParameters,
) -> Result<openfiles::OpenFile, std::io::Error> {
// Give platform-specific code a chance to handle special files
// (e.g. /dev/null on Windows, which needs to open NUL instead).
// This is checked before absolute_path so that paths like /dev/null
// are intercepted on platforms where they aren't valid native paths.
if let Some(result) = crate::sys::fs::try_open_special_file(path.as_ref()) {
return result.map(openfiles::OpenFile::from);
}

let path_to_open = self.absolute_path(path.as_ref());

// See if this is a reference to a file descriptor, in which case the actual
Expand Down
9 changes: 9 additions & 0 deletions brush-core/src/sys/stubs/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,12 @@ pub fn get_default_standard_utils_paths() -> Vec<std::path::PathBuf> {
pub fn open_null_file() -> Result<std::fs::File, error::Error> {
Err(error::ErrorKind::NotSupportedOnThisPlatform("opening null file").into())
}

/// Gives the platform an opportunity to handle a special file path (e.g. `/dev/null`).
//
// This is a stub implementation that returns no result.
pub fn try_open_special_file(
_path: &std::path::Path,
) -> Option<Result<std::fs::File, std::io::Error>> {
None
}
5 changes: 5 additions & 0 deletions brush-core/src/sys/unix/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,3 +191,8 @@ pub fn open_null_file() -> Result<std::fs::File, error::Error> {

Ok(f)
}

/// Gives the platform an opportunity to handle a special file path (e.g. `/dev/null`).
pub const fn try_open_special_file(_path: &Path) -> Option<Result<std::fs::File, std::io::Error>> {
None
}
22 changes: 22 additions & 0 deletions brush-core/src/sys/windows/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,31 @@

pub use crate::sys::stubs::fs::*;

use std::path::Path;

use crate::error;

/// Splits a platform-specific PATH-like value into individual paths.
///
/// On Windows, this delegates to [`std::env::split_paths`].
pub fn split_paths<T: AsRef<std::ffi::OsStr> + ?Sized>(s: &T) -> std::env::SplitPaths<'_> {
std::env::split_paths(s)
}

/// Opens a null file that will discard all I/O.
pub fn open_null_file() -> Result<std::fs::File, error::Error> {
let f = std::fs::File::options()
.read(true)
.write(true)
.open("NUL")?;
Ok(f)
}

/// Gives the platform an opportunity to handle a special file path (e.g. `/dev/null`).
pub fn try_open_special_file(path: &Path) -> Option<Result<std::fs::File, std::io::Error>> {
if path == Path::new("/dev/null") {
Some(open_null_file().map_err(std::io::Error::other))
} else {
None
}
}
26 changes: 26 additions & 0 deletions brush-shell/tests/cases/compat/redirection.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,32 @@ cases:
echo "Done."
echo "${var}"

- name: "Input redirection from /dev/null"
stdin: |
cat < /dev/null
echo "exit: $?"

- name: "Redirect stderr to /dev/null"
stdin: |
ls /non-existent-path 2>/dev/null
echo "exit: $?"

- name: "Append redirection to /dev/null"
stdin: |
echo hi >>/dev/null
echo "exit: $?"

- name: "Redirect to /dev/null via variable"
stdin: |
target=/dev/null
echo hi >"$target"
echo "exit: $?"

- name: "Read and write /dev/null"
stdin: |
cat <>/dev/null
echo "exit: $?"

- name: "Redirect stdout and stderr"
stdin: |
ls -d . non-existent-dir &>/dev/null
Expand Down
Loading