【问题标题】:Trouble with the process substitution in the file command文件命令中的进程替换问题
【发布时间】:2017-04-09 10:41:09
【问题描述】:

更容易显示为试图用文字来描述。

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

file -f <(command)

【问题讨论】:

    标签: bash shell pipe


    【解决方案1】:

    你做对了。这是file 实现中的一个错误,我可以在我的(Debian jessie 上的文件 5.22)上重现它。它期望-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, ...}) = 0
    …
    read(3, "foo\n", 4096)                 = 5
    …
    read(3, "", 4096)                       = 0
    lseek(3, 0, SEEK_SET)                   = -1 ESPIPE (Illegal seek)
    read(3, "", 4096)                       = 0
    close(3)                                = 0
    

    文件程序打开文件描述符 3 上的文件名列表并读取它。它试图回到文件的开头。这失败了,但程序再次从文件中读取,由于文件位置已经在末尾,因此不会产生任何数据。因此 file 以一个空的文件名列表结束。

    源码中-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】:

      不要使用

      【讨论】:

      • 你知道&lt;(cmd)$(cmd)的区别吗?如果是,您知道这不是解决方案。另外,想象一下,如果文件名包含一些空格会发生什么......
      • 您是正确的,文件名中的空格会导致解决方案出现问题,例如 file $(find . -name jo* -printf %f' ') (尽管文件名中没有空格例如,因此假设没有空格要处理)另一种解决方案,考虑到空格将是 find 。 -name jo* -exec 文件 '{}' \;
      • 我知道如何解决问题(一种方法是您的建议,另一种方法是 find ... | file -f -,这里至少还有另外两种方法。;)但问题的优点是 为什么进程替换不起作用?(我得到了接受的答案:))无论如何,谢谢您的关注。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-02-01
      • 2014-05-08
      • 1970-01-01
      • 1970-01-01
      • 2012-11-20
      • 1970-01-01
      • 2013-09-25
      相关资源
      最近更新 更多