Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/bash/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/shell/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Bash脚本:删除带有通配符的文件和目录列表的更好方法_Bash_Shell_If Statement_For Loop - Fatal编程技术网

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

Bash脚本:删除带有通配符的文件和目录列表的更好方法,bash,shell,if-statement,for-loop,Bash,Shell,If Statement,For Loop,我试图传递一个文件列表,其中包括我要删除的通配符文件和目录,但在删除之前先检查它们是否存在。如果已删除这些文件,请通知已删除目录,而不是目录中的每个文件。也就是说,如果我删除了/root/*.tst,只需说“我删除了*.tst” 我似乎无法组合使用单引号、双引号或转义*让上面的代码执行我希望它执行的操作。在解释代码失败的原因之前,您应该使用以下方法: for i in /boot/*.txt /root/*.txt /root/tmpdir; do # i will be a singl

我试图传递一个文件列表,其中包括我要删除的通配符文件和目录,但在删除之前先检查它们是否存在。如果已删除这些文件,请通知已删除目录,而不是目录中的每个文件。也就是说,如果我删除了/root/*.tst,只需说“我删除了*.tst”

我似乎无法组合使用单引号、双引号或转义*让上面的代码执行我希望它执行的操作。

在解释代码失败的原因之前,您应该使用以下方法:

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
,并尝试使用展开中的第一个名称作为命令名,在变量
文件
具有文本值
/boot/*.tst
的环境中执行。要在字符串中包含所有模式,您需要使用

file=/boot/*.tst\ /root/*.tst\ /root/tmpdir
或者更自然地

file="/boot/*.tst /root/*.tst /root/tmpdir"
无论哪种方式,模式尚未扩展;文本
*
存储在
文件的值中。然后使用

for i in $file    # no quotes!
$file
扩展为其文本值后,存储的模式将扩展为匹配的文件名集。但是,此循环仅适用于不包含空格的文件名;名为
foo-bar
的单个文件将被视为分配给
i
的两个独立值,即
foo
bar
。在
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[@]}"
在解释代码失败的原因之前,以下是您应该使用的:

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
,并尝试使用展开中的第一个名称作为命令名,在变量
文件
具有文本值
/boot/*.tst
的环境中执行。要在字符串中包含所有模式,您需要使用

file=/boot/*.tst\ /root/*.tst\ /root/tmpdir
或者更自然地

file="/boot/*.tst /root/*.tst /root/tmpdir"
无论哪种方式,模式尚未扩展;文本
*
存储在
文件的值中。然后使用

for i in $file    # no quotes!
$file
扩展为其文本值后,存储的模式将扩展为匹配的文件名集。但是,此循环仅适用于不包含空格的文件名;名为
foo-bar
的单个文件将被视为分配给
i
的两个独立值,即
foo
bar
。在
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

你可以替换

file=/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
变量没有被引用(
表示$glob中的路径
),这会在该点触发shell扩展。

shell会自动扩展glob。如果希望能够在错误消息中打印文本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
变量没有被引用(
表示$glob中的路径
),这会在此时触发shell扩展。

多亏了包括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
}

非常感谢包括约翰·库格曼在内的所有人的帖子

这是我最后使用的代码,它提供了两种类型的删除。第一种是更有力地删除所有内容。第二个保留的目录结构只是删除文件。如上所述,请注意,文件名中的空白不由此方法处理

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
}

这只适用于不包含空格的模式(例如,
rmglob'/root/tmp dir'
),这与我在通知已删除的内容方面所期望的完全一样。您将如何修改上述内容以确保捕获带有空格的文件?这仅适用于不包含空格的模式(例如,
rmglob'/root/tmp dir'
),这与我在通知已删除的内容方面所期望的完全一样。您将如何修改上述内容以确保捕获带有空格的文件?