0

This is a simple script I wrote for a class, where we're suppose to "fake delete" files by moving them to $HOME/trashbin. I'm trying to make it compatible with file names containing spaces, however nothing seems to work.

Even when used with fake_erase.sh "file name.txt", if I do echo $# it says 2 and tries to parse both bits of the input separately. Is there anything I can do so that it works properly ?

rm "file name.txt" has no problem with the file having a space in it if it's in quotes ! (though it's likely not written in bash)

Here is the entire code :

#!/usr/bin/env bash

trashbin="$HOME/trashbin"

function usage() {
    echo "usage : $0 file ..."
    echo "moves files to $trashbin"
    echo "creates $trashbin if it doesn't exist"
    exit -1
}

# Error if no arguments
if (( $# < 1 )); then
    usage
fi

# Create directory if it doesn't exist
if [[ ! -d "$trashbin" ]]; then
    mkdir "$trashbin"
fi

for i in $@; do
    name="$( basename $i )"
    mv $i "$trashbin/$name"
done
ice-wind
  • 690
  • 4
  • 20
  • 1
    If it is appropriate to print a usage statement (eg, if the user requests it, or if you decide that calling the program with no arguments is a valid request for usage), your program should print the usage statement and `exit 0`. If you consider calling the program without arguments an error, you should print a message to stderr and exit non-zero. Exiting non-zero without writing an error message to stderr is bad practice. (extremely common, but still bad) – William Pursell Oct 28 '22 at 15:24
  • 1
    You didn't use quotes everywhere they're needed. `name="$(basename "$i")"`. Really, only the inner ones are strictly necessary on that line (`name=$(basename "$i")` is just fine). – Charles Duffy Oct 28 '22 at 15:28
  • 1
    Also, `for i in $@` needs to be `for i in "$@"`, `mv $i` needs to be `mv "$i"`, etc etc. – Charles Duffy Oct 28 '22 at 15:28
  • 1
    ...consider running your code through http://shellcheck.net/ and fixing what it finds. – Charles Duffy Oct 28 '22 at 15:29
  • Mind, with `fake_erase.sh "file name.txt"` causing `$#` to be 2 instead of 1, I suspect you have other problems in code you aren't showing us. In particular, it smells like [BashFAQ #50](https://mywiki.wooledge.org/BashFAQ/050). – Charles Duffy Oct 28 '22 at 15:30
  • ...an easy way to get that bug is `command='fake_erase "file name.txt"'` and then running `$command`. Don't do that; just run `fake_erase "file name.txt"` with no variable involved anywhere; if you have a _reason_ to store a command in a variable, read through BashFAQ #50 as linked above and follow whichever remediation is appropriate to your circumstances. – Charles Duffy Oct 28 '22 at 15:31
  • (another easy way to get that bug is "smart quotes" instead of real UNIX quotes; that's not evident in the code you've shown here, but some tools that were more written for word processing than for programmers' use replace `"` with prettier open-quote or close-quote characters, and those don't actually work for quoting). – Charles Duffy Oct 28 '22 at 15:33
  • ...but for any of the alternate problems above, we'd need a [mre] that actually demonstrates them; the code in the question doesn't, so for now, I'm closing it as a duplicate of a preexisting question about the problems for which a reproducer was _actually shown_. – Charles Duffy Oct 28 '22 at 15:34
  • Not relevant to your core problem, but the use of `basename` is completely unnecessary here, unless you want to manipulate the basename. `mv -- "$i" "$trashbin"` would suffice. – M. Nejat Aydin Oct 28 '22 at 15:36
  • BTW, just to point out something William said earlier -- usage errors (and logs, and prompts, and anything else meant for the user to read to understand what the program is doing) should go to stderr, not stdout. You don't want them running through a pipeline and never being seen by the user. – Charles Duffy Oct 28 '22 at 15:36

0 Answers0