次のスクリプトを検討してくださいcompare_times.sh
(ありがとうございます。http://superuser.com/a/1780500):
#!/bin/bash
# Syntax: compare_times.sh directory_1 directory_2
# Semantics: for pairs of files with the same path at any depth in both directory_1 and directory_2, compare the file modification times and, if they differ, say which of the two files is older.
case "$2" in
/*)
# $2 is an absolute path
cd $1
find . -type f -exec bash -c "if [[ -f \"{}\" && -f \"$2/{}\" ]]; then if (cmp -s \"{}\" \"$2/{}\") then if [[ \"{}\" -ot \"$2/{}\" ]]; then echo \"$1/{} is older than $2/{}\"; else if [[ \"$2/{}\" -ot \"{}\" ]]; then echo \"$2/{} is older than $1/{}\"; fi; fi; fi; fi" \;;;
*)
# $2 is a relative path
WORKING_DIR=$PWD
cd $1
find . -type f -exec bash -c "if [[ -f \"{}\" && -f \"$WORKING_DIR/$2/{}\" ]]; then if (cmp -s \"{}\" \"$WORKING_DIR/$2/{}\") then if [[ \"{}\" -ot \"$WORKING_DIR/$2/{}\" ]]; then echo \"$1/{} is older than $2/{}\"; else if [[ \"$WORKING_DIR/$2/{}\" -ot \"{}\" ]]; then echo \"$2/{} is older than $1/{}\"; fi; fi; fi; fi" \;
esac
計算時間とパス名の潜在的な抜け穴を除いて、スクリプトがうまく機能しているようです。ただし、出力は重複しています./
。
$ pwd
/tmp
$ ls --full-time ad bd | cut -d ' ' -f 6-
ad:
2023-05-14 15:38:02.707216583 +0200 f
bd:
2023-05-14 15:38:06.835165122 +0200 f
$ compare_times.sh ad bd
ad/./f is older than bd/./f
$ compare_times.sh /tmp/ad bd
/tmp/ad/./f is older than bd/./f
$ compare_times.sh ad /tmp/bd
ad/./f is older than /tmp/bd/./f
$ cd ad
$ compare_times.sh . ../bd
././f is older than ../bd/./f
$ compare_times.sh . /tmp/bd
././f is older than /tmp/bd/./f
$ cd ../bd
$ compare_times.sh ../ad .
../ad/./f is older than ././f
$ compare_times.sh /tmp/ad .
/tmp/ad/./f is older than ././f
./
出力を整理して読みやすくするにはどうすればよいですか?たとえば、上記で実行したコマンドの場合、予想される出力は次のようになります。
$ compare_times.sh ad bd
ad/f is older than bd/f
$ compare_times.sh /tmp/ad bd
/tmp/ad/f is older than bd/f
$ compare_times.sh ad /tmp/bd
ad/f is older than /tmp/bd/f
$ cd ad
$ compare_times.sh . ../bd
f is older than ../bd/f
$ compare_times.sh . /tmp/bd
f is older than /tmp/bd/f
$ cd ../bd
$ compare_times.sh ../ad .
../ad/f is older than f
$ compare_times.sh /tmp/ad .
/tmp/ad/f is older than f
答え1
本当に悪いコードです。
これを読みやすく理解するための最初のステップは、2つのスクリプトに分割することです。ただし、1つのスクリプトでも実行できます。
#! /bin/bash -
if [[ "$#" -eq 2 ]]; then
[[ -d "$1" && -d "$2" ]] || exit 2
scriptpath="$(realpath -- "$0")"
d1_path="$(realpath -- "$1")"
d2_path="$(realpath -- "$2")"
PWD_ORI="$(realpath -- "$PWD")"
cd -- "$d1_path" || exit 1
find . -type f -exec "$scriptpath" "$d1_path" "$d2_path" {} "$PWD_ORI" \;
elif [[ "$#" -eq 4 ]]; then
[[ -d "$1" && -d "$2" && -f "$3" ]] || exit 2
d1_path="$1"
d2_path="$2"
file_relpath="$3"
file_relpath="${file_relpath#./}"
f1_path="${d1_path}/${file_relpath}"
f2_path="${d2_path}/${file_relpath}"
PWD_ORI="$4"
if [[ -f "$f1_path" && -f "$f2_path" ]]; then
if cmp -s -- "$f1_path" "$f2_path"; then
if [[ "$f1_path" -ot "$f2_path" ]]; then
printf '%s\n' "'${f1_path#"$PWD_ORI"}' is older than '${f2_path#"$PWD_ORI"}'"
elif [[ "$f2_path" -ot "$f1_path" ]]; then
printf '%s\n' "'${f2_path#"$PWD_ORI"}' is older than '${f1_path#"$PWD_ORI"}'"
fi
fi
fi
fi
答え2
zshに切り替えるのがオプションの場合、同じアプローチを使用する方が簡単で信頼性が高くなります。
#! /bin/zsh -
f1=( ${1?}/**/*(ND.) ) f2=( ${2?}/**/*(ND.) )
f1=( ${f1#$1/} ) f2=( ${f2#$2/} )
for f in ${f1:*f2}; do
f1=$1/$f f2=$2/$f
if cmp -s -- $f1 $f2; then
if [[ $f1 -ot $f2 ]]; then
print -r -- ${(q+)f1} is older than ${(q+)f2}
elif [[ $f2 -ot $f2 ]]; then
print -r -- ${(q+)f2} is older than ${(q+)f1}
fi
fi
done
答え3
申し訳ありません。メソッドがあまりにも多くを好きではありません-exec
。オブジェクトがソートされたプロセスにあり、最初に収集(名前、サイズ、年齢)され、次にロジックが導入されるバッチスタイルのアプローチを紹介します。次のコードを使用すると、出力が見やすく印刷されるか、only
would cp "$2/$1" "$3/$1"
、differ
would overwrite、older
would touch、willignoreなどの一時関数名を使用してsame
インタプリタに提供される準備ができます。
cmp
文字帯域幅はすべてのパーサーを妨げる可能性があり、これはインターフェース(通常は呼び出し)の解析が必要なすべてのソリューションスタイルで深刻な問題です。インジェクションと文字帯域幅の高い標準により、zsh
代替回答に示されている文字列処理に感銘を受けました。
#!/bin/bash
# <$1: mandatory directory
# <$2: mandatory directory
# <$FS: optional separator (default ";")
# <$RS: optional separator (default "\n")
# >stdout: differ|older|same|only relative_path directory directory
[ -d "$1" ] && [ -d "$2" ] && [[ "$1" != "$2" ]] || exit 8
FS="${FS:-;}"
RS="${RS:-$'\n'}"
(
find "$1" -type f -printf "%T@$FS%s$FS$1$FS%P$RS";
find "$2" -type f -printf "%T@$FS%s$FS$2$FS%P$RS";
) | # mod time ; size ; directory ; relative path
LC_COLLATE=C sort -t "$FS" -k4 -k1,1n |
awk 'NF!=4{ print "ERROR: lost parsing "FNR":"$0 >"/dev/stderr"
exit 8
}
function alter(dir) { return (dir == d1 ? d2 : d1) }
function quote(s){ return SQ gensub(SQ,BQ,"g",s) SQ }
# differ 'path' 'dir1' 'dir2' : older in dir1 than in dir2 and have different content
# older 'path' 'dir1' 'dir2' : older in dir1 than in dir2 and have same content
# same 'path' 'dir1' 'dir2' : same age in dir1 than in dir2 and have same content
pname && pname == $4 {
cmp = "cmp -s "quote(pdir"/"pname)" "quote(alter(pdir)"/"pname)
print( (psize == $2 && !system(cmp)) ? (ptime == $1 ? "same" : "older") : "differ",
quote(pname), quote(pdir), quote(alter(pdir)))
pname = ""; next
}
# only 'path' 'dir1' 'dir2' : path exist in dir1 not in dir2
pname { print("only", quote(pname), quote(pdir), quote(alter(pdir)))
pname = ""
}
END { if (pname) print("only", quote(pname), quote(pdir), quote(alter(pdir)))
}
{ pname = $4; pdir = $3; psize = $2; ptime = $1;
}
' d1="$1" d2="$2" FS="$FS" RS="$RS" OFS=" " SQ="'" BQ="'\\\\\\\\''"
同じアプローチを使用しますが、注入の機会が少なく、単一の存在または違いの検出がない場合は、上記のfdupes
パイプラインで並べ替えることができる年齢と類似性を含む多くの情報を収集します。機能を追加したい場合は、収集した他のファイルシステム情報を便利な形式にまとめることができます。
#!/bin/bash
# <$1: mandatory directory
# <$2: mandatory directory
# >stdout: "older" relative_path directory directory
[ -d "$1" ] && [ -d "$2" ] && [[ "$1" != "$2" ]] || exit 8
fdupes -q -t -r "$1" "$2" |
awk '
!NF{same++; next} # similarity id
NF > 1 && $1" "$2 ~ "....-..-.. ..:.." {
sub(" "d1"/"," {D1} "); sub(" "d2"/"," {D2} ")
printf(ORS"%06d %s", same,$0)
next}
{printf("\\n%s",$0)} # newline in name
' d1="$1" d2="$2" | LC_COLLATE=C sort -k5 -k2,3 |
# same yyyy-mm-dd HH:MM {D} name
awk '
function direc(dir) { return (dir == "{D1}" ? d1 : d2) }
function alter(dir) { return (dir == "{D1}" ? d2 : d1) }
pname && pname == $5 {
if ( psame == $1 && ptime != $2" "$3 )
print("older", pname, direc(pdir), alter(pdir))
pname = ""; next
}
{ pname = $5; pdir = $4; psame = $1; ptime = $2" "$3 }
' d1="$1" d2="$2"
答え4
s/\/\.\//\//g
/./
sed(ストリームエディタ)を使用して、出力/
のすべての位置を変更します。
compare_times.sh ad bd | sed -e "s/\/\.\//\//g"
スクリプト内でsedを適用するには、スクリプト内のbash機能で操作を実行する必要があります。
function my_function(){
... previous script goes here ...
}
my_function $1 $2 | sed -e "s/\/\.\//\//g"