for
ループ内で次のことを実行しようとしています:
スクリプトを使用してファイルを検索し、それらの名前を画面にエコーできますが、ファイルにパイプすることはできません。 (私はこれまでgzipの部分をテストしていません)
スクリプトの関連部分は次のとおりです($LOGCLEAN_FILE
が存在し、スクリプトの前の部分に書き込まれます)。
for F in `find . -type f \( ! -name '*.gz' \) -a \( ! -name '*.Z' \) -mtime +7`
do
{
print "Will be compressing file ${F}" >> $LOGCLEAN_FILE
} ;
##gzip $F
done
" >> $LOGCLEAN_FILE"
テキストなしでスクリプトを実行すると、出力が画面に表示されます。
何が悪いのですか?
批判は大歓迎です-私はまだ学んでいます。
Findの出力を確実に後処理することはできません。使用する -exec
in find
:
find . -type f ! -name '*.gz' ! -name '*.Z' -mtime +7 -exec sh -c '
for i do
printf "%s\n" "Will be compressing file $i"
gzip "$i"
done' sh {} + >> log
GNU find
の実装)を使用すると、shを実行せずに回避することもできます。
find . -type f ! -name '*.gz' ! -name '*.Z' -mtime +7 \
-printf 'Will be compressing file %p\n' -exec gzip {} + >> log
シェルループでfind
の出力を再現する必要は本当にありません。 ファイル名のリストをパックする場合、一般的な式は次のとおりです。
find ... | gzip > logfile.gz
ファイル自体をgzipしたい場合は、次のように変更されます。
find ... | tar -czvf archive.tar.gz -T -
これはtar
にファイルから作業するファイル名のリストを読み取るように指示し、単一の-
は標準入力を表します。 (-T
AKA --files-from=
オプションはGNU tarにあります。他のフレーバーについてはわかりません。)もちろん、ファイルで作業するとどうなるでしょうか。名前に\n
が含まれています。
リダイレクト(>>
)をループの外(done
の後)に配置します。
for i in $(find . -type f \( ! -name '*.gz' \) -a \( ! -name '*.Z' \) -mtime +7);
do echo "Will be compressing file ${i}";
##gzip $i;
done >> $LOGCLEAN_FILE
または、最終的にgzip
コマンドのコメントを外すことを計画している場合は、次のことを検討してください。
for i in $(find . -type f \( ! -name '*.gz' \) -a \( ! -name '*.Z' \) -mtime +7);
do gzip "$i" && echo "Successfully compressed ${i}";
done >> $LOGCLEAN_FILE
&&
は、前のコマンドが0
(エラーなし)で終了した場合にのみ、次のコマンドを実行することを意味します。
ファイル名にはスペースと改行の両方を含めることができるため、考えられるアプローチは次のとおりです。
#!/bin/bash
logclean_file="logclean_file.txt"
ts="$(date)"
printf "%s\n" "$ts" > "$logclean_file"
# Set IFS blank
# -r Backslash does not act as an escape character. The backslash
# is considered to be part of the line. In particular, a
# back-slash-newline pair may not be used as a line continuation.
# -d delim
# The first character of delim is used to terminate the input
# line, rather than newline.
#
# Here setting -d to nul or 0x00. This enables us to capture any file-
# names with the print0 from find.
#
# fn The variable to read into.
#
while IFS= read -r -d $'\x00' fn; do
printf "Will perhaps be compressing file %s\n" "$fn" >> "$logclean_file"
# If file + gz does not exist
if [[ ! -e "$fn.gz" ]]; then
if gzip command "$fn"; then
echo "Horray! success!" >> "$logclean_file"
else
echo "Harf! Gzip failed." >> "$logclean_file"
fi
else
echo "Nah. Already exists." >> "$logclean_file"
fi
done < <(find . -type f \( ! -name '*.gz' \) -a \( ! -name '*.Z' \) -print0)
# Notice -print0 at end which means find will print filenames, - separating
# them with 0x00 instead of new-line
echo
もprint
も実際には移植可能ではありません。 print
はksh93に固有ですが、echo
はPOSIXプロセスの非常に遅い時点で標準化されただけであり、古いバージョンを信頼して予測可能にすることはできません結果。 echo vs print および printfの方が優れている理由... を参照してください。
printf "Some %s\n" "$var" >> "$foo"
# Or
echo "Some $var" >> "$foo"
いくつかの参考文献:
TLDPガイドの場合、および stylish を使用している場合は、スクリーンリーディングのために one of these をお勧めします。
パイプで出力ファイルにアクセスしようとしている場合は、ファイルパスを繰り返し処理します...
...反復を処理します。
サブシェルを介して明示的なファイルリストを繰り返し処理し、expand
を呼び出してstdoutに出力し、個々のファイルを上書きしたいと思っていました。 この回答で対処される問題 。
find $(git ls-files | grep '.py$') \
-exec bash -c 'expand -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \;
このスレッドの他の回答は汎用的な解決策を提供していないので、私はその回答のわずかに変更されたバージョンを公開します。これは私が見た中で動作する唯一のものであり、ファイルパスを反復するときにのみ適用されます。
Findを使用してその名前を反復できるように、反復項目にちなんで名付けられたファイルを乱用して作成するソリューションを想像できます。
失敗した試行:
そのため、find exec呼び出しと同様に、forループ内でbash -c ...
を使用してみました。しかし、出力ファイルが空になるという同じ問題に遭遇しました。
免責事項:私の問題が正確に何であるかわかりません、find execを使用する前のすべての試みで、書き込みが予想されていたすべての出力ファイルが上書きされ、私の問題はその後は空のファイルでした。