パスから追加の「./」を削除するにはどうすればよいですか?

パスから追加の「./」を削除するにはどうすればよいですか?

次のスクリプトを検討してください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。オブジェクトがソートされたプロセスにあり、最初に収集(名前、サイズ、年齢)され、次にロジックが導入されるバッチスタイルのアプローチを紹介します。次のコードを使用すると、出力が見やすく印刷されるか、onlywould cp "$2/$1" "$3/$1"differwould overwrite、olderwould 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"

関連情報