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 . -print0
bash 배열로 캡처
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
'developer tip' 카테고리의 다른 글
Android 라이브러리 프로젝트 용 jar를 만드는 방법 (0) | 2020.10.24 |
---|---|
기존 스트림에 새로운 가치 추가 (0) | 2020.10.24 |
함수형 프로그래밍에서 응용 함수를 사용해야하는 이유는 무엇입니까? (0) | 2020.10.24 |
apk 파일의 코드를 추출하는 방법 (0) | 2020.10.24 |
uint32와 uint32_t의 차이점 (0) | 2020.10.24 |