【问题标题】:bash script: find content in file between specific lines and run command on content, replace old content with the output of commandbash脚本:在特定行之间的文件中查找内容并对内容运行命令,用命令的输出替换旧内容
【发布时间】:2014-04-06 14:26:38
【问题描述】:

我是一个真正的脚本新手,之前只用一些 vars、ifs、简单的 grep、awk 等命令编写了真正简单的脚本。

问:我有几千个带有明文的文件(电子邮件)和(有时)几个独立的 GPG 加密文本部分,如下所示:

several lines of
cleartext stuff (more specifically: email headers)

-----BEGIN PGP MESSAGE-----
RTDHNRFSGNRTDHNRFSGNRTDHNRFSGN
RTDHNRFSGNRTDHNRFSGNRTDHNRFSGN
-----END PGP MESSAGE-----

some more lines
of cleartext

-----BEGIN PGP MESSAGE-----
WPGLUFPJUWPGLUFPJUWPGLUFPJU
WPGLUFPJUWPGLUFPJUWPGLUFPJU
-----END PGP MESSAGE-----

我正在尝试制作一个(最好是)bash 脚本,该脚本遍历文件夹中的所有文件,找到 GPG 加密文本的每个实例,对其进行解密,并用解密后的文本替换旧的加密文本,然后保存文件. 因此,当脚本完成后,上述假设文件如下所示:

several lines of
cleartext stuff (more specifically: email headers)

decrypted message #1

some more lines
of cleartext

decrypted message #2

当尝试仅使用 GPG 解密文件时,GPG 将跳过所有明文内容并仅输出 first 解密消息。

所以我认为我需要类似while循环的东西,以独立查找以“-----BEGIN PGP MESSAGE-----”开头并以“-----END PGP MESSAGE-”结尾的所有实例----" 并在其上使用 GPG 命令,然后用 GPG 命令的输出替换该实例。然后继续下一个密文实例。

到目前为止,我只有这几行,但它们显然不能正确地做我想要的。我不想在每个单独的文件上使用脚本。而且我不想使用临时文件,我想有更好的方法来完成所有这些。

#!/bin/bash

TEMPFILE="${1}.tmp"

## grep only the relevant gpg lines to decrypt.
## this will output ALL encrypted instances to $TEMPFILE
sed -n '/^-----BEGIN PGP MESSAGE/,/^-----END PGP MESSAGE/p' "$1" > "$TEMPFILE"

## decrypt. this will only give me the decrypted output
## of the first encrypted instance in $TEMPFILE.
## and I don't know how to shove this into the proper place in the original file.
gpg --batch -d --no-tty --output "${1}.dc.eml" "$TEMPFILE"

## remove $TEMPFILE
rm "$TEMPFILE"

这是我编造的脚本语言,希望能更好地解释我想要做什么:

for all files in folder; do
    while i can find an instance of "-----BEGIN PGP" to "-----END PGP"; do
        command: gpg decrypt > $tempvar
        command: replace the instance of "-----BEGIN PGP" to "-----END PGP" with $tempvar
    end while
end for

这可能很容易实现(我希望如此),但我已经陷入这个解密困境好几天了,我无法正确弄清楚如何去做。任何对正确方向的帮助或提示都会对我有很大帮助。

编辑:最终代码,感谢 glenn jackman! :

for file in *; do
    in_pgp_section=false
    pgp_text=""

    while IFS= read -r line; do
        if [[ $line == *BEGIN\ PGP\ MESSAGE* ]]; then
            in_pgp_section=true
        fi

        if ! $in_pgp_section; then
            printf "%s" "$line"
            continue
        fi

        pgp_text+="$line"$'\n'

        if [[ $line == *END\ PGP\ MESSAGE* ]]; then
            printf "%s" "$pgp_text" | gpg --batch -d --no-tty --use-agent
            in_pgp_section=false
            pgp_text=""
        fi
    done < "$file" > "$file.decrypted"
done

【问题讨论】:

    标签: bash shell replace find gnupg


    【解决方案1】:

    未经测试

    for file in *; do
        in_pgp_section=false
        pgp_text=""
    
        while read line; do
            if [[ $line == "-----BEGIN PGP MESSAGE-----" ]]; then
                in_pgp_section=true
            fi
    
            if ! $in_pgp_section; then
                echo "$line"
                continue
            fi
    
            pgp_text+="$line"$'\n'
    
            if [[ $line == "-----END PGP MESSAGE-----" ]]; then
                printf "%s" "$pgp_text" | gpg -d
                in_pgp_section=false
                pgp_text=""
            fi
        done < "$file" > "$file.decrypting"
    
        ln "$file" "$file.encrypted"  &&
        mv "$file.decrypting" "$file"
    done
    

    这应该解密当前目录中所有文件的所有 PGP 部分,并将原始文件保留为“.encrypted”扩展名

    【讨论】:

    • 您的代码一开始无法正常工作,我尝试了一些操作并让它工作。我将最终代码放在上面的初始帖子中。但是还有一个我还没有弄清楚的问题,在我的原始文件中,有些行以空格或制表符开头,但这些空格和制表符不会出现在解密的文件中。相反,这些行从原始行的第一个字母数字字符开始。我想“if !$in_pgp_section; then” echo/printf 命令有一些东西我必须做才能使它工作。非常感谢您到目前为止的帮助,我从玩您的代码中学到了很多东西!
    • 更改为 while IFS= read -r line; do -- 将 IFS 设置为 null 将使行中的所有空格保持完整。
    【解决方案2】:

    这不是答案,而是朝着正确方向迈出的一步:

    awk '/^-----BEGIN PGP MESSAGE-----$/{store=1;txt="";}
         {if(store==0){print}else{txt=txt"\n"$0}}
         /^-----END PGP MESSAGE-----$/{store=0;print tolower(txt)}' t.txt
    

    /^-----BEGIN PGP MESSAGE-----$/{store=1;txt="";} 当行匹配时,我们初始化变量 txt 并将标志 store 设置为 1/^-----END PGP MESSAGE-----$/{store=0;print txt}

    {if(store==0){print}else{txt=txt"\n"$0}} 对于每一行,如果标志为 0,我们打印该行,否则我们将该行存储(附加)在txt

    /^-----END PGP MESSAGE-----$/{store=0;print tolower(txt)} 当行匹配时,我们取消设置标志并做有趣的部分(我只是打印小写...)。那是你现在的工作。您可能需要调用 system("gpg") 并使用一些管道。祝你好运!

    【讨论】:

    • 我不太清楚如何正确地管道和实现你的代码,所以我把时间花在了 glenn jackmans 的回答上。但我真的很感激帮助!我确实从您的帖子中至少学到了一些东西!
    • 嗯,是的,它比我还难!我什至不知道我的解决方案是否可行……您可以使用 awk 编写文件,例如 glenn jackman 的解决方案。但这并不那么令人满意。我会继续寻找。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-28
    • 1970-01-01
    • 2021-12-15
    • 2014-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多