찾기 결과의 -exec 명령에서 배열에 값을 할당할 수 없습니다.

찾기 결과의 -exec 명령에서 배열에 값을 할당할 수 없습니다.

7일이 지난 파일을 삭제하기 위해 찾기 결과를 배열로 배포하기 위해 다음 명령을 작성했습니다.

F_ARR[0]=''
unset F_ARR
find "$CDIR" ! -type d -mtime +7 -exec sh -c '
    for pathname; do
        F_ARR+=("$pathname")
    done' sh {} +
echo ${#F_ARR[@]}

그러나 F_ARR 배열의 길이를 인쇄하면 0이 표시됩니다. 내 코드에 실수가 있었나요?

답변1

find ... -exec sh -c 'sh code' {} +

bash/ksh 셸에서 find하나 이상의 명령 호출을 실행하는 새 프로세스에서 명령을 실행합니다. 이 코드를 해석하는 호출의 변수에만 영향을 미칠 sh수 있습니다 (단, 배열 지원을 의미하지는 않습니다).sh codeshsh

찾은 파일의 경로를 현재 쉘의 배열에 저장하려면 find해당 쉘에서 할당을 수행해야 합니다.

4.4+ 에서는 bash다음을 수행할 수 있습니다.

readarray -td '' F_ARR < <(find "$CDIR" ! -type d -mtime +7 -print0)

find지원하지 않는 경우 -print0로 대체할 수 있습니다 -exec printf '%s\0' {} +.

(이전 버전의 경우 bash다음을 참조하세요.비슷한 질문에 대한 답변에서 이를 수행하는 방법은 무엇입니까?).

zsh여기에서는 대신 셸을 사용할 수도 있습니다 .

f_arr=($CDIR/**/*(NDm+7^/))

위와 차이점은 다음과 같습니다.

  • 파일 목록이 정렬되었습니다. oNglob 한정자를 추가하여 정렬을 비활성화 할 수 있습니다.
  • $CDIR8일이 넘은 경우에도 자체적으로 포함되지 않습니다.
  • $CDIR디렉토리에 대한 심볼릭 링크인 경우 zsh에도 해당 디렉토리로 이동합니다.

질문에 및 으로 태그를 지정했으므로 bashksh 의 다양한 구현 및 변형 또는ksh(ksh88, ksh93, David Korn의 향후 ksh2020(원래), pdksh 및 그 파생물(예: OpenBSDshmksh

대부분의 기능은 및 bash에서 제공되며 일부 기능은 및 에서 제공됩니다 . 기본 기능이며 다른 곳에서는 찾을 수 없는 몇 가지 기능 중 하나입니다 .ksh88ksh93zshmkshreadarraybash

-dksh93은 나중에 bash(2000), zsh(2003) 및 mksh(2011)에 의해 복사된 옵션 을 1993년에 read추가했지만 bash, zsh, mksh 및 ksh2020만 null 구분 기호와 함께 이를 사용하여 NUL로 구분된 레코드를 처리할 수 있습니다. 프로세스 대체는 ksh86(1986)에 추가되었지만 pdksh 기반 ksh 변형 중 어느 것도 이를 지원하지 않으므로 위에서 이전 버전에 대해 언급한 방법은 bash모든 ksh 버전에서 작동하지 않습니다(zsh의 ksh 에뮬레이션 모드 제외).

ksh88, ksh93 및 pdksh 기반 셸에서 한 가지 옵션은 출력을 사후 처리하여 find배열을 정의하고 다음을 사용하여 코드를 평가할 수 있는 셸 코드를 생성하는 것입니다 eval.

eval set -A F_ARR "$(
  LC_ALL=C find "$CDIR" ! -type d -exec awk -v q="'" -- '
    BEGIN {
      for (i = 1; i < ARGC; i++) {
        gsub(q, q "\\" q q, ARGV[i])
        printf " %s", q ARGV[i] q
      }
      exit
    }' {} +
)"

(이것은 또는 $CDIR로 시작하지 않는다고 가정합니다 . 가능하다면 일부 구현에서는 를 사용할 수 있습니다 . 어쨌든 로 시작하면 차단 됩니다).+-kshset -A F_ARR ---find

set -A F_ARR모든 POSIX sh에 동일한 접근 방식을 사용할 수 있으며 다음 을 대체하여 set --POSIX 셸용 배열을 채울 수 있습니다 "$@".

답변2

이것은 예상된 것입니다 - 참조맨페이지All commands of a pipeline are executed in separate subshells이 인용문 및 다음 FAQ 항목 과 같은 자세한 내용을 보려면 다음을 참조하세요 .

   Something is going wrong with my while...read loop
     Most likely, you've encountered the problem in which the shell runs all
     parts of a pipeline as subshell.  The inner loop will be executed in a
     subshell and variable changes cannot be propagated if run in a pipeline:

           bar | baz | while read foo; do ...; done

     Note that exit in the inner loop will only exit the subshell and not the
     original shell.  Likewise, if the code is inside a function, return in
     the inner loop will only exit the subshell and won't terminate the func‐
     tion.

     Use co-processes instead:

           bar | baz |&
           while read -p foo; do ...; done
           exec 3>&p; exec 3>&-

     If read is run in a loop such as while read foo; do ...; done then lead‐
     ing whitespace will be removed (IFS) and backslashes processed.  You
     might want to use while IFS= read -r foo; do ...; done for pristine I/O.
     Similarly, when using the -a option, use of the -r option might be pru‐
     dent (“read -raN-1 arr <file”); the same applies for NUL-terminated
     lines:

           find . -type f -print0 |& \
               while IFS= read -d '' -pr filename; do
                   print -r -- "found <${filename#./}>"
           done

따라서 코드를 변환해 보겠습니다(또한 처음 두 줄을 배열을 올바르게 지우는 코드로 바꿉니다).

set -A F_ARR
find "$CDIR" ! -type d -mtime +7 -print0 |&
while IFS= read -d '' -pr pathname; do
        F_ARR+=("$pathname")
done
echo ${#F_ARR[@]}

이를 위해서는찾기(1)이 옵션은 지원되며 구현은 -print0NUL 종료 회선을 지원할 만큼 충분히 새롭습니다. Korn Shell이 ​​오래된 경우에는 사용하지 마십시오(그러나 둘 중 하나도 지원하지 않으므로 충분히 새로운 것일 가능성이 높습니다).mksh-d ''+=(…)

전체 공개: 나는므케시개발자.

답변3

잠시 고민하다가 선생님의 코딩 방식을 대부분 유지하기로 결정했습니다.
저는 ksh에 익숙하지 않아서 bash로 코딩해야 합니다.
번역이 필요하시면 죄송합니다.

배열 값을 전달할 수 없도록 파이프라인에 서브셸을 생성하므로 임시
파일 이 필요합니다.forwhile

mktemp임시 파일이 처리할 필요가 없는 디렉토리에 배치 되도록 파일 모드를 변경할 수 있습니다 .
while 루프의 에코 부분을 코드로 바꾸십시오.

#!/bin/bash

i=0
tmp=`mktemp /tmp/findingXXXXXX`
find "$CDIR" ! -type d -mtime +7 |awk '{print "F_ARR["(NR-1)"]="$0}' >$tmp
. $tmp
rm -f $tmp
while [ "${F_ARR[$i]}" != "" ];
  do
      echo i=$i ${F_ARR[$i]}
      ((i++))

done

관련 정보