development

"find"명령 결과를 Bash에 배열로 저장하려면 어떻게해야합니까?

big-blog 2020. 12. 11. 19:07
반응형

"find"명령 결과를 Bash에 배열로 저장하려면 어떻게해야합니까?


결과를 find배열 로 저장하려고 합니다. 내 코드는 다음과 같습니다.

#!/bin/bash

echo "input : "
read input

echo "searching file with this pattern '${input}' under present directory"
array=`find . -name ${input}`

len=${#array[*]}
echo "found : ${len}"

i=0

while [ $i -lt $len ]
do
echo ${array[$i]}
let i++
done

현재 디렉토리 아래에 2 개의 .txt 파일이 있습니다. 그래서 나는 결과로 '2'를 기대 ${len}합니다. 그러나 1을 인쇄합니다. 그 이유는 모든 결과 find를 하나의 요소로 취하기 때문입니다. 이 문제를 어떻게 해결할 수 있습니까?

추신
: 비슷한 문제에 대한 StackOverFlow에서 몇 가지 해결책을 찾았습니다. 하지만 약간 다르기 때문에 제 경우에는 신청할 수 없습니다. 루프 전에 변수에 결과를 저장해야합니다. 다시 한 번 감사드립니다.


다음은의 출력을 배열 find가져 오는 한 가지 솔루션입니다 bash.

array=()
while IFS=  read -r -d $'\0'; do
    array+=("$REPLY")
done < <(find . -name "${input}" -print0)

일반적으로 파일 이름에는 공백, 줄 바꿈 및 기타 스크립트에 적대적인 문자가 포함될 수 있으므로 까다 롭습니다. find파일 이름을 서로 안전하게 분리하고 사용하는 유일한 방법 -print0은 널 문자로 분리 된 파일 이름을 인쇄하는 것을 사용 하는 것입니다. bash의 readarray/ mapfile함수가 null로 구분 된 문자열을 지원하지만 지원하지 않는 경우 이는 불편 하지 않습니다. Bash read는 위의 루프로 연결됩니다.

작동 원리

  1. 첫 번째 줄은 빈 배열을 만듭니다. array=()

  2. read명령문이 실행될 때마다 표준 입력에서 널로 구분 된 파일 이름을 읽습니다. -r옵션은 read백 슬래시 문자를 그대로 두도록 지시 합니다. -d $'\0'지시 read입력이 널 (null)로 구분 될 것이다. 에 이름을 생략 했으므로 read쉘은 입력을 기본 이름 : REPLY.

  3. array+=("$REPLY")문은 새 파일 이름을 배열에 추가합니다 array.

  4. 마지막 줄은 리디렉션과 명령 대체를 결합 find하여 while루프 의 표준 입력에 대한 출력을 제공합니다 .

프로세스 대체를 사용하는 이유는 무엇입니까?

프로세스 대체를 사용하지 않았다면 루프는 다음과 같이 작성 될 수 있습니다.

array=()
find . -name "${input}" -print0 >tmpfile
while IFS=  read -r -d $'\0'; do
    array+=("$REPLY")
done <tmpfile
rm -f tmpfile

위의 출력은 find임시 파일에 저장되며 해당 파일은 while 루프에 대한 표준 입력으로 사용됩니다. 프로세스 대체의 개념은 이러한 임시 파일을 불필요하게 만드는 것입니다. 따라서 while루프가에서 stdin을 가져 오는 대신에서 stdin을 가져 오도록 tmpfile할 수 있습니다 <(find . -name ${input} -print0).

프로세스 대체는 널리 유용합니다. 명령이 파일에서 읽으려는 많은 위치에서 <(...)파일 이름 대신 프로세스 대체를 지정할 수 있습니다 . >(...)명령이 파일 쓰려 는 파일 이름 대신 사용할 수 있는 유사한 형식 이 있습니다 .

배열과 마찬가지로 프로세스 대체는 bash 및 기타 고급 셸의 기능입니다. POSIX 표준의 일부가 아닙니다.

추가 참고 사항

다음 명령은 셸 배열이 아닌 셸 변수를 만듭니다.

array=`find . -name "${input}"`

배열을 만들려면 find 출력 주위에 괄호를 넣어야합니다. 순진하게도 다음과 같이 할 수 있습니다.

array=(`find . -name "${input}"`)  # don't do this

문제는 쉘이 결과에 대해 단어 분할을 수행 find하여 배열의 요소가 원하는대로 보장되지 않는다는 것입니다.


bash4 이상을 사용하는 경우 사용을 다음 find으로 바꿀 수 있습니다.

shopt -s globstar nullglob
array=( **/*"$input"* )

에서 **활성화 된 패턴 globstar은 0 개 이상의 디렉토리와 일치하여 패턴이 현재 디렉토리의 임의 깊이와 일치하도록합니다. nullglob옵션이 없으면 패턴 (매개 변수 확장 후)이 문자 그대로 처리되므로 일치하는 항목이 없으면 빈 배열이 아닌 단일 문자열이있는 배열을 갖게됩니다.

dotglob숨겨진 디렉토리 (예 :)를 탐색 .ssh하고 숨겨진 파일 (예 :)도 일치 시키려면 첫 번째 줄에 옵션을 추가하십시오 .bashrc.


Bash 4.4는 /에 대한 -d옵션을 도입 했으므로 이제 다음과 같이 해결할 수 있습니다.readarraymapfile

readarray -d '' array < <(find . -name "$input" -print0)

공백, 줄 바꿈 및 글 로빙 문자를 포함하여 임의의 파일 이름으로 작동하는 방법.

로부터 수동 (다른 옵션을 생략) :

mapfile [-d delim] [array]

-d
의 첫 번째 문자는 delim개행 대신 각 입력 행을 종료하는 데 사용됩니다. 경우 delim빈 문자열입니다, mapfile그것은 NUL 문자를 읽을 때 줄을 종료합니다.

그리고 readarray의 동의어입니다 mapfile.


당신은 같은 것을 시도 할 수 있습니다

array=(`find . -type f | sort -r | head -2`)
, 배열 값을 인쇄하려면 echo와 같은 것을 시도해 볼 수 있습니다. "${array[*]}"


In bash, $(<any_shell_cmd>) helps to run a command and capture the output. Passing this to IFS with \n as delimiter helps to convert that to an array.

IFS='\n' read -r -a txt_files <<< $(find /path/to/dir -name "*.txt")

You could do like this:

#!/bin/bash
echo "input : "
read input

echo "searching file with this pattern '${input}' under present directory"
array=(`find . -name '*'${input}'*'`)

for i in "${array[@]}"
do :
    echo $i
done

For me, this worked fine on cygwin:

declare -a names=$(echo "("; find <path> <other options> -printf '"%p" '; echo ")")
for nm in "${names[@]}"
do
    echo "$nm"
done

This works with spaces, but not with double quotes (") in the directory names (which aren't allowed in a Windows environment anyway).

Beware the space in the -printf option.

참고URL : https://stackoverflow.com/questions/23356779/how-can-i-store-the-find-command-results-as-an-array-in-bash

반응형