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
1つ以上のコマンド呼び出しを実行する新しいプロセスでコマンドを実行します。このコードを解釈する呼び出しの変数にのみ影響を与えるsh
ことができます(ただし、配列サポートを意味するわけではありません)。sh code
sh
sh
見つかったファイルのパスを現在のシェルの配列に保存するには、その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^/))
上記との違いは次のとおりです。
- ファイルのリストがソートされました。
oN
glob修飾子を追加してソートを無効にできます。 $CDIR
8日を超えてもそれ自体は含まれません。$CDIR
ディレクトリへのシンボリックリンクの場合zsh
でも、そのディレクトリに移動します。
質問にタグを付けたので、bash
kshのさまざまな実装とバリアント、またはksh
(ksh88、ksh93、David Kornの将来のksh2020(元)、pdksh、およびその派生(OpenBSDなど)sh
mksh
ほとんどの機能はおよびbash
で提供され、一部の機能はおよびで提供されます。これは基本的な機能であり、他の場所では見つからないいくつかの機能の1つです。ksh88
ksh93
zsh
mksh
readarray
bash
-d
ksh93は後で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ベースのシェルの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
またはで始まらないと仮定します。可能であれば、いくつかの実装ではを使用できます。とにかくで始まるとブロックされます)。+
-
ksh
set -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)このオプションはサポートされており、実装は-print0
NUL出口回線をサポートするのに十分に新しくなります。 Korn Shellが古い場合は使用しないでください(ただし、どちらもサポートしていないため、十分に新しいものになる可能性があります)。mksh
-d ''
+=(…)
完全な公開:私ムケシ開発者。
答え3
しばらく悩んで、先生のコーディング方式をほとんど維持することにしました。
私はkshに慣れていないのでbashでコーディングする必要があります。
翻訳が必要な場合は申し訳ありません。
配列値を渡すことができないようにパイプラインにサブシェルを作成するため、一時
ファイルが必要です。for
while
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