假设我有一个命令foo
接受一个文件并在该文件中执行一些转换(类似于sed -i
)。假设此命令不接受经典的“从标准输入读取、转换、写入标准输出”选项。
有没有某种方法可以“转换”此命令,以便它允许使用 stdin/stdout 工作流程?
我能想到的第一件事是编写某种使用临时文件的包装器。但我想知道是否有某种标准化的“元工具”可以做到这一点(或一些 bash 技巧)。
答案1
虽然有点俗气,但是:
my_temp_file=$(mktemp)
cat > "$my_temp_file"
foo "$my_temp_file"
cat "$my_temp_file"
rm -f "$my_temp_file"
如果不明显的话,这个
- 读取标准输入并将其写入文件,
foo
在临时文件上调用您的程序,- 读取临时文件并将其写入标准输出,并且
- 删除临时文件。
如果要处理的数据太大而无法放入/tmp
.您可以通过使用--tmpdir
选项将mktemp
临时文件放入具有更多可用空间的文件系统中来稍微缓解这一问题。
如今,连接命令&&
非常流行。这么说可能更有意义
my_temp_file=$(mktemp) && {
cat > "$my_temp_file" && {
foo "$my_temp_file"
cat "$my_temp_file"
}
rm -f "$my_temp_file"
}
因为
- 如果
mktemp
失败,你没有临时文件,你也无能为力, - 如果
cat > "$my_temp_file"
失败,则说明您尚未捕获输入,因此您无能为力(除非您仍然想删除临时文件)
如果退出状态foo
对您很重要,请适当处理。
trap
可以选择通过设置一个来执行此操作,rm
即使事情出现问题也可以使其更加防弹。
答案2
就像 G-Man 的答案一样,但封装在一个函数中:
foo_stream() {
local t=$(mktemp) &&
cat - >| "$t" &&
foo "$t" &&
cat "$t" &&
rm "$t"
}
因此,如果 foo 是:(foo() { sed -i 's/.*/& & &/' "$1"; }
将每行“三倍”),那么我们可以按照自己喜欢的方式在管道中使用 foo_stream :
$ seq 10 | foo_stream | tac
10 10 10
9 9 9
8 8 8
7 7 7
6 6 6
5 5 5
4 4 4
3 3 3
2 2 2
1 1 1
注意:我用来set -o noclobber
避免意外覆盖现有文件,>|
重定向可以覆盖它。
答案3
如果你有控制权foo
它不编辑到位但你可以提供一个输入文件和输出文件:
foo() { sed 's/.*/& & &/' "$1" > "$2"; }
然后您可以使用进程替换来表示“输入管道”和“输出管道”。
$ foo <(seq 10) >(tac)
10 10 10
9 9 9
8 8 8
7 7 7
6 6 6
5 5 5
4 4 4
3 3 3
2 2 2
1 1 1