検索結果の -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シェルでfind1つ以上のコマンド呼び出しを実行する新しいプロセスでコマンドを実行します。このコードを解釈する呼び出しの変数にのみ影響を与える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、およびその派生(OpenBSDなど)shmksh

ほとんどの機能はおよびbashで提供され、一部の機能はおよびで提供されます。これは基本的な機能であり、他の場所では見つからないいくつかの機能の1つです。ksh88ksh93zshmkshreadarraybash

-dksh93は後でbash(2000)、zsh(2003)、およびmksh(2011)によってコピーされたオプションを1993年にread追加しましたが、bash、zsh、mksh、およびksh2020はnull区切り文字と一緒にNULで区切られたレコードを使用します。処理できます。プロセス置換はksh86(1986)に追加されましたが、pdkshベースのkshバリアントのいずれもサポートしていないため、上記の以前のバージョンで説明した方法はすべてのbashkshバージョンでは機能しません(zshのkshエミュレーションモードを除く)。

ksh88、ksh93、およびpdkshベースのシェルの1つのオプションは、出力を後処理して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この引用符や次のよくある質問などの詳細については、以下を参照してください。

   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

したがって、コードを変換してみましょう(また、最初の2行を配列を正しく消去するコードに置き換えます)。

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

関連情報