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

然而,我很难真正使 find 的输出在 bash 或其他命令行实用程序中有用。我设法利用输出的唯一方法是将其通过管道传输到 perl,并将 perl 的 IFS 更改为 null:

find . -print0 | perl -e '$/="\0"; @files=<>; print $#files;'

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

find . | wc -l

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

我怎样才能做到这一点?

这不起作用:

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

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

有帮助吗?

解决方案

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)

请注意,这里使用的重定向构建体(cmd1 < <(cmd2))类似于,但不完全一样的更通常的管道(cmd2 | cmd1) - 如果命令是shell内建(例如while),管道版本在子shell执行它们,并且它们设置(例如阵列a)任何变量都将丢失时退出。 cmd1 < <(cmd2)只能运行在CMD2子shell,所以阵列住过去的建设。警告:这种形式的重定向只在bash可用,甚至没有庆典在SH-仿真模式;你必须#!/bin/bash启动脚本。

此外,由于文件处理步骤中(在这种情况下,只是a[i++]="$file",但你可能要爱好者直接在环做什么)将其输入重定向,它不能使用可能从标准输入读取的任何命令。为了避免这种限制,我倾向于使用:

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)

...经由单元3通过文件列表,而不是标准输入。

其他提示

也许你正在寻找xargs的:

find . -print0 | xargs -r0 do_something_useful

选项-L 1可以为你有用太,这使得xargs的EXEC do_something_useful只有1个文件的参数。

的主要问题是,该定界符NUL(\ 0)是无用的在这里,因为它是不能分配IFS一个NUL值。因此,作为优秀的程序员,我们照顾,这对我们的节目输入的东西它能够处理。

首先,我们创建一个小程序,它确实这部分我们:

#!/bin/bash
printf "%s" "$@" | base64

...并调用它base64str(不要忘记使用chmod + x)的

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

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

因此,关键是,一个base64字符串没有迹象引起麻烦的bash - 当然是一个XXD或类似的东西也可以做的工作

然而计数文件的另一种方法:

find /DIR -type f -print0 | tr -dc '\0' | wc -c 

由于击4.4,内建mapfile具有-d开关(以指定的分隔符,类似于-d语句的read开关),以及分隔符可以是一个空字节。因此,一个很好的问题的答案在标题

  

捕获find . -print0的输出转换成一个bash阵列

是:

mapfile -d '' ary < <(find . -print0)

您可以安全地完成计数与此:

find . -exec echo ';' | wc -l

(它输出找到的每个文件/目录换行符,然后计数打印新行...)

我认为存在更优雅的解决方案,但我会扔掉这个。这也适用于带有空格和/或换行符的文件名:

i=0;
for f in *; do
  array[$i]="$f"
  ((i++))
done

然后您可以例如逐一列出文件(在本例中按相反顺序):

for ((i = $i - 1; i >= 0; i--)); do
  ls -al "${array[$i]}"
done

这一页 给出了一个很好的例子,更多信息请参见 第26章 在里面 高级 Bash 脚本指南.

如果

避免xargs的,您可以:

man ruby | less -p 777 
IFS=$'\777' 
#array=( $(find ~ -maxdepth 1 -type f -exec printf "%s\777" '{}' \; 2>/dev/null) ) 
array=( $(find ~ -maxdepth 1 -type f -exec printf "%s\777" '{}' + 2>/dev/null) ) 
echo ${#array[@]} 
printf "%s\n" "${array[@]}" | nl 
echo "${array[0]}" 
IFS=$' \t\n' 

我是新的,但我相信,这一个答案;希望它可以帮助别人:

STYLE="$HOME/.fluxbox/styles/"

declare -a array1

LISTING=`find $HOME/.fluxbox/styles/ -print0 -maxdepth 1 -type f`


echo $LISTING
array1=( `echo $LISTING`)
TAR_SOURCE=`echo ${array1[@]}`

#tar czvf ~/FluxieStyles.tgz $TAR_SOURCE

这是类似于Stephan202的版本,但文件(和目录)的全部一次投入的阵列。这里的for环就是要“做有用的事情”:

files=(*)                        # put files in current directory into an array
i=0
for file in "${files[@]}"
do
    echo "File ${i}: ${file}"    # do something useful 
    let i++
done

要获得一个计数:

echo ${#files[@]}

老问题,但没有人提出这种简单的方法,所以我想我会的。诚然,如果你的文件名有ETX,这并不能解决你的问题,但我怀疑它服务于任何真实世界的场景。尝试使用空,似乎默认IFS处理规则相抵触运行。季节你的口味与查找选项和错误处理。

savedFS="$IFS"
IFS=$'\x3'
filenames=(`find wherever -printf %p$'\x3'`)
IFS="$savedFS"

戈登·戴维森的答案是伟大的庆典。然而有用的快捷方式为用户的zsh存在:

首先,将你串中的变量:

A="$(find /tmp -type f -print0)"

接着,分割这个变量并将它存储在一个阵列:

B=( ${(s/^@/)A} )

有一招:^@是NUL字符。要做到这一点,必须按下Ctrl + V,接着按Ctrl + @

您可以检查$ B的每一个条目包含正确的值:

for i in "$B[@]"; echo \"$i\"

细心的读者可能注意到呼叫find命令可以在使用**语法大多数情况下被避免。例如:

B=( /tmp/** )

猛砸从未善于处理文件名(或任何文本真的),因为它使用空格作为列表分隔符。

我建议使用python与 SH 的库,而不是

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top