두 개의 스크립트를 만들었습니다. 먼저 모든 폴더의 mtime 타임스탬프를 읽고 time_old.txt에 한 줄씩 씁니다.
for d in */ ; do
time_old=$(stat --format %Y "$d")
escaped=$(printf %q "$d")
echo "$time_old $escaped" >> time_old.txt
done
두 번째는 행을 다시 읽고 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
하고 쉘 언어와 호환되는 형식으로 파일 경로를 인용하는 독립 실행형 유틸리티가 필요합니다 . 이것이 보장되지 않으면 위험해질 수 있습니다. 이는 또한 디렉터리당 하나의 프로세스를 분기하고 실행할 때 효율성을 감소시킵니다( 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"