【问题标题】:How to split on NULs in shell [duplicate]如何在 shell 中拆分 NUL [重复]
【发布时间】:2010-10-01 22:11:27
【问题描述】:

我正在使用 zsh 作为 shell。

我想执行 unix find 命令并将结果放入 shell 数组变量中,例如:

FILES=($(find . -name '*.bak'))

这样我就可以用类似的东西迭代这些值

for F in "$FILES[@]"; do echo "<<$F>>"; done

但是,我的文件名至少包含空格,可能还有其他时髦的字符,所以上面的方法不起作用。起作用的是:

IFS=$(echo -n -e "\0"); FILES=($(find . -name '*.bak' -print0)); unset IFS

但这很丑。这已经有点超出我对 zsh 语法的舒适限制了,所以我希望有人能指出一些我从不知道但应该知道的基本功能。

【问题讨论】:

    标签: shell zsh


    【解决方案1】:

    我倾向于使用read。一个快速的谷歌搜索显示我 zsh 似乎也支持:

    find . -name '*.bak' | while read file; do echo "<<$file>>"; done
    

    这不会以零字节拆分,但它会使其与包含除换行符以外的空格的文件名一起使用。如果文件名出现在要执行的命令的最后,您可以使用xargs,也可以在文件名中使用换行符:

    find . -name '*.bak' -print0 | xargs -0 cp -t /tmp/dst
    

    将找到的所有文件复制到目录/tmp/dst。 xargs 方法的缺点是您当然没有变量中的文件名。所以这并不总是适用的。

    【讨论】:

    • 这是我提出的确切示例问题的一个很好的解决方案,谢谢!。但我仍然想知道如何将它放入数组中以供重复使用。
    【解决方案2】:

    另外,由于您使用的是 zsh,因此您可以使用 zsh 的扩展通配语法来查找文件,而不是使用 find 命令。据我所知,find 的所有功能都存在于通配语法中,并且它可以正确处理带有空格的文件名。有关详细信息,请参阅 zshexpn(1) 联机帮助页。如果您全职使用 zsh,那么非常值得学习语法。

    【讨论】:

    • 你知道的,我曾经一直使用它。我不知道我是怎么戒掉这个习惯的。感谢您的提醒!对于我真正在做的事情,它会很好用。但我仍然想知道原始问题是否有其他用途。
    【解决方案3】:

    我想出的唯一方法是使用 eval:

    (zyx:~/tmp) % F="$(find . -maxdepth 1 -name '* *' -print0)"
    (zyx:~/tmp) % echo $F | hexdump -C
    00000000  2e 2f 20 10 30 00 2e 2f  d0 96 d1 83 d1 80 d0 bd  |./ .0../........|
    00000010  d0 b0 d0 bb 20 c2 ab d0  a1 d0 b0 d0 bc d0 b8 d0  |.... ...........|
    00000020  b7 d0 b4 d0 b0 d1 82 c2  bb 2e d0 9c d0 b8 d1 82  |................|
    00000030  d1 8e d0 b3 d0 b8 d0 bd  d0 b0 20 d0 9e d0 bb d1  |.......... .....|
    00000040  8c d0 b3 d0 b0 2e 20 d0  93 d1 80 d0 b0 d0 bd d0  |...... .........|
    00000050  b8 20 d0 be d1 82 d1 80  d0 b0 d0 b6 d0 b5 d0 bd  |. ..............|
    00000060  d0 b8 d0 b9 2e 68 74 6d  6c 00 2e 2f 0a 20 0a 6e  |.....html../. .n|
    00000070  00 2e 2f 20 7b 5b 5d 7d  20 28 29 21 26 7c 00 2e  |../ {[]} ()!&|..|
    00000080  2f 74 65 73 74 32 20 2e  00 2e 2f 74 65 73 74 33  |/test2 .../test3|
    00000090  20 2e 00 2e 2f 74 74 74  0a 74 74 0a 0a 74 20 00  | .../ttt.tt..t .|
    000000a0  2e 2f 74 65 73 74 20 2e  00 2e 2f 74 74 0a 74 74  |./test .../tt.tt|
    000000b0  0a 74 20 00 2e 2f 74 74  5c 20 74 0a 74 74 74 00  |.t ../tt\ t.ttt.|
    000000c0  2e 2f 0a 20 0a 0a 00 2e  2f 7b 5c 5b 5d 7d 20 28  |./. ..../{\[]} (|
    000000d0  29 21 26 7c 00 0a                                 |)!&|..|
    000000d6
    (zyx:~/tmp) % echo $F[1]
    .
    (zyx:~/tmp) % eval 'F=( ${(s.'$'\0''.)F} )'
    (zyx:~/tmp) % echo $F[1]
    ./ 0
    

    ${(s.\0.)...} 和 ${(s.$'\0'.)...} 不起作用。 你可以使用函数:

    function SplitAt0()
    {
        local -r VAR=$1
        shift
        local -a CMD
        set -A CMD $@
        local -r CMDRESULT="$($CMD)"
        eval "$VAR="'( ${(s.'$'\0''.)CMDRESULT} )'
    }
    

    用法:SplitAt0 varname command [arguments]

    我应该使用${(ps.\0.)F},而不是${(s.\0.)F}

    % F=${(ps.\0.)"$(find . -maxdepth 1 -name '* *' -print0)"}
    % echo $F[1]
    ./ 0
    

    【讨论】:

      【解决方案4】:

      我试过了

      F=(*)
      

      它甚至可以处理文件名中带有换行符的文件。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-05-08
        • 1970-01-01
        • 2013-03-02
        • 1970-01-01
        • 2021-12-13
        • 1970-01-01
        • 2010-09-22
        • 2011-02-23
        相关资源
        最近更新 更多