diff --git a/doc/manual/src/command-ref/nix-store.md b/doc/manual/src/command-ref/nix-store.md index 31fdd7806..17cbd0461 100644 --- a/doc/manual/src/command-ref/nix-store.md +++ b/doc/manual/src/command-ref/nix-store.md @@ -54,6 +54,11 @@ have an effect. created by sequentially numbering symlinks beyond the first one (e.g., `foo`, `foo-2`, `foo-3`, and so on). + - [`--stdin`](#opt-stdin) + + Read *paths…* from the standard input. + Useful for chaining nix-store commands. + # Operation `--realise` ## Synopsis diff --git a/doc/manual/src/release-notes/rl-next.md b/doc/manual/src/release-notes/rl-next.md index 091d12b7e..c78b20eed 100644 --- a/doc/manual/src/release-notes/rl-next.md +++ b/doc/manual/src/release-notes/rl-next.md @@ -1,5 +1,9 @@ # Release X.Y (202?-??-??) +* Commands which take installables on the command line can now read them from the standard input if + passed the `--stdin` flag. This is primarily useful when you have a large amount of paths which + exceed the OS arg limit. + * The special handling of an [installable](../command-ref/new-cli/nix.md#installables) with `.drv` suffix being interpreted as all of the given [store derivation](../glossary.md#gloss-store-derivation)'s output paths is removed, and instead taken as the literal store path that it represents. The new `^` syntax for store paths introduced in Nix 2.13 allows explicitly referencing output paths of a derivation. @@ -15,4 +19,4 @@ ```shell-session $ nix path-info /nix/store/gzaflydcr6sb3567hap9q6srzx8ggdgg-glibc-2.33-78.drv^* ``` - provides information about each of its outputs. + provides information about each of its outputs. \ No newline at end of file diff --git a/src/libcmd/command.hh b/src/libcmd/command.hh index b6d554aab..49c7b4f9b 100644 --- a/src/libcmd/command.hh +++ b/src/libcmd/command.hh @@ -128,6 +128,8 @@ struct InstallablesCommand : virtual Args, SourceExprCommand virtual bool useDefaultInstallables() { return true; } + bool readFromStdIn; + std::vector getFlakesForCompletion() override; protected: diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 90f001902..7d444aac0 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -694,6 +694,13 @@ StorePathSet Installable::toDerivations( InstallablesCommand::InstallablesCommand() { + + addFlag({ + .longName = "stdin", + .description = "Read installables from the standard input.", + .handler = {&readFromStdIn, true} + }); + expectArgs({ .label = "installables", .handler = {&_installables}, @@ -710,10 +717,18 @@ void InstallablesCommand::prepare() Installables InstallablesCommand::load() { - if (_installables.empty() && useDefaultInstallables()) + if (_installables.empty() && useDefaultInstallables() && !readFromStdIn) // FIXME: commands like "nix profile install" should not have a // default, probably. _installables.push_back("."); + + if (readFromStdIn && !isatty(STDIN_FILENO)) { + std::string word; + while (std::cin >> word) { + _installables.emplace_back(std::move(word)); + } + } + return parseInstallables(getStore(), _installables); } diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index 3bbefedbe..d4218550a 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -1020,6 +1020,7 @@ static int main_nix_store(int argc, char * * argv) { Strings opFlags, opArgs; Operation op = 0; + bool readFromStdIn; parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) { Operation oldOp = op; @@ -1078,6 +1079,8 @@ static int main_nix_store(int argc, char * * argv) op = opGenerateBinaryCacheKey; else if (*arg == "--add-root") gcRoot = absPath(getArg(*arg, arg, end)); + else if (*arg == "--stdin" && !isatty(STDIN_FILENO)) + readFromStdIn = true; else if (*arg == "--indirect") ; else if (*arg == "--no-output") @@ -1090,6 +1093,13 @@ static int main_nix_store(int argc, char * * argv) else opArgs.push_back(*arg); + if (readFromStdIn && op != opImport && op != opRestore && op != opServe) { + std::string word; + while (std::cin >> word) { + opArgs.emplace_back(std::move(word)); + }; + } + if (oldOp && oldOp != op) throw UsageError("only one operation may be specified");