是否有海绵的标准替代品来将文件导入自身?

arg*_*m2f 6 pipe tee

我经常想做这样的事情:

 cat file | command > file
Run Code Online (Sandbox Code Playgroud)

(这显然不起作用)。我见过的唯一解决方案是sponge,即

 cat file | command | sponge file
Run Code Online (Sandbox Code Playgroud)

不幸的是,sponge我不可用(我也不能安装它或任何其他包)。

有没有更标准的快速方法来做到这一点,而不必每次都将其分解为多个命令(管道到临时文件,管道返回原始文件,删除临时文件)?tee例如,我尝试过,它似乎有效,但它是一致/安全的解决方案吗?

Kus*_*nda 6

一个 shell 函数替换sponge

mysponge () (
    append=false

    while getopts 'a' opt; do
        case $opt in
            a) append=true ;;
            *) echo error; exit 1
        esac
    done
    shift "$(( OPTIND - 1 ))"

    outfile=$1

    tmpfile=$(mktemp "$(dirname "$outfile")/tmp-sponge.XXXXXXXX") &&
    cat >"$tmpfile" &&
    if "$append"; then
        cat "$tmpfile" >>"$outfile"
    else
        if [ -f "$outfile" ]; then
            chmod --reference="$outfile" "$tmpfile"
        fi
        if [ -f "$outfile" ]; then
            mv "$tmpfile" "$outfile"
        elif [ -n "$outfile" ] && [ ! -e "$outfile" ]; then
            cat "$tmpfile" >"$outfile"
        else
            cat "$tmpfile"
        fi
    fi &&
    rm -f "$tmpfile"
)
Run Code Online (Sandbox Code Playgroud)

myspongeshell 函数将标准输入上的所有可用数据传递到临时文件。

当所有数据都被重定向到临时文件时,收集的数据被复制到由函数的参数命名的文件中。如果不将数据附加到文件中(即-a未使用),并且如果给定的输出文件名指的是现有的常规文件,如果它不存在,则使用mv(如果文件是一个现有的常规文件,尝试chmod首先使用 GNU 将文件模式传输到临时文件)。如果输出不是常规文件(命名管道、标准输出等),则数据输出为cat.

如果命令行上没有给出文件,则收集的数据将发送到标准输出。

最后,临时文件被删除。

函数中的每一步都依赖于上一步的成功完成。如果一个命令失败(它可能包含重要数据),则不会尝试删除临时文件。

如果命名的文件不存在,则将使用用户的默认权限等创建它,并将来自标准输入的数据写入其中。

mktemp实用程序不是标准的,但通常可用。

上述功能模拟物的行为中所描述的手册spongemoreutils包在Debian。


使用tee代替sponge不是一个可行的选择。你说你已经尝试过了,它似乎对你有用。它可能有效,也可能无效。它依赖于管道中命令的启动时间(它们几乎是同时启动的)以及输入数据文件的大小。

下面是一个示例,显示了使用tee不起作用的情况。

原始文件是 200000 字节,但在管道之后,它被截断为 32 KiB(这很可能对应于我系统上的某些缓冲区大小)。

$ yes | head -n 100000 >hello
$ ls -l hello
-rw-r--r--  1 kk  wheel  200000 Jan 10 09:45 hello
Run Code Online (Sandbox Code Playgroud)
$ cat hello | tee hello >/dev/null
$ ls -l hello
-rw-r--r--  1 kk  wheel  32768 Jan 10 09:46 hello
Run Code Online (Sandbox Code Playgroud)