developer tip

find 출력을 캡처합니다.

copycodes 2020. 10. 24. 10:36
반응형

find 출력을 캡처합니다. -print0을 bash 배열로


사용이 find . -print0때문에 공백, 줄 바꿈, 인용 부호 등을 포함하는 파일 이름의 가능성에 bash는 파일 목록을 얻는 유일한 안전한 방법이 될 것 같다

그러나 실제로 bash 또는 다른 명령 줄 유틸리티에서 find의 출력을 유용하게 만드는 데 어려움을 겪고 있습니다. 출력을 사용할 수있는 유일한 방법은 출력을 perl로 파이프하고 perl의 IFS를 null로 변경하는 것입니다.

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

이 예는 발견 된 파일 수를 인쇄하여 다음과 같이 발생하는 파일 이름의 개행으로 개수가 손상 될 위험을 방지합니다.

find . | wc -l

대부분의 명령 줄 프로그램은 null로 구분 된 입력을 지원하지 않으므로 find . -print0위의 펄 스 니펫에서 수행 한 것처럼 bash 배열 의 출력을 캡처 한 다음 작업을 계속 하는 것이 가장 좋은 방법이라고 생각합니다. 있다.

어떻게 할 수 있습니까?

작동하지 않습니다.

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

훨씬 더 일반적인 질문은 다음과 같습니다. bash에서 파일 목록으로 유용한 일을 어떻게 할 수 있습니까?


Greg의 Bash 에서 뻔뻔 스럽게 도난 당했습니다 .

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) 과 유사하지만 동일하지는 않습니다 . 명령이 셸 내장 (예 :) while인 경우 파이프 라인 버전은 하위 셸 및 설정 한 변수에서 실행합니다. (예 : 배열 a) 종료시 손실됩니다. cmd1 < <(cmd2)서브 쉘에서만 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)

... 표준 입력이 아닌 유닛 3을 통해 파일 목록을 전달합니다.


아마도 당신은 xargs를 찾고 있습니다 :

find . -print0 | xargs -r0 do_something_useful

-L 1 옵션도 유용 할 수 있습니다. 이는 xargs exec를 하나의 파일 인수만으로 do_something_useful하게 만듭니다.


주요 문제는 IFS에 NUL 값을 할당 할 수 없기 때문에 구분 기호 NUL (\ 0)이 여기서 쓸모 없다는 것입니다. 따라서 좋은 프로그래머로서 우리 프로그램에 대한 입력은 처리 할 수있는 것입니다.

먼저이 부분을 수행하는 작은 프로그램을 만듭니다.

#!/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스위치 ( 문의 -d스위치 와 유사한 구분 기호를 지정하기위한 read)가 있으며 구분 기호는 널 바이트가 될 수 있습니다. 따라서 제목의 질문에 대한 좋은 답변

출력을 find . -print0bash 배열로 캡처

is :

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' 

I am new but I believe that this an answer; hope it helps someone:

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

This is similar to Stephan202's version, but the files (and directories) are put into an array all at once. The for loop here is just to "do useful things":

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

To get a count:

echo ${#files[@]}

Old question, but no-one suggested this simple method, so I thought I would. Granted if your filenames have an ETX, this doesn't solve your problem, but I suspect it serves for any real-world scenario. Trying to use null seems to run afoul of default IFS handling rules. Season to your tastes with find options and error handling.

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

Gordon Davisson's answer is great for bash. However a useful shortcut exist for zsh users:

First, place you string in a variable:

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

Next, split this variable and store it in an array:

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

There is a trick: ^@ is the NUL character. To do it, you have to type Ctrl+V followed by Ctrl+@.

You can check each entry of $B contains right value:

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

Careful readers may notice that call to find command may be avoided in most cases using ** syntax. For example:

B=( /tmp/** )

Bash has never been good at handling filenames (or any text really) because it uses spaces as a list delimiter.

I'd recommend using python with the sh library instead.

참고URL : https://stackoverflow.com/questions/1116992/capturing-output-of-find-print0-into-a-bash-array

반응형