捕获查找的输出.-print0进入bash数组

Idr*_*ris 73 arrays bash null find delimiter

使用find . -print0似乎是获取bash中文件列表的唯一安全方法,因为文件名可能包含空格,换行符,引号等.

但是,我很难让find的输出在bash或其他命令行实用程序中有用.我设法使用输出的唯一方法是将它传递给perl,并将perl的IFS更改为null:

find . -print0 | perl -e '$/="\0"; @files=<>; print $#files;'
Run Code Online (Sandbox Code Playgroud)

此示例打印找到的文件数,避免文件名中的换行符损坏计数的危险,如下所示:

find . | wc -l
Run Code Online (Sandbox Code Playgroud)

由于大多数命令行程序不支持空分隔输入,我认为最好的方法是捕获find . -print0bash数组中的输出,就像我在上面的perl片段中所做的那样,然后继续执行任务,无论它是什么是.

我怎样才能做到这一点?

这不起作用:

find . -print0 | ( IFS=$'\0' ; array=( $( cat ) ) ; echo ${#array[@]} )
Run Code Online (Sandbox Code Playgroud)

一个更普遍的问题可能是:如何用bash中的文件列表做有用的事情?

Gor*_*son 99

Greg的BashFAQ无耻地偷走了:

unset a i
while IFS= read -r -d $'\0' file; do
    a[i++]="$file"        # or however you want to process each file
done < <(find /tmp -type f -print0)
Run Code Online (Sandbox Code Playgroud)

请注意,此处使用的重定向构造(cmd1 < <(cmd2))与更常用的管道(cmd2 | cmd1)相似但不完全相同- 如果命令是shell builtins(例如while),则管道版本在子shell中执行它们,以及它们设置的任何变量(例如阵列a)在退出时丢失. cmd1 < <(cmd2)只在子shell中运行cmd2,因此数组将超过其构造.警告:这种重定向形式仅在bash中可用,在sh仿真模式下甚至不是bash; 你必须用你的脚本开始#!/bin/bash.

此外,因为文件处理步骤(在这种情况下,只是a[i++]="$file",但你可能想在循环中直接做一些更好的事情)将其输入重定向,它不能使用任何可能从stdin读取的命令.为了避免这种限制,我倾向于使用:

unset a i
while IFS= read -r -u3 -d $'\0' file; do
    a[i++]="$file"        # or however you want to process each file
done 3< <(find /tmp -type f -print0)
Run Code Online (Sandbox Code Playgroud)

...通过单元3传递文件列表,而不是stdin.

  • 将元素添加到数组末尾的更简单方法是:`arr + =("$ file")` (14认同)
  • `-d''`相当于`-d $'\ 0'`. (9认同)

Bal*_*sár 7

也许你正在寻找xargs:

find . -print0 | xargs -r0 do_something_useful
Run Code Online (Sandbox Code Playgroud)

选项-L 1对你也很有用,这使得xargs exec do_something_useful只有1个文件参数.

  • 这不是我所追求的,因为没有机会与列表进行类似数组的事情,例如排序:必须使用每个元素,当它出现在find命令之外时.如果你可以详细说明这个例子,"do_something_useful"部分是一个bash数组推送操作,那么这可能就是我所追求的. (2认同)

gni*_*urf 7

从Bash 4.4开始,内置mapfile-dswitch(用来指定定界符,类似于语句的-dswitch read),定界符可以是空字节。因此,标题中问题的一个很好的答案

将 的输出捕获find . -print0到 bash 数组中

是:

mapfile -d '' ary < <(find . -print0)
Run Code Online (Sandbox Code Playgroud)

  • 这看起来更优雅,而且也像locate的魅力一样:`mapfile -d '' list &lt;&lt;(locate -b -0 -r "$1$")`。 (3认同)

zst*_*egi 5

主要问题是,分隔符NUL(\ 0)在这里没用,因为不可能为IFS分配NUL值.因此,作为优秀的程序员,我们需要注意的是,我们程序的输入是它能够处理的.

首先,我们创建一个小程序,为我们完成这一部分:

#!/bin/bash
printf "%s" "$@" | base64
Run Code Online (Sandbox Code Playgroud)

...并将其命名为base64str(不要忘记chmod + x)

其次,我们现在可以使用一个简单而直接的for循环:

for i in `find -type f -exec base64str '{}' \;`
do 
  file="`echo -n "$i" | base64 -d`"
  # do something with file
done
Run Code Online (Sandbox Code Playgroud)

所以诀窍是,base64-string没有任何迹象会导致bash出现问题 - 当然xxd或类似的东西也可以完成这项工作.

  • `read -r -d''`将把下一个NUL的所有内容读入``$ REPLY'`.没有必要关心`IFS`. (2认同)