2つのスクリプトを作成しました。まず、すべてのフォルダのmtimeタイムスタンプを読み取り、time_old.txtに1行ずつ書き込みます。
for d in */ ; do
time_old=$(stat --format %Y "$d")
escaped=$(printf %q "$d")
echo "$time_old $escaped" >> time_old.txt
done
2番目は行を再読み込みし、touch
フォルダの古いタイムスタンプを使用して実行されます。
input="time_old.txt"
while IFS= read -r line
do
echo "$line"
touch_output=$(touch -m -d @$line)
echo "$touch_output"
done < "$input"
これは、同様のフォルダには機能しますFolder1
が、名前が 。Folder2
インフォルダには機能しません。Foo & Bar
私の間違いはどこにありますか?
答え1
代わりに、次のことを行います(GNUツールの前提)。
find . ! -name . -prune -type d -printf '@%T@\0%p\0' > time_old.list
復元:
xargs -t -a time_old.list -r0 -n2 touch -md
を使用する場合は、printf %q
結果をシェルコードとして解釈する必要があります。それは次のとおりです。
find . ! -name . -prune -type d -printf 'touch -md @%T@ ' \
-exec printf '%q\n' {} \; > time_old.bash
そして:
bash -v ./time_old.bash
また覆います。
printf
ただし、この非標準をサポートし、%q
シェル言語と互換性のある形式でファイルパスを引用するスタンドアロンユーティリティが必要です。これが保証されていないと危険になる可能性があります。これにより、ディレクトリごとに1つのプロセスを分岐して実行すると効率が低下します(stat
GNUアプローチと同様)。回避できる場合は、シェルが外部入力から生成されたコードを解釈することはできません。
また、touch
標準出力には何も書きませんのでoutput=$(touch...)
意味がありません。
zsh
ここでは、安全な引用演算子(単一引用符を使用すると、ファイル名に含まれる文字や文字以外のファイル名に関係なく安全でなければならず、shに似たシェル(yashを除く)と互換性がなければなりません)をstat
使用することもできます。実際にはGNUよりも前ののstat
で、GNUismに頼る必要はありません。
zmodload zsh/stat
{
for d (*(ND/))
stat -A t -F @%s.%9. +mtime -- "$d" &&
print -r touch -d $t -- ${(qq)d}
} > time_old.sh
そして回復を使用してくださいsh -v ./time_old.sh
。
答え2
escaped=$(printf %q "$d") touch_output=$(touch -m -d @$line)
私の間違いはどこにありますか?
ファイルからファイル名を引用するように配置しましたが、問題は、引用符がシェルコマンドラインに直接ある場合にのみ機能することです。拡張結果は次のとおりです。いいえ引用符、バックスラッシュエスケープ、またはその他のシェル構文を解析します。代わりに、これらの文字は文字通り処理されます。したがって、これは、、、、$line
に1650819409 Foo\ \&\ Bar
分割されます。これは次の質問に似ています。1650819409
Foo\
\&\
Bar
変数に保存されたコマンドをどのように実行できますか?(eval
シェルを使用して拡張値を解析できますが、これには特に信頼できないデータ修正などの独自の問題があります。)
ファイル名に改行文字を含めることができないと思われる場合は、直接保存できます。これにより、1650819409 Foo & Bar
最初のスペースの後のすべてのエントリがファイル名になるような同様の行が生成されます。
> time_old.txt
for d in */ ; do
case "$d" in
*$'\n'*) printf >&2 "newlines in filenames not supported, skipping %q\n" "$d"; continue;;
esac
time_old=$(stat --format %Y -- "$d") &&
printf '%s\n' "$time_old $d"
done > time_old.txt
input="time_old.txt"
while IFS= read -r line
do
time=${line%% *} # remove from first newline to end
file=${line#* } # remove up to and incl. the first newline
printf >&2 "Changing time on '%s' to '%s'\n" "$file" "$time"
touch -m -d "@$time" -- "$file"
done < "$input"