简体   繁体   English

coproc和子流程重定向的替代方法(重击3)

[英]Alternatives to coproc and sub-process redirection (Bash 3)

Okay, so I'm in a situation where I'd really like to be using either a co-process via coproc or via redirection such as <(some command) , but unfortunately I'm limited to bash 3.2 in one of the my target environments, which means I'm limited in what I can do. 好的,所以我真的很想使用通过coproc或通过重定向(例如<(some command)进行协同处理的情况,但是不幸的是,我只能在其中之一中使用bash 3.2。目标环境,这意味着我的工作能力有限。

The reason I need a co-process is that I need to read line-by-line from one file, while looping over another. 我需要协同处理的原因是,我需要从一个文件中逐行读取,同时循环遍历另一个文件。

Currently I'm using exec <6 /foo/bar to create keep a file open for reading so that I can do read line <&6 whenever I need more input. 目前,我正在使用exec <6 /foo/bar创建一个保持打开状态以供读取的文件,以便在需要更多输入时可以read line <&6 This works fine, but it only works on plain-text files, however really I'd like to keep my file(s) compressed, rather than decompressing them before running my script. 这可以正常工作,但仅适用于纯文本文件,但是实际上我想保持文件压缩,而不是在运行脚本之前将其解压缩。

I also need to be able to do the same for writing to a new, compressed file without having to waste space writing in plain-text then compressing afterwards. 我还需要能够做到这一点,以便写入新的压缩文件,而不必浪费空间以纯文本格式编写然后进行压缩。

So… are there any alternatives available in bash 3? 那么……bash 3中有没有其他选择? As I've noted, I'm already in a loop over another file, so I don't have the option of just piping my output into gzip (or piping zcat into my loop) as I need to do this independently of my loop. 正如我已经指出的,我已经在另一个文件的循环中,所以我不能选择仅将输出管道zcatgzip (或将zcat管道zcat到我的循环)中,因为我需要独立于循环执行此操作。

To try to give an example, here's a stripped down version of what I'm doing now: 为了给出一个例子,这是我现在正在做的精简版:

# Decompress compressed match-file
gzip -dc /foo/compressed.gz > /tmp/match

# Setup file handles (to keep files open for reading/writing)
exec 5< /tmp/match
exec 6> /tmp/matches

# Loop over input file (/foo/bar) for matches
read next_match <&5
while read line; do
    if [ "$line" = "$next_match" ]; then
        read next_match <&5
        echo "$line" >&6
    fi

    echo "$line"
done < /foo/bar

# Close file handles
exec <5&-
exec 6>&-
rm /tmp/match

# Compress matches and overwrite old match file
gzip -cf9 /tmp/matches /foo/compressed.gz
rm /tmp/matches

Forgive any typos, and the general uselessness of the actual script, I just wanted to keep it fairly simple. 原谅任何错别字和实际脚本的普遍无用,我只是想使其相当简单。 As you can see, while it works fine, it's not exactly optimal thanks to the wasteful plain-text files. 如您所见,虽然它可以正常工作,但由于浪费了纯文本文件,所以它并不是最佳选择。

You might want to use mknod to create pipes and let gzip write/read in background processes. 您可能要使用mknod创建管道,并让gzip在后台进程中进行写入/读取。 The following seems to work for me: 以下内容似乎对我有用:

#!/bin/bash

# create test files (one character per line)
echo abcdefgh | grep -o . | gzip > /tmp/foo.gz
echo aafbchddjjklsefksi | grep -o . > /tmp/bar

# create pipes for zipping an unzipping
PIPE_GUNZIP=/tmp/$$.gunzip
PIPE_GZIP=/tmp/$$.gzip
mkfifo "$PIPE_GUNZIP"
mkfifo "$PIPE_GZIP"

# use pipes as endpoints for gzip / gunzip
gzip -dc /tmp/foo.gz > "$PIPE_GUNZIP" &
GUNZIP_PID=$!
gzip -c9 > /tmp/foo.gz.INCOMPLETE < "$PIPE_GZIP" &
GZIP_PID=$!

exec 5< "$PIPE_GUNZIP"
exec 6> "$PIPE_GZIP"

read next_match <&5
while read line; do
    if [ "$line" = "$next_match" ]; then
        read next_match <&5
        echo "$line" >&6
    fi

    echo "$line"
done < /tmp/bar

# Close file handles
exec 5<&-
exec 6>&-

# wait for gzip to terminate, replace input with output, clean up
wait $GZIP_PID
mv /tmp/foo.gz.INCOMPLETE /tmp/foo.gz
rm "$PIPE_GZIP"

# wait for gunzip to terminate, clean up
wait $GUNZIP_PID
rm "$PIPE_GUNZIP"

# check result
ls -l /tmp/{foo,bar}*
gzip -dc /tmp/foo.gz

Since process substitution is available in bash 3.2, you can simply use it. 由于bash 3.2中提供了流程替换功能,因此您可以简单地使用它。

# Setup file handles (to keep files open for reading/writing)
exec 5< <( gzip -dc /foo/compressed.gz )
exec 6> >( gzip -c9 /foo/new_compressed.gz)

# Loop over input file (/foo/bar) for matches
read next_match <&5
while read line; do
    if [ "$line" = "$next_match" ]; then
        read next_match <&5
        echo "$line" >&6
    fi

    echo "$line"
done < /foo/bar

# Close file handles
exec <5&- 6>&-

# Overwrite old match file
mv /foo/new_compressed.gz /foo/compressed.gz

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM