簡體   English   中英

捕獲查找的輸出。 -print0進入bash數組

[英]Capturing output of find . -print0 into a bash array

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

但是,我很難讓find的輸出在bash或其他命令行實用程序中有用。 我設法使用輸出的唯一方法是將它傳遞給perl,並將perl的IFS更改為null:

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

此示例打印找到的文件數,避免文件名中的換行符損壞計數的危險,如下所示:

find . | wc -l

由於大多數命令行程序不支持空分隔輸入,我認為最好的方法是捕獲find . -print0的輸出find . -print0 bash數組中的find . -print0 ,就像我在上面的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 builtins(例如while ),則管道版本在子shell中執行它們,它們設置的任何變量(例如數組a )在退出時都會丟失。 cmd1 < <(cmd2)僅在子shell中運行cmd2,因此數組將超過其構造。 警告:這種重定向形式僅在bash中可用,在sh仿真模式下甚至不是bash; 你必須用#!/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傳遞文件列表,而不是stdin。

也許你正在尋找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-string沒有任何跡象會導致bash出現問題 - 當然xxd或類似的東西也可以完成這項工作。

另一種計算文件的方法:

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

從Bash 4.4開始,內置mapfile具有-d開關(用於指定分隔符,類似於read語句的-d開關),分隔符可以是空字節。 因此,對標題中的問題給出了很好的答案

捕獲find . -print0輸出find . -print0 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

此頁面提供了一個很好的示例,有關更多示例,請參閱Advanced Bash-Scripting Guide中的第26章

如果可以,請避免使用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,這不能解決你的問題,但我懷疑它適用於任何真實場景。 嘗試使用null似乎違反了默認的IFS處理規則。 通過查找選項和錯誤處理來滿足您的口味。

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

Gordon Davisson的回答對bash很有幫助。 但是zsh用戶存在一個有用的快捷方式:

首先,將字符串放在變量中:

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

接下來,拆分此變量並將其存儲在數組中:

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

有一個技巧: ^@是NUL角色。 要做到這一點,你必須輸入Ctrl + V,然后按Ctrl + @。

您可以檢查$ B的每個條目是否包含正確的值:

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

細心的讀者可能會注意到在大多數情況下使用**語法可以避免調用find命令。 例如:

B=( /tmp/** )

Bash從未擅長處理文件名(或任何文本),因為它使用空格作為列表分隔符。

我建議使用shthon代替sh庫。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM