简体   繁体   English

Bash:带有文件名数组的rm

[英]Bash: rm with an array of filenames

So I'm working on making an advanced delete script. 因此,我正在制作一个高级删除脚本。 The idea is the user inputs a grep regex for what needs to be deleted, and the script does an rm operation for all of it. 这个想法是用户输入一个grep regex输入需要删除的内容,然后脚本对该文件执行rm操作。 Basically eliminates the need to write all the code directly in the command line each time. 基本上不需要每次都直接在命令行中编写所有代码。

Here is my script so far: 到目前为止,这是我的脚本:

#!/bin/bash
# Script to delete files passed to it

if [ $# -ne 1 ]; then
    echo "Error! Script needs to be run with a single argument that is the regex for the files to delete"
    exit 1
fi

IFS=$'\n'

files=$(ls -a | grep $1 | awk '{print "\"" $0 "\"" }')

## TODO ensure directory support

echo "This script will delete the following files:"
for f in $files; do
    echo "  $f"
done

valid=false

while ! $valid ; do
    read -p "Do you want to proceed? (y/n): "
    case $REPLY in
        y)
            valid=true
            echo "Deleting, please wait"
            echo $files
            rm ${files}
        ;;
        n)
            valid=true
        ;;
        *)
            echo "Invalid input, please try again"
        ;;
    esac
done

exit 0

My problem is when I actually do the "rm" operation. 我的问题是当我实际执行“ rm”操作时。 I keep getting errors saying No such file or directory. 我不断收到错误消息,说没有这样的文件或目录。

This is the directory I'm working with: 这是我正在使用的目录:

drwxr-xr-x   6 user  staff   204 May  9 11:39 .
drwx------+ 51 user  staff  1734 May  9 09:38 ..
-rw-r--r--   1 user  staff    10 May  9 11:39 temp two.txt
-rw-r--r--   1 user  staff     6 May  9 11:38 temp1.txt
-rw-r--r--   1 user  staff     6 May  9 11:38 temp2.txt
-rw-r--r--   1 user  staff    10 May  9 11:38 temp3.txt

I'm calling the script like this: easydelete.sh '^tem' 我这样调用脚本:easydelete.sh'^ tem'

Here is the output: 这是输出:

This script will delete the following files:
  "temp two.txt"
  "temp1.txt"
  "temp2.txt"
  "temp3.txt"
Do you want to proceed? (y/n): y
Deleting, please wait
"temp two.txt" "temp1.txt" "temp2.txt" "temp3.txt"
rm: "temp two.txt": No such file or directory
rm: "temp1.txt": No such file or directory
rm: "temp2.txt": No such file or directory
rm: "temp3.txt": No such file or directory

If I try and directly delete one of these files, it works fine. 如果我尝试直接删除这些文件之一,则可以正常工作。 If I even pass that whole string that prints out before I call "rm", it works fine. 如果我在调用“ rm”之前甚至传递了打印出来的整个字符串,它就可以正常工作。 But when I do it with the array, it fails. 但是,当我对数组执行此操作时,它将失败。

I know I'm handling the array wrong, just not sure exactly what I'm doing wrong. 我知道我在处理数组错误,只是不确定我在做什么错。 Any help would be appreciated. 任何帮助,将不胜感激。 Thanks. 谢谢。

Consider instead: 考虑改为:

# put all filenames containing $1 as literal text in an array
#files=( *"$1"* )

# ...or, use a grep with GNU extensions to filter contents into an array:
# this passes filenames around with NUL delimiters for safety
#files=( )
#while IFS= read -r -d '' f; do
#  files+=( "$f" )
#done < <(printf '%s\0' * | egrep --null --null-data -e "$1")

# ...or, evaluate all files against $1, as regex, and add them to the array if they match:
files=( )
for f in *; do
  [[ $f =~ $1 ]] && files+=( "$f" )
done

# check that the first entry in that array actually exists
[[ -e $files || -L $files ]] || {
  echo "No files containing $1 found; exiting" >&2
  exit 1
}

# warn the user
echo "This script will delete the following files:" >&2
printf '  %q\n' "${files[@]}" >&2

# prompt the user
valid=0
while (( ! valid )); do
  read -p "Do you want to proceed? (y/n): "
  case $REPLY in
    y) valid=1; echo "Deleting; please wait" >&2; rm -f "${files[@]}" ;;
    n) valid=1 ;;
  esac
done

I'll go into the details below: 我将在下面详细介绍:

  • files has to be explicitly created as an array to actually be an array -- otherwise, it's just a string with a bunch of files in it. 必须将files显式创建为数组才能实际数组-否则,它只是其中包含一堆文件的字符串。

    This is an array: 这是一个数组:

     files=( "first file" "second file" ) 

    This is not an array (and, in fact, could be a single filename): 这不是数组(实际上可以是单个文件名):

     files='"first file" "second file"' 
  • A proper bash array is expanded with "${arrayname[@]}" to get all contents, or "$arrayname" to get only the first entry. 适当的bash数组将使用"${arrayname[@]}"进行扩展以获取所有内容,或者使用"$arrayname"扩展以仅获取第一项。

     [[ -e $files || -L $files ]] 

    ...thus checks the existence (whether as a file or a symlink) of the first entry in the array -- which is sufficient to tell if the glob expression did in fact expand, or if it matched nothing. ...因此检查数组中第一个条目的存在(无论是文件还是符号链接)-足以判断glob表达式是否确实在扩展,或者是否不匹配。

  • A boolean is better represented with numeric values than a string containing true or false : Running if $valid has potential to perform arbitrary activity if the contents of valid could ever be set to a user-controlled value, whereas if (( valid )) -- checking whether $valid is a positive numeric value (true) or otherwise (false) -- has far less room for side effects in presence of bugs elsewhere. 一个布尔值,是更好地与比含有字符串数值来表示truefalse :跑步if $valid有潜力执行任意的活动,如果内容valid所能设置为用户控制值,而if (( valid )) - -检查$valid是一个正数值(true)还是其他(false)-在其他地方存在错误的情况下,产生副作用的空间要小得多。

  • There's no need to loop over array entries to print them in a list: printf "$format_string" "${array[@]}" will expand the format string additional times whenever it has more arguments (from the array expansion) than its format string requires. 不需要遍历数组条目即可将它们打印在列表中: printf "$format_string" "${array[@]}"将在其参数(来自数组扩展)的参数多于其格式时,将扩展格式字符串更多次。字符串要求。 Moreover, using %q in your format string will quote nonprintable values, whitespace, newlines, &c. 此外,在格式字符串中使用%q将引用不可打印的值,空格,换行符&c。 in a format that's consumable by both human readers and the shell -- whereas otherwise a file created with touch $'evil\\n - hiding' will appear to be two list entries, whereas in fact it is only one. 以人类阅读器和外壳都可以使用的格式-否则,通过touch $'evil\\n - hiding'创建的文件将看起来touch $'evil\\n - hiding'两个列表项,而实际上只是一个列表项。

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

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