bash script - ignoring whitespaces in script param

2019-07-23 15:56发布

I'm quite new to bash scripting and I've ran out of ideas in my homework script.

The script takes 4 arguments - pathToDirectory, [-c/-m/-r] == copy/move/remove, ext1 and ext2 (example of running a script: script.sh /home/user/somefolder -c a.txt b.sh ).

The script should find all files in /home/user/someFolder (and its all subfolders) that contain 'a.txt' in their names and (in -c and -m case) rename that 'a.txt' part to 'b.sh' and depending on -c/-m argument either create a new file or just rename an existing file (in -r case it just removes the file) and then write in stdout something like 'old name => new name'.

example output of a script mentioned above:
/home/user/someFolder/bbb.txt => /home/user/someFolder/bba.txt

Well, that was not a problem to implement, everything worked until I posted my code to our upload system (evaluates our script).

The very first Upload System's try to run my script looked like "script.sh /something/graph 1 -c .jpg .jpeg".

The problem now is, that the whole '/something/graph 1' is a path and that whitespace before '1' ruins it all.

expected output: ./projekty/graph 1.jpg => ./projekty/graph 1.jpeg
my script output: ./projekty/graph => ./projekty/graph.jpeg 1.jpg => 1.jpeg

What I have so far:

if [ "$2" = "-r" ]; then
    for file in  $(find $1 -name "*$3"); do
        echo $file
        rm -f $file
    done    

elif [ "$2" = "-c" ]; then
    for file in  $(find "$1" -name "*$3") ; do      
        cp "$file" "${file//$3/$4}"
        echo $file "=>" ${file%$3}$4            
    done

elif [ "$2" = "-m" ]; then
    for file in  $(find $1 -name "*$3"); do
        mv "$file" "${file//$3/$4}"
        echo $file "=>" ${file%$3}$4        
    done    

else    
    echo Unknown parameter >&2
fi

My tried&notworking&probablystupid idea: as the -r/-c/-m parameter should be at $2, I was able to detect that $2 is something else (assumpting something that still belongs to the path) and append that $2 thing to $1, so then I had a variable DIR which was the whole path. Using shift I moved all parameters to the left (because of the whitespace, the -r/-m/-c parameter was not on $2 but on $3, so I made it $2 again) and then the code looked like: (just the -c part)

DIR=$1
if [ "$2" != "-r" ] && [ "$2" != "-c" ] && [ "$2" != "-m" ]; then
     DIR+=" $2"
     shift      
fi

if [ "$2" = "-c" ]; then
    for file in  $(find "$DIR" -name "*$3") ; do        
        cp "$file" "${file//$3/$4}"
        echo $file "=>" ${file%$3}$4            
    done    
fi

when i echoed "$DIR", it showed the whole path (correctly), but it still didn't work.. Is there any other/better/any way how to fix this please ? :/

Thanks in advance !

标签: bash shell
1条回答
forever°为你锁心
2楼-- · 2019-07-23 16:28
  1. As the target string needs to be replaced only at the very end of a filename, "${file//$3/$4}" is a bad idea.

    Example: ./projekty/graph.jpg.jpg.jpg.graph.jpg

  2. Passing a string prone to unquoted expansion to a loop is a no better idea either.

    The fact is that find works as expected and its output looks like:

    ./projekty/graph 1.jpg
    

    But inside a loop it is expanded incorrectly:

    ./projekty/graph
    1.jpg
    

    To avoid this, you can save the output of find to a variable and then tokenize it until no text is left:

    list="$(find $1 -name "*$3")"
    
    while [ -n "$list" ]; do
        file="${list%%$'\n'*}"
        list="${list#$file}"
        list="${list#$'\n'}"
        # your commands here
        # ...
    done
    
查看更多
登录 后发表回答