繁体   English   中英

Bash脚本:使用通配符删除文件和目录列表的更好方法

[英]Bash script: A better way to remove a list of files and directories with wildcards

我正在尝试传递文件列表,包括要删除的通配符文件和目录,但是请检查它们以查看它们是否先存在然后再删除。 如果删除了它们,则通知该目录已删除,而不是目录中的每个文件。 即,如果我删除/root/*.tst,请说“我删除了* .tst”。

#!/bin/bash

touch /boot/{1,2,3,4,5,6,7,8,9,10}.tst
touch /root/{1,2,3,4,5,6,7,8,9,10}.tst
mkdir /root/tmpdir

#setup files and directories we want to delete
file=/boot/*.tst /root/*.tst /root/tmpdir
for i in $file; do
    if [[ -d "$file" || -f "$file" ]]; then #do I exist
        rm -fr $i
        echo removed $i #should tell me that I removed /boot/*.tst or /root/*.tst or /root/tmpdir
    else
        echo $i does not exist # should tell me if /boot/*.tst or /root/*.tst or /root/tmpdir DNE
    fi
done

我似乎无法将单引号或双引号或转义符进行任何组合*使上述内容达到我想要的目的。

在解释代码为何失败之前,应使用以下方法:

for i in /boot/*.txt /root/*.txt /root/tmpdir; do
    # i will be a single file if a pattern expanded, or the literal
    # pattern if it does not. You can check using this line:
    [[ -f $i ]] || continue
    # or use shopt -s nullglob before the loop to cause non-matching
    # patterns to be silently ignored
    rm -fr "$i"
    echo removed $i
done

似乎希望将i设置为三种模式中的每一种,这有点棘手,应该避免使用,因为您使用的大多数运算符都期望单个文件或目录名称,而不是匹配多个名称的模式。


您展示的尝试

file=/boot/*.tst /root/*.tst /root/tmpdir

将扩展/root/*.tst并尝试在扩展名中将第一个名称用作命令名,该命令在变量file具有文字值/boot/*.tst的环境中执行。 要将所有模式包含在字符串中,您需要使用它们之间的空格来转义

file=/boot/*.tst\ /root/*.tst\ /root/tmpdir

或更自然

file="/boot/*.tst /root/*.tst /root/tmpdir"

无论哪种方式,模式都还没有扩展。 文字*存储在file的值中。 然后,您将使用

for i in $file    # no quotes!

$file扩展为其文字值之后,存储的模式将扩展为匹配文件名的集合。 但是,此循环仅适用于不包含空格的文件名。 名为foo bar的单个文件将被视为分配给i两个单独的值,即foobar bash处理此类文件名的正确方法是使用数组:

files=( /boot/*.tst /root/*.tst /root/tmpdir )

# Quote are necessary this time to protect space-containing filenames
# Unlike regular parameter assignment, the patterns were expanded to the matching
# set of file names first, then the resulting list of files was assigned to the array,
# one file name per element.
for i in "${files[@]}"

您可以更换

file=/boot/*.tst /root/*.tst /root/tmpdir

通过

printf -v file "%s " /boot/*.tst /root/*.tst /root/tmpdir

外壳会自动扩展球体。 如果您希望能够在错误消息中打印原义glob,则需要用引号引起来。

rmglob() {
    local glob

    for glob in "$@"; do
        local matched=false

        for path in $glob; do
            [[ -e $path ]] && rm -rf "$path" && matched=true
        done

        $matched && echo "removed $glob" || echo "$glob does not exist" >&2
    done
}

rmglob '/boot/*.tst' '/root/*.tst' '/root/tmpdir'

请注意谨慎使用引号。 引用deleteGlobs的参数。 函数内的$glob变量未加引号( for path in $glob ),这会在此时触发外壳扩展。

非常感谢所有人的帖子,包括John Kugelman。

这是我最终使用的代码,提供了两种删除类型。 首先是删除所有内容的强制性。 第二个保留的目录结构只是删除文件。 如上所述,请注意,此方法不处理文件名中的空格。

rmfunc() {
local glob

for glob in "$@"; do
    local matched=false 
    local checked=true

    for path in $glob; do
        $checked && echo -e "\nAttempting to clean $glob" && checked=false
        [[ -e $path ]] && rm -fr "$path" && matched=true
    done

    $matched && echo -e "\n\e[1;33m[\e[0m\e[1;32mPASS\e[1;33m]\e[0m Cleaned $glob" || echo -e "\n\e[1;33m[\e[0m\e[1;31mERROR\e[1;33m]\e[0m Can't find $glob (non fatal)."   
done
}

# Type 2 removal
xargfunc() {
local glob

for glob in "$@"; do
    local matched=false
    local checked=true 

    for path in $glob; do
        $checked && echo -e "\nAttempting to clean $glob" && checked=false
        [[ -n $(find $path -type f) ]] && find $path -type f | xargs rm -f && matched=true
    done

    $matched && echo -e "\n\e[1;33m[\e[0m\e[1;32mPASS\e[1;33m]\e[0m Cleaned $glob" || echo -e "\n\e[1;33m[\e[0m\e[1;31mERROR\e[1;33m]\e[0m Can't find $glob (non fatal)."   
fi
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM