Bash: 检查文件是否包含另一个文件的内容

4

我想将一个文件的内容追加到另外一个文件中,如果该内容尚未包含在其中。下面是我的尝试:

catAndAppendIfMissing(){
    [[ ! -s $2 ]] && touch "$2" || [[ ! -s $2 ]] && sudo touch "$2"
    if grep $1 $2; then
        echo "found"
    else
        catAndAppend $1 $2       #this appends file $1 contents to file $2 (and takes care of adding newlines if needed and uses sudo if needed, thus the separate function)
    fi
}

使用if grep $1 $2,我试图查看文件$1的内容是否出现在文件$2中。那就是不按预期工作的部分:
当我在同一个文件上运行两次时,它将简单地两次将相同的文本附加到目标文件中。
如何解决这个问题?
准化:
  • 我正在使用OSX 10.11.5(但Linux / 跨平台的解决方案对于我在家中或其他人阅读此文都可能相关)
  • 我选择使用catAndAppend而不是cat $file1 >> $file2来处理需要sudo的情况,并通过需要添加换行符来将附加的内容与已有内容分开。
  • 如果文件$1出现在文件$2中的任何位置(不仅仅是开头或结尾),我不希望进行追加操作。
  • 作为信息,以下是我对之一的文件$1内容尝试:
alias ls='ls -a'
alias mkdir="mkdir -pv"
alias wget="wget -c"
alias histg="history | grep"
alias echopath='echo $PATH | tr -s ":" "\n"'
alias myip="curl -sSL http://ipecho.net/plain | xargs echo"
alias webpic="mogrify -resize 690\> *.png"

alias cddog='cd ~/dev/go/src/github.com/dogtools/dog'
alias xp='cd ~/dev/go/src/experiments'
  • 但我需要将其与包含变量导出、代码、命令、配置或任何类型的文本的其他文件一起使用

"if grep $1 $2" 应该改为 'if grep cat $1 $2'。 - Ronak Patel
catAndAppend ? cat "$1" >> "$2"?catAndAppendcat "$1" >> "$2" - Andreas Louv
catAndAppendpermission denied文件一起工作,并在追加内容后添加换行符,以适应将来对同一文件的追加操作。这就是为什么我使用自定义的方法而不是cat "$1" >> "$2" - Nicolas Marshall
同时,@BigDataLearner,如果 if grep cat $1 $2 仍然失败(它总是进入 else 分支并追加文件已经包含的内容)。 - Nicolas Marshall
你尝试过单独执行 grep 命令吗?在担心其他东西之前先让它在命令行中正常工作。Grep 通常使用单词/短语/正则表达式作为搜索目标,不会尝试打开 file1 并获取其中的所有“单词”。您可以尝试调整 comm 的输出以查看两个文件是否相同。祝你好运。 - shellter
4个回答

4

如果文件$1出现在文件$2任何位置,则不要追加:

catAndAppendIfMissing(){
    f1=$(wc -c < "$1")
    diff  -y <(od -An -tx1 -w1 -v "$1") <(od -An -tx1 -w1 -v "$2") | \
    rev | cut -f2 | uniq -c | grep -v '[>|]' | numgrep /${f1}../ | \
    grep -q -m1 '.+*' || cat "$1" >> "$2";     }

工作原理:

  1. 使用wc计算文件$1中的字符数。
  2. 使用od为两个文件生成每行一个字节的十六进制转储,并使用bashism命令获取一个diff文件,并将其传输到...
  3. rev,然后cut个字段,并对连续具有空格而不是“>”的行进行uniq计数。
  4. 如果其中一个计数等于或大于$f1,则可以追加。这可以通过变量来检查,但numgrep很方便并且有助于避免使用变量。

注意事项:优点是适用于二进制文件。缺点是效率低下,od读取两个文件的全部内容,而diff读取od输出的全部内容。如果file1是一个单行字符串,且该字符串位于1TB的file2的第一行中,则会浪费大量时间。


(旧版本). 如果文件$1已经追加到文件$2中,则不要进行追加:

catAndAppendIfMissing(){
    f1=$(wc -c < "$1")
    f2=$(wc -c < "$2")
    [ $f1 -le $f2 ] &&  cmp -s "$1" "$2" 0 $(( $f2 - $f1 )) && return 1 
    cat "$1" >> "$2"
    }

工作原理:

  1. 使用wc获取文件长度,将其存储在$f1$f2中。
  2. 如果第一个文件比第二个文件更长(或者更短,如果cmp显示第一个文件没有被已经追加到第二个文件中),则使用cat将其附加到第二个文件中。否则,返回错误代码。

如果我要查找的字符串位于文件末尾,则该方法可行。如果字符串出现在文件其他位置,它会再次进行追加。 - Nicolas Marshall
@n-marshall,由于Q有点“追加”倾向,给人的印象是目标仅仅是避免双重“追加”。请考虑改进Q的措辞以澄清$f1$不应出现在$f2$的任何地方这一点。 - agc
没错,我之前没有意识到表达不清楚。无论如何,现在已经编辑好了 :) - Nicolas Marshall
我终于有时间尝试你的新解决方案,但是现在出现了一个错误:./common/configs/.shell-functions: line 65: syntax error near unexpected token \('`./common/configs/.shell-functions: line 65: \ diff -y <(od -An -tx1 -w1 -v "$1") <(od -An -tx1 -w1 -v "$2") | '`我肯定需要一些帮助,因为我无法理解那段代码(是的,即使有解释! :) 我不知道你是如何想出那个解决方案的) - Nicolas Marshall
@n-marshall,这是bash代码,如果在像dash这样的shell中运行,就会出现这种错误。为了验证,请问脚本的第一行是#!/bin/bash还是#!/bin/sh?如果是#!/bin/sh,那么ls -l /bin/sh显示什么? - agc
显示剩余10条评论

1

也许没有必要尝试有条件地更新文件;只需引用每个文件以确保定义了所有别名,然后无条件地将alias的输出存储到您原本要附加的文件中。

source "$1"   # Original aliases
source "$2"   # New aliases
alias > "$1"  # Combined aliases

这个只包含别名的文件只是我想要处理的文件之一。我有其他包含导出、函数、配置文件等的文件...基本上我正在尝试将系统的所有配置文件放在git上。 - Nicolas Marshall
我选择这个例子只是因为它有一些非平凡的事情需要处理,比如换行符(\n和文件中的字面换行符)以及各种引号。 - Nicolas Marshall

0

我完成了我的作业,并想出了一个解决方案,几乎符合要求,唯一的区别是它是用Python而不是Bash编写的。然后从Bash中调用我的Python脚本。

这是代码:

import re, os, subprocess, mmap, sys, pprint 

def isFile1InFile2(file1Path, file2Path): 
    with open(file2Path) as file2: 
        file2Access = mmap.mmap(file2.fileno(), 0, access=mmap.ACCESS_READ) 
        file1Contents = open(file1Path).read() 
        if file2Access.find(file1Contents) != -1: 
            return True 
        else: 
            return False 

def appendIfMissing(source, dest): 
    destFullPath = os.path.expanduser(dest) 
    if os.path.isfile(destFullPath): 
        if isFile1InFile2(source, destFullPath): 
            print ('Source\'s contents found in dest file, no need to append') 
        else: 
            print('Source\'s contents cannot be found in dest file, appending...') 
            # append source file to destfile 
            command = ' '.join(['source', './common/configs/.shell-functions', '&&', 'catAndAppend', source, destFullPath]) 
            os.system(command) 

    else: 
        print "destfile not a file yet, copying sourcefile to destfile..." 
        # copy source file to destfile 
        command = ' '.join(['source', './common/configs/.shell-functions', '&&', 'catAndAppend', source, destFullPath]) 
        print command 
        os.system(command)

if len(sys.argv) != 3:
    sys.exit('[ERROR] appendIfMissing.py, line 31: number of arguments passed is not 3')
else:
    appendIfMissing(sys.argv[1], sys.argv[2])

然后通过bash调用它:

appendIfMissing(){ 
    python ./common/configs/appendIfMissing.py $1 $2 
} 

通过保持bash函数不变(从python调用的那个函数):

createFileIfMissing(){
    # create file if doesn't exist, with right permission
    [[ ! -s $1 ]] && touch "$1" || [[ ! -s $1 ]] && sudo touch "$1"
}

addNewLineToFile(){
    [[ ! -e $1 ]] || [[ -w $1 ]] && printf "\n" >> $1 || [[ -e $1 ]] && [[ ! -w $1 ]] && sudo bash -c "printf \"\n\" >> $1"
}

catAndAppend(){ 
    createFileIfMissing $2 
    # append stuff to it 
    [[ ! -e $2 ]] || [[ -w $2 ]] && cat $1 >> $2 || [[ -e $2 ]] && [[ ! -w $2 ]] && sudo bash -c "cat $1 >> $2" 
    addNewLineTo $2 
} 

缺点:

  • 它不是bash。我在我的问题中要求的是bash解决方案(但实际上我只关心有解决方案)
  • 它不是bash。由于它是为系统设置脚本而设计的,我必须首先安装Python才能使其工作。但是我最终也想安装Python。

优点:

  • 它比bash更易读/易于维护/可定制(在两种语言中都是新手时,Python更直观易懂)
  • 它跨平台

@agc 我应该接受你的答案还是他的?我的答案是基于 Python 而不是 Bash,而他的答案是基于 Linux 而不是 OSX(你当时不知道,抱歉)。 - Nicolas Marshall
如果我可以为你花费的时间给你小费(用 SO 声望或真钱),请不要犹豫! - Nicolas Marshall

0

行:

if grep $1 $2 

应该是:

if grep `cat $1` $2

或者

file1_Content=`cat $1`

if grep ${file1_Content} $2

或者

file1_Content=`cat $1`
grep ${file1_Content} $2

if [ $? == 0 ];then
  echo "found"
else
  #catAndAppend
fi

1
我真的无法让语法 if grep \cat $1` $2工作... 我会得到像grep: invalid option -- '` 这样的输出。 - Nicolas Marshall

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接