如何从路径中删除多余的“./”?

如何从路径中删除多余的“./”?

考虑以下脚本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

那真是糟糕的代码。

使这个更容易阅读和理解的第一步是将其分成两个脚本。但也可以只用一个脚本来完成:

#! /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 和samewillignore)将其输入到解释器中。

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"

相关内容