문제

사용 find . -print0 공백, 신생, 따옴표 등을 포함하는 파일 이름의 가능성으로 인해 Bash에서 파일 목록을 얻는 유일한 안전한 방법 인 것 같습니다.

그러나 실제로 Bash 또는 다른 명령 줄 유틸리티에서 Find의 출력을 유용하게 만드는 데 어려움을 겪고 있습니다. 출력을 사용하는 유일한 방법은 Perl에 배관하고 Perl의 IFS를 NULL로 변경하는 것입니다.

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

이 예제는 다음과 같이 발생하는 파일의 숫자를 인쇄하여 다음과 같이 발생하는 카운트를 손상시키는 파일 이름의 최신 라인의 위험을 피합니다.

find . | wc -l

대부분의 명령 줄 프로그램이 Null 기준 입력을 지원하지 않기 때문에 가장 좋은 점은 출력을 포착하는 것입니다. find . -print0 위의 Perl Snippet에서 수행 한 것처럼 Bash 배열에서 작업을 계속하고 작업을 계속하십시오.

어떻게 할 수 있습니까?

이것은 작동하지 않습니다 :

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

훨씬 더 일반적인 질문은 다음과 같습니다. 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- 혈전 모드에서도 강타하지 않습니다. 스크립트를 시작해야합니다 #!/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)

... Stdin이 아닌 단위 3을 통해 파일 목록을 전달합니다.

다른 팁

어쩌면 당신은 xargs를 찾고있을 것입니다 :

find . -print0 | xargs -r0 do_something_useful

옵션 -L 1은 귀하에게도 유용 할 수 있으므로 Xargs Exec Do_Something_는 단 1 개의 파일 인수만으로 제공합니다.

주요 문제는 Delimiter Nul ( 0)이 IFS를 NUL 값을 할당 할 수 없기 때문에 여기서 쓸모가 없다는 것입니다. 우수한 프로그래머로서 우리는 돌보는 데 도움이됩니다. 프로그램의 입력은 처리 할 수있는 것입니다.

먼저 우리는 작은 프로그램을 만듭니다.

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

... 그리고 그것을 base64str이라고 부릅니다 (chmod +x를 잊지 마십시오)

둘째, 이제 우리는 이제 간단하고 간단한 for-loop를 사용할 수 있습니다.

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

따라서 비결은 Base64 -string에 부호가 없다는 것이 부호가 없다는 것입니다. 물론 XXD 또는 비슷한 일도 작업을 수행 할 수 있습니다.

파일 계산의 또 다른 방법 :

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

Bash 4.4 이후, 내장 mapfile has the -d 스위치 (Delimiter를 지정하기 위해 -d 스위치 read 진술), 구분 기자는 무효 바이트가 될 수 있습니다. 따라서 제목의 질문에 대한 좋은 대답

출력 캡처 find . -print0 배쉬 배열로

이다:

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

당신은 이것으로 안전하게 카운트를 할 수 있습니다.

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

(모든 파일/dir에 대한 신생을 인쇄 한 다음 인쇄 된 최신 라인을 계산합니다 ...)

나는 더 우아한 솔루션이 존재한다고 생각하지만 이것을 던질 것입니다. 이것은 공간 및/또는 신성경이있는 파일 이름에도 적용됩니다.

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 장 에서 고급 배쉬 스크립팅 가이드.

가능하면 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는 공백을 목록 구분 기자로 사용하기 때문에 파일 이름 (또는 텍스트)을 처리하는 데 능숙하지 않았습니다.

Python을 사용하는 것이 좋습니다 대신 도서관.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top