使用带有数组和值的 diff 时出现问题 SHELL [duplicate]

Issue using diff with array and value quoted SHELL [duplicate]

提问人:Marc-Antoine Xavier 提问时间:12/7/2022 最后编辑:Marc-Antoine Xavier 更新时间:12/8/2022 访问量:39

问:

嗨,大家好,我在使用 diff 时遇到了问题。

在我的脚本中,我试图将 1 个目录中的所有文件与其他 2 个目录中的所有文件进行比较 使用 diff 进行比较是文件是相同的。

这是我的剧本: `

#!/bin/bash

files1=()
files2=()

# Directories to compare. Adding quotes at the begining and at the end of each files found in content1 & content3

content2=$(find /data/logs -name "*.log" -type f)
content1=$(find /data/other/logs1 -type f | sed 's/^/"/g' | sed 's/$/"/g')
content3=$(find /data/other/logs2 -type f | sed 's/^/"/g' | sed 's/$/"/g')

# ADDING CONTENT INTO FILES1 & FILES2 ARRAY
while read -r line; do
        files1+=("$line")
done <<< "$content1"

# content1 and content3 goes into the same array
while read -r line3;do
        files1+=("$line3")
done <<< "$content3"

while read -r line2; do
        files2+=("$line2")
done <<< "$content2"


# Here i'm trying to compare 1 by 1 the files in files2 to all files1
for ((i=0; i<${#files2[@]}; i++))
do
        for ((j=0; j<${#files1[@]}; j++))
        do
                if [[ -n ${files2[$i]} ]];then
                        diff -s "${files2[$i]}" "${files1[$j]}" > /dev/null
                        if [[ $? == 0 ]]; then
                                echo ${files1[$j]} "est identique a" ${files2[$i]}
                                unset 'files2[$i]'
                                break
                        fi
                fi
        done
done

#SHOW THE FILES WHO DIDN'T MATCHED
echo ${files2[@]}

`

当我尝试差异时,我遇到了以下问题: diff: “/data/content3/other/log2/perso log/somelog.log”: 没有这样的文件或目录

但是当我在做的时候

ll "/data/content3/other/log2/perso log/somelog.log" -rw-rw-r-- 2 lopom lopom 551M 30 oct. 18:53 '/data/content3/other/logs2/perso log/somelog.log'

所以文件存在。

我需要这些引号,因为有时路径中有空间

some1 知道如何解决这个问题吗?

谢谢。

我已经尝试用单引号更改引号,但它没有修复它

数组 shell 行情

评论

1赞 Charles Duffy 12/8/2022
readarray -d '' files1 < <(find /data/logs -name '*.log' -type f -print0)-- 首先创建字符串,将文件名混合在一起,在字符串中添加引号,然后尝试将该内容解析为数组,这只是添加了额外的失败模式。
1赞 Charles Duffy 12/8/2022
请记住,文件名可以包含引号。文件名可以包含换行符。文件名可以包含通配符。文件名可以包含二进制数据,但 NUL 除外——这就是为什么 NUL 和其他字符可以安全地用于分隔路径列表的原因(单个文件名中不能存在的另一个字符是 ,但它确实存在于路径中)。当您尝试将文件名列表存储在单个字符串中时,您相信这些名称会符合一组假定的规则,并在它们不符合这些规则时给自己带来麻烦。/

答:

0赞 Paul Hodges 12/8/2022 #1

首先,不要这样做——

content2=$(find /data/logs -name "*.log" -type f)
content1=$(find /data/other/logs1 -type f | sed 's/^/"/g' | sed 's/$/"/g')
content3=$(find /data/other/logs2 -type f | sed 's/^/"/g' | sed 's/$/"/g')

不要将所有这些堆叠到单个变量中。这是在问十种晦涩难懂的麻烦。更重要的是,这些调用将引号作为文件名的一部分嵌入到数据中,这可能是导致崩溃的原因,因为名称中没有带有引号的实际文件。seddiff

另外,如果您要丢弃输出,而只是用于检查文件是否相同,请尝试。它是静默的,而且速度要快得多,因为它在第一个不同的字节处退出,而无需读取两个文件的其余部分并生成报告。如果有很多文件,这将加起来。diffcmp-s

如果日志是目录中唯一的东西,并且您不必扫描子目录,并且文件名不能同时出现在 /data/other/logs1 和 /data/other/logs2 中,但您非常确定它至少会出现在其中一个中......然后简化:

for f in /data/logs/*.log                     # I'll assume these are all files...
do  t=/data/other/logs[12]/"${f#/data/logs/}" # always just one?
    if cmp -s "$f" "$t"                       # cmp -s *has* no output
    then echo "$t est identique a $f"         # files are same
    elif [[ -e "$t" ]]                        # check t exists
    then echo "$t diffère de $f"              # maybe ls -l "$f" "$t" ?  
    else echo "$t n'existe pas"               # report it does not
    fi
done

这不需要数组,没有,没有调用等。findsed

如果您确实需要读取子目录,请使用 glob 来处理它,这样您就不必担心使用 .(c.f. https://mywiki.wooledge.org/ParsingLs 出于某些原因。shoptread

shopt -s globstar
for f in /data/logs/**/*.log   # globstar makes ** match at arbitrary depth
do  for t in /data/other/logs[12]/**/"${f#/data/logs/}" # if >1 possible hit
    do  if cmp -s "$f" "$t" 
        then echo "$t est identique a $f"
        elif [[ -e "$t" ]]
        then echo "$t diffère de $f" 
        else echo "$t n'existe pas"  # $t will be the glob, one iteration
        fi
    done
done