1

I'm trying to write a code that moves files to a .junk directory using the built-in environment variables. My goal is that all a user would have to do is type sh junk filename1 filename2 filename3... and the script would use $1, $2, $3... to move the files. Unfortunately I can't quite get the counter loop to work correctly.

#!/bin/s if [ $# = 0 ] then echo "Usage: junk -l | -p | { filename }*" elif [ $1 = "-l" ] && [ $HOME/.junk ] then ls $HOME/.junk elif [ $1 = "-p" ] && [ $HOME/.junk ] then rm -r $HOME/.junk elif [ $1 = "$@" ] && [ $HOME/.junk ] then echo $# i=1 s="$" while [ $i -le $# ] do echo $s$i mv $s$i .junk/$i i=$(($i+1)) done fi 
6
  • 2
    What are "built-in environment variables"? Which shell are you using /bin/s?
    – Anthon
    CommentedApr 28, 2014 at 21:36
  • 1
    That is terrible code. $1 should always be quoted: "$1". What is [ $HOME/.junk ] supposed to be/do? And [ $1 = "$@" ]? Why does while [ $i -le $# ] start with i=1 instead of i=0? What is supposed to happen with a single file argument? mv $s$i is probably supposed to be an (unquotes, of course) indirection which would have to be done with eval or (better) "${!i}"... The counter loop really is the least of your problems.CommentedApr 28, 2014 at 21:44
  • Why are you using a loop in the first place? All you really need is mv "$@" .junk. Could you explain what your script is doing and why you want the loop?
    – terdon
    CommentedApr 28, 2014 at 21:52
  • @terdon there is one barely possible chance that mv "$@" ./junk will present argument list size problems, but that would take a lot of junk.
    – mikeserv
    CommentedApr 29, 2014 at 0:07
  • @mikeserv It is likely that calling the script would be a problem too in that case.
    – Kusalananda
    CommentedFeb 13, 2019 at 15:07

2 Answers 2

1

Some immediate problems:

  1. Use getopts for argument processing or have a reason why not.
  2. If the script is given more than 1 argument, the "$@" in the third elif will blow it up.
  3. Use a for f in "$@" loop instead of a while loop.

Why?

  1. What happens if you type sh junk --help ... or some more interesting mv option?
  2. QED
  3. Simpler => Better
3
  • 1
    for f in "$@" ; do is hardly simpler than for f ; do which is the same thing when you're iterating on positionals. Also while and until allow conditionals - as many as you can put in a compound command list, thought I don't think our asker is making good use of it. One I always use for positionals is while ${1+:} false ; do which is pretty simple. It also gives me the freedom of skipping iterations with shift as needed.
    – mikeserv
    CommentedApr 29, 2014 at 18:15
  • @mikeserv Good comment, but it would be a better answer. Just add one sentence at the top pointing out that attempting to treat positional parameters like an array is the wrong idea. It might actually be useful to some other bash beginner and I'll upvote it past my grumpy answer.CommentedApr 29, 2014 at 18:32
  • Positional parameters are an array. The shell positional $@array is the only fully portable shell array there is. And with its special IFS handling its really the only one you need.
    – mikeserv
    CommentedApr 29, 2014 at 19:29
1
#!/bin/sh junkdir="$HOME/.junk" do_list=false do_purge=false while getopts 'lp' opt; do case "$opt" in l) do_list=true ;; p) do_purge=true ;; *) printf 'Usage: %s [-l|-p] [ name ... ]\n' "$0" >&2 exit 1 esac done shift "$(( OPTIND - 1 ))" if [ ! -d "$junkdir" ] && ! "$do_purge"; then printf '%s: Missing junk directory "%s"\n' "$0" "$junkdir" >&2 printf '%s: Re-run with -p to create it\n' "$0" >&2 exit 1 fi if "$do_list"; then printf 'Current junk in "%s":\n' "$junkdir" ls -l "$junkdir" fi if "$do_purge"; then printf 'Emptying junk directory "%s"\n' "$junkdir" rm -rf "$junkdir" mkdir "$junkdir" fi if [ "$#" -eq 0 ]; then exit fi echo 'Junking the following things:' printf '%s\n' "$@" mv "$@" "$junkdir" 

This script implements what it is you're trying to do. It uses getopts to parse the command line flags -l and/or -p.

I'm setting do_list and do_purge in the while loop that parses the command line option because I want to control the order in which the ls and the rm takes place further down. If a user uses -lp or -pl I want them to see the contents of the junk directory before it is removed.

Removing the junk directory with -p also recreates it. I do this with rm -rf followed by mkdir for simplicity.

After the command line parsing loop, I adjust the positional parameters so that they only contain non-option arguments (names of files and/or directories).

If there are no positional arguments ($# is zero), I don't try to run mv to move files to the junk directory.

    You must log in to answer this question.

    Start asking to get answers

    Find the answer to your question by asking.

    Ask question

    Explore related questions

    See similar questions with these tags.