AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / computer / 问题 / 1704369
Accepted
WesternGun
WesternGun
Asked: 2022-02-12 00:41:26 +0800 CST2022-02-12 00:41:26 +0800 CST 2022-02-12 00:41:26 +0800 CST

tar 使用管道删除文件而不提取文件:正确的语法是什么?

  • 772

我正在使用 GNU tar 来处理 tar(docker 映像的层)来修改其中的一些 jar。我在做:

  • 将图像作为 tar 保存到磁盘
  • 提取它,所以我将每一层都放在一个目录中
  • 进入每一层,我有一个layer.tar,一个json和一个VERSION
  • 迭代所有*/*.jar文件layer.tar,试图找到一些类文件
  • 如果我找到它们,提取具有文件树结构的 jar,从中删除类文件,然后将其放回layer.tar,覆盖原始 jar
  • 将每一层打包回一个新的 tar,使用 docker 加载并稍后推送(尚未完成)

我为此创建了一个脚本,它几乎可以完成工作,但有 2 个罐子一个接一个,一个带有要删除的类,另一个没有它。

#!/bin/bash

# tar needs find to package without ".". u for update, c for create
function pack_all_without_period() {
    find $1 -printf "%P\n" -type f -o -type l -o -type d | sudo tar -$3vf $2 --no-recursion -C $1 -T -
}

if [ -z $1 ]; then
    printf "Save the image as tar, extract, and enter each layer to remove the vulnerable classes(JMSAppender/SocketServer/SimpleSocketServer)\nPlease provide the image name. \n"
    exit 1
fi
dir="log4j-1.x-fix"
image_tar=amq-image-to-fix.tar
if [ ! -d $dir ]; then 
    mkdir $dir
fi
# save image to tar
docker save $1 -o $image_tar
# extract tar
tar xf $image_tar -C $dir
# each layer is extracted to a folder, each folder has a "layer.tar". 
# Go into each folder, extract `layer.tar`, and use `jar` to remove the classes
# and package them back to `layer.tar` (-a to append), and delete the extracted folders.
# at last, package all layers + manifest.json and so back into another tar, WITHOUT COMPRESSION
cd $dir
# enter layer and exit
for layer in */; do
    echo Processing layer $layer
    cd $layer
    # tar does not support overwrite, as tape cannot be overwritten; so I wanted to remove the original jar from tar, 
    # then append it back with tar -u/-A/-r; but then I found tar --delete is extremely slow(by design)
    # so at last I have to extract all files and package them back
    mkdir temp
    sudo tar --extract --directory=temp --file layer.tar --wildcards "*.jar"   # file tree is preserved, so package them back is easy
    if [[ $? -eq 0 ]]; then 
        for f in $(find . -mindepth 2 -name "*.jar" -not -type l -printf "%P\n"); do # exclude jolokia.jar(link)
            sudo jar -tvf $f | grep -E "(*JMSAppender*.class|*SocketServer.class|*log4j*.class)"
            if [[ $? -eq 0 ]]; then
                echo Found classes in $f
                read -p "Do you want to remove these classes? (Y/N) " option
                if [[ $option == 'Y' || $option == 'y' ]]; then
                    echo Removing class file from $f
                    sudo zip -d $f "*JMSAppender.class" "*SocketServer.class" "*SimpleSocketServer.class"
                    ######### here I need to delete the original jar with the classes I just deleted, but I don't know how ############
                else continue
                fi
            else
                continue
            fi

        done
        # append folders to tar, without leading "."
        echo Appending modified folders to layer.tar anew
        pack_all_without_period temp layer.tar r
    fi
    sudo rm -r $(find . -maxdepth 1 -mindepth 1 -type d -print)
    cd .. # back to $dir
done
cd ..

# tar will always include a folder "." as root. This function get rid of it, so the archive
# only contains the content of the folder
# compress will preserve ownership and group by default; and to extract while preserving the same info,
# we use '--same-owner', which is used by default when using sudo. 
# again, append all layers and files to new tar, without leading "."
echo after processing all layers, we are at $(pwd)
pack_all_without_period $dir amq-image-fixed.tar c
sudo rm -Irv $dir $image_tar




