如何将字符串传递给需要文件的命令?

如何将字符串传递给需要文件的命令?

假设一个程序cook采用一个参数:包含要烹饪的食物食谱的文本文件的路径名。假设我希望从bash脚本中调用该程序,还假设我已经在字符串变量中包含了配方:

#!/bin/bash

the_recipe="$(cat << EOF
wash cucumbers
wash knife
slice cucumbers
EOF
)"

cook ...  # What should I do here? It expects a file, but I only have a string.

当命令需要文件名参数时,如何将配方传递给命令?

我考虑过创建一个临时文件只是为了传递文件,但我想知道是否有其他方法可以解决这个问题。

答案1

/dev/stdin您可以使用代表标准输入的“假”文件名。

所以执行这个:

echo "$the_recipe" | cook /dev/stdin

echo 命令和管道将指定变量的内容发送到下一个命令的标准输入cook,然后打开标准输入(作为单独的文件描述符)并读取它。

答案2

使用 shell 的进程替换功能的另一种方法是在或bash下创建 FIFO ,或使用命名文件描述符 ( ),具体取决于操作系统。替换语法()被 FIFO 或 FD 的名称替换,并且其中的命令在后台运行。/tmp/var/tmp/dev/fd/*<(cmd)

cook <(printf '%s\n' "${the_recipe}")

该命令cook现在读取变量的内容,就像它在文件中一样。

这也可以用于多个输入文件:

cook <(printf '%s\n' "${the_1st_recipe}") <(printf '%s\n' "${the_2nd_recipe}")

答案3

这取决于应用程序的功能。它可能会或可能不会坚持使用命名文件,也可能会或可能不会坚持使用可查找文件。

匿名管道

有些应用程序只是从任何地方读取输入。通常它们默认从标准输入读取。如果您有字符串形式的数据,并且希望将其传递到应用程序的标准输入,有几种方法。您可以通过管道传递信息:

printf '%s' "$the_recipe" | cook     # passes $the_recipe exactly
printf '%s\n' "$the_recipe" | cook   # passes $the_recipe plus a final newline

不要echo在此处使用,因为根据 shell 的不同,它可能会破坏反斜杠。

如果应用程序坚持使用文件参数,它可能会接受-标准输入。这是一个常见的约定,但并不系统。

printf '%s\n' "$the_recipe" | cook -

如果应用程序需要实际的文件名,则传递/dev/stdin以表示标准输入。

printf '%s\n' "$the_recipe" | cook /dev/stdin

对于某些应用程序来说,这还不够:它们需要具有某些属性的文件名,例如具有给定的扩展名,或者它们需要在创建临时文件的可写目录中的文件名。在这种情况下,您通常需要一个临时文件,尽管有时命名管道就足够了。

命名管道

可以给管道命名。我提到这一点是为了完整性,但这很少是最方便的方法。它确实避免了在磁盘上获取数据。如果应用程序需要一个名称有限制的文件,但不需要该文件可查找,那么它是合适的。

mkfifo named.pipe
printf '%s\n' "$the_recipe" >named.pipe & writer=$!
cook named.pipe
rm named.pipe
wait "$writer"

(省略错误检查。)

临时文件

如果应用程序需要一个可寻找的文件,您需要创建一个临时文件。可查找文件是应用程序可以来回移动、一次读取任意部分的文件。管道不允许这样做:它需要从头到尾按顺序读取,不能倒退。

Bash、ksh 和 zsh 具有方便的语法,可通过临时文件将字符串作为输入传递。请注意,它们总是在字符串中附加一个换行符(即使已经有一个换行符)。

cook <<<"$the_recipe"

使用其他 shell,或者如果您想控制哪个目录包含临时文件,则需要手动创建临时文件。大多数 unice 都提供mktemp公用事业安全地创建临时文件。 (永远不要使用类似的东西… >/tmp/temp.$$!它允许其他程序,甚至以其他用户身份运行,劫持该文件。)这是一个脚本,它创建一个临时文件,并在中断时负责将其删除。

tmp=
trap 'rm -f "$tmp"' EXIT
trap 'rm -f "$tmp"; trap "" HUP; kill -INT $$' HUP
trap 'rm -f "$tmp"; trap "" INT; kill -INT $$' INT
trap 'rm -f "$tmp"; trap "" TERM; kill -INT $$' TERM
tmp=$(mktemp)
printf '%s\n' "$the_recipe" >"$tmp"
cook "$tmp"

要选择创建临时文件的位置,请设置TMPDIR调用的环境变量mktemp。例如,要在当前目录而不是临时文件的默认位置创建临时文件:

tmp=$(TMPDIR="$PWD" mktemp)

(使用$PWD而不是.为您提供绝对文件名,这意味着您可以安全地调用cd脚本,而不必担心$tmp不再指定相同的文件。)

如果应用程序需要具有特定扩展名的文件名,则需要创建一个临时目录并在其中创建文件。要创建临时目录,请使用mktemp -d.TMPDIR如果您想控制临时目录的创建位置,请再次设置环境变量。

tmp=
trap 'rm -rf "$tmp"' EXIT
trap 'rm -rf "$tmp"; trap "" HUP; kill -INT $$' HUP
trap 'rm -rf "$tmp"; trap "" INT; kill -INT $$' INT
trap 'rm -rf "$tmp"; trap "" TERM; kill -INT $$' TERM
tmp=$(mktemp -d)
printf '%s\n' "$the_recipe" >"$tmp/myfile.ext"
cook "$tmp/myfile.ext"

答案4

在尝试更复杂的解决方案之前,请务必检查您的版本是否cook支持参数-(破折号)作为文件名。如果支持此约定,它会告诉cook您从标准输入中读取,如下所示:

echo "$the_recipe" | cook -

或者

cook - <<<"$the_recipe"

相关内容