文件命令中的进程替换出现问题

4
更易于展示,而不是用言语来描述。
find . -name jo\* -print > list
cat list
#./jo1
#./jo2
#./jo3

# the "file" by reading the list of files from the file "list"
file -f list
#./jo1: ASCII text
#./jo2: ASCII text
#./jo3: ASCII text

#now with process substitution
file -f <(find . -name jo\* -print)

没有输出任何内容... ;(

#repeat with -x
set -x
file -f <(find . -name jo\* -print)
set +x

#shows
+ file -f /dev/fd/63
++ find . -name 'jo*' -print
+ set +x

所以,这应该是可行的。但它没有起作用。为什么?

编辑

请注意 - 在您输入文件名的任何地方,进程替换都应该有效,比如说:

diff <(some command) <(another command)

上述代码中使用的是Bash。

diff /dev/fd/... /dev/fd/...

例如,在 grep 中,您可以使用以下命令:

grep -f <(command_for_produce_the_patterns) files..

同样地,bash 在内部使用这个作为

grep -f /dev/fd/63 files....

所以,同样的应该适用于文件

file -f <(command)
2个回答

5
您做得很好。这是您实现file时出现的错误,我在我的电脑上也能重现(Debian jessie操作系统下5.22版本的file)。它期望参数-f是一个可寻址的文件,当文件不可寻址时,无法检测到错误。这就是为什么它可以使用常规文件,但无法使用管道(这是进程替换用于在两个进程之间传递数据的对象)。您可以通过strace观察发生了什么:
$ strace file -f <(echo foo)
…
open("/proc/self/fd/13", O_RDONLY)      = 3
fstat(3, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0read(3, "foo\n", 4096)                 = 5read(3, "", 4096)                       = 0
lseek(3, 0, SEEK_SET)                   = -1 ESPIPE (Illegal seek)
read(3, "", 4096)                       = 0
close(3)                                = 0

文件程序打开文件描述符3上的文件名列表并读取它。然后尝试将其移到文件开头,但失败了。不过,程序再次从文件中读取,因为文件位置已经在末尾,所以没有数据产生。因此,文件最终得到一个空的文件名列表。
在源代码中,-f选项触发unwrap函数:
private int
unwrap(struct magic_set *ms, const char *fn)
{
    // …
    if (strcmp("-", fn) == 0) {
            f = stdin;
            wid = 1;
    } else {
        if ((f = fopen(fn, "r")) == NULL) {
                (void)fprintf(stderr, "%s: Cannot open `%s' (%s).\n",
                    progname, fn, strerror(errno));
                return 1;
        }
        while ((len = getline(&line, &llen, f)) > 0) {
            // … code to determine column widths
        }
        rewind(f);
    }
    // Code to read the file names from f follows
}

如果文件名不是-(指示从标准输入读取),那么代码会读取文件两次,一次确定文件名的最大宽度,一次处理文件。调用rewind缺少错误处理。如果文件名为-,代码不会尝试对齐列。

-2

不要使用<(cmd)语法,而是使用$(cmd)。这样应该可以解决这个bug。


1
你知道 <(cmd)$(cmd) 之间的区别吗?如果是,你就知道这不是一个解决方案。而且,想象一下,如果文件名包含空格会发生什么... - cajwine
你说得对,文件名中的空格会导致像 file $(find . -name jo* -printf %f' ') 这样的解决方案出现问题(尽管示例中没有空格,因此假定没有需要处理的空格)。考虑到空格的情况,另一种解决方案是 find . -name jo* -exec file '{}' ;。 - Raman Sailopal
我知道如何解决这个问题(一种方法是你的建议,另一种是 find ... | file -f -,还有至少两种其他方法。;) 但问题的价值在于“为什么进程替换不起作用?”(我已经得到了被接受的答案 :))无论如何,感谢您的关注。 - cajwine

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接