但我发现:

  1. tar只能追加,不会覆盖。所以我改变了它,所以我会先删除原来的 jarlayer.tar然后追加
  2. 然后我发现它tar --delete some/path/foo.tar不适用于tar --file xxx --delete path-to-jar. GNU tar 文档声称--delete可以在标准输入和标准输出的管道中工作(https://www.gnu.org/software/tar/manual/html_node/delete.html)但是正确的语法是什么?我尝试了这些但没有工作:
    sudo tar tf layer.tar $f | sudo tar --delete #not deleting
    sudo tar xf layer.tar --exclude $f | sudo tar cf layer.tar -T -  # create tar of size 0

还有一些注意事项:

  • 我不想提取所有文件,因为每一层都包含/usr或/boot我不想处理。我的罐子基本上都在下面/opt(不是 100% 确定)
  • 我需要保留所有权/时间戳等。这就是我使用的原因sudo(但不确定这是否能达到我的目的)

我使用这样的脚本:

./remove-log4j-1.x-classes.sh registry.access.redhat.com/jboss-amq-6/amq63-openshift:1.4-44.1638430186

请帮忙,谢谢!

编辑:我现在尝试:

tar tf layer.tar -O | tar f - --delete $f > layer-new.tar

或者

zcat -f layer.tar | tar f - --delete $f > layer-new.tar

但我因错误而失败:

tar: opt/amq/lib/optional/log4j-1.2.17.redhat-1.jar: Not found in archive
tar: Exiting with failure status due to previous errors
linux tar
  • 1 1 个回答
  • 148 Views

1 个回答

  • Voted
  1. Best Answer
    WesternGun
    2022-02-12T07:46:29+08:002022-02-12T07:46:29+08:00

    现在检查 tar 的版本后:

    $ tar --version
    tar (GNU tar) 1.29
    Copyright (C) 2015 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.
    
    Written by John Gilmore and Jay Fenlason.
    

    我去了 GNU Tar 页面并下载了最新版本,现在是 1.34

    https://ftp.gnu.org/gnu/tar/tar-latest.tar.gz

    组织良好的 repo,因为它还包含/tests. delete在这里,我发现了几个以和 in开头的测试用例delete02.at,我找到了正确的语法(毫不奇怪,它说从标准输入删除带有存档的成员不能正常工作。实际上它适用于 tar 1.29 和 1.34,所以你可以跳过 1.34 的安装):

    
    # Deleting a member with the archive from stdin was not working correctly.
    
    AT_SETUP([deleting a member from stdin archive])
    AT_KEYWORDS([delete delete02])
    
    AT_TAR_CHECK([
    genfile -l 3073 -p zeros --file 1
    cp 1 2
    cp 2 3
    tar cf archive 1 2 3
    tar tf archive
    cat archive | tar f - --delete 2 > archive2
    echo separator
    tar tf archive2],
    [0],
    [1
    2
    3
    separator
    1
    3
    ])
    
    AT_CLEANUP
    

    所以,现在的语法是:

    cat tar_archive | tar f - --delete <filename_to_delete> > another_archive
    

    您用于cat获取 tarball 的内容、管道 ( |) 到tar自身,并使用来自标准输入 ( -,现在是 ) 的文件进行处理cat,并删除并重定向 ( >) 到另一个文件。在此之后,您可以将此新文件重命名为要替换的原始存档名称。但是,您不能就地编辑。

    如果要安装它,请使用./configure && sudo make && sudo make install. 奇怪的是,它不会替换tar 1.29under /bin,而是安装在/usr/local/bin/tar.

    所以现在完整的脚本是:

    #!/bin/bash
    
    tar=/usr/local/bin/tar # or tar=/bin/tar, the syntax is the same
    
    # tar needs find to package without ".". u for update, c for create
    function pack_all_without_period() {
        find $1 -printf "%P\n" -type f -o -type l -o -type d | sudo $tar -$3f $2 --no-recursion -C $1 -T -
    }
    
    if [ -z $1 ]; then
        printf "Save the image as tar, extract, and enter each layer to remove the vulnerable classes(JMSAppender/SocketServer/SimpleSocketServer)\nPlease provide the image name. \n"
        exit 1
    fi
    dir="fix"
    image_tar=amq-image-to-fix.tar
    if [ ! -d $dir ]; then 
        mkdir $dir
    fi
    # save image to tar
    docker save $1 -o $image_tar
    # extract tar
    $tar xf $image_tar -C $dir
    # each layer is extracted to a folder, each folder has a "layer.tar". 
    # Go into each folder, extract `layer.tar`, and use `jar` to remove the classes
    # and package them back to `layer.tar` (-a to append), and delete the extracted folders.
    # at last, package all layers + manifest.json and so back into another tar, WITHOUT COMPRESSION
    cd $dir
    # enter layer and exit
    for layer in */; do
        echo Processing layer $layer
        cd $layer
        # tar does not support overwrite, as tape cannot be overwritten; so I wanted to remove the original jar from tar, 
        # then append it back with tar -u/-A/-r; but then I found tar --delete is extremely slow(by design)
        # so at last I have to extract all files and package them back
        sudo $tar --extract --directory=. --file layer.tar --wildcards "*.jar"   # file tree is preserved, so package them back is easy
        if [[ $? -eq 0 ]]; then 
            for f in $(find . -mindepth 1 -name "*.jar" -not -type l -printf "%P\n"); do # exclude jolokia.jar(link)
                sudo jar -tvf $f | grep -E "(*JMSAppender*.class|*SocketServer.class|*log4j*.class)"
                if [[ $? -eq 0 ]]; then
                    echo Found classes in $f
                    read -p "Do you want to remove these classes? (Y/N) " option
                    if [[ $option == 'Y' || $option == 'y' ]]; then
                        echo Removing class file from $f
                        sudo zip -d $f "*JMSAppender.class" "*SocketServer.class" "*SimpleSocketServer.class"
                        ######### here the correct syntax, finally #########
                        cat layer.tar | tar f - --delete $f > layer-new.tar
                        sudo mv layer-new.tar layer.tar
                        tar -rf layer.tar $f
                    else continue
                    fi
                else
                    continue
                fi
    
            done
            
            sudo rm -r $(find . -maxdepth 1 -mindepth 1 -type d -print)
        fi
        cd .. # back to $dir
    done
    
    cd ..
    
    # tar will always include a folder "." as root. This function get rid of it, so the archive
    # only contains the content of the folder
    # compress will preserve ownership and group by default; and to extract while preserving the same info,
    # we use '--same-owner', which is used by default when using sudo. 
    # again, append all layers and files to new tar, without leading "."
    echo after processing all layers, we are at $(pwd)
    pack_all_without_period $dir amq-image-fixed.tar c
    sudo rm -Irv $dir $image_tar
    
    
    • 1

相关问题

  • 如何让我的 Linux 机器看起来像是在运行 Windows?

  • 对于 cp 或 mv,是否有等同于 cd - 的东西?

  • 以 root 身份运行 docker 容器

  • 如何在域和 Linux 活动目录中启用指纹传感器

  • 如何在CentOS 7 中将Ctrl+C 永久更改为Ctrl+K?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    如何减少“vmmem”进程的消耗?

    • 11 个回答
  • Marko Smith

    从 Microsoft Stream 下载视频

    • 4 个回答
  • Marko Smith

    Google Chrome DevTools 无法解析 SourceMap:chrome-extension

    • 6 个回答
  • Marko Smith

    Windows 照片查看器因为内存不足而无法运行?

    • 5 个回答
  • Marko Smith

    支持结束后如何激活 WindowsXP?

    • 6 个回答
  • Marko Smith

    远程桌面间歇性冻结

    • 7 个回答
  • Marko Smith

    子网掩码 /32 是什么意思?

    • 6 个回答
  • Marko Smith

    鼠标指针在 Windows 中按下的箭头键上移动?

    • 1 个回答
  • Marko Smith

    VirtualBox 无法以 VERR_NEM_VM_CREATE_FAILED 启动

    • 8 个回答
  • Marko Smith

    应用程序不会出现在 MacBook 的摄像头和麦克风隐私设置中

    • 5 个回答
  • Martin Hope
    Saaru Lindestøkke 为什么使用 Python 的 tar 库时 tar.xz 文件比 macOS tar 小 15 倍? 2021-03-14 09:37:48 +0800 CST
  • Martin Hope
    CiaranWelsh 如何减少“vmmem”进程的消耗? 2020-06-10 02:06:58 +0800 CST
  • Martin Hope
    Jim Windows 10 搜索未加载,显示空白窗口 2020-02-06 03:28:26 +0800 CST
  • Martin Hope
    v15 为什么通过电缆(同轴电缆)的千兆位/秒 Internet 连接不能像光纤一样提供对称速度? 2020-01-25 08:53:31 +0800 CST
  • Martin Hope
    andre_ss6 远程桌面间歇性冻结 2019-09-11 12:56:40 +0800 CST
  • Martin Hope
    Riley Carney 为什么在 URL 后面加一个点会删除登录信息? 2019-08-06 10:59:24 +0800 CST
  • Martin Hope
    zdimension 鼠标指针在 Windows 中按下的箭头键上移动? 2019-08-04 06:39:57 +0800 CST
  • Martin Hope
    jonsca 我所有的 Firefox 附加组件突然被禁用了,我该如何重新启用它们? 2019-05-04 17:58:52 +0800 CST
  • Martin Hope
    MCK 是否可以使用文本创建二维码? 2019-04-02 06:32:14 +0800 CST
  • Martin Hope
    SoniEx2 更改 git init 默认分支名称 2019-04-01 06:16:56 +0800 CST

热门标签

windows-10 linux windows microsoft-excel networking ubuntu worksheet-function bash command-line hard-drive

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve