Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

I am writing a script that applies a command to loads of files. The way I automate this boils down to

#!/bin bash
for in_file in ${PWD}/*/*/*.txt; do
    out_file="${in_file//in/out}"
    CM="/usr/local/bin/command -in \"${in_file}\" -out \"${out_file}\""
    echo ${CM}
    ${CM}
done

The typical output I get is

/usr/local/bin/command -in "/home/user/infile.txt" -out "/home/user/outfile.txt"
error:
/usr/local/bin/command: unable to find file "/home/user/infile.txt"
...

But then I cut & paste the same command, verbatim, including all the quotes etc., on the command prompt, and it runs without a problem!

Because many file names (that I did not make!) have spaces, I need the quotes in the script. But can someone tell me why they do not work in the script and do work on the command prompt? And is there a way to do it properly?

share|improve this question
3  
mywiki.wooledge.org/BashFAQ/050: "I'm trying to put a command in a variable, but the complex cases always fail!" –  glenn jackman Feb 25 at 14:55
    
+1 Using functions, that looks like scripting as Brian Fox intended. I know I should, but it feels like overkill for small scripts. –  alle_meije Feb 26 at 12:44

2 Answers 2

up vote 4 down vote accepted

Use an array: that's the best way to preserve a multi-word command with arguments containing whitespace.

#!/bin bash
for in_file in ${PWD}/*/*/*.txt; do
    out_file="${in_file//in/out}"
    cmd=( /usr/local/bin/command -in "$in_file" -out "$out_file" )
    echo "${cmd[@]}"
    "${cmd[@]}"
done
share|improve this answer
    
that is an excellent (and very clean) way to do it -- thanks! –  alle_meije Feb 25 at 16:32

"${CM}" will not use blank characters as separators. So bash thinks the whole string is the name of your program.

With ${CM} bash think the " are part of the argument.

You could use << eval "${CM}" >> but you could have side effects with unescaped special chars in file names.

So better is the following :

Does this work ?

#!/bin bash
for in_file in "$PWD"/*/*/*.txt; do
    out_file="$in_file/in/out"
    CM="/usr/local/bin/command -in \"$in_file\" -out \"$out_file\""
    echo "${CM}"
    /usr/local/bin/command -in "$in_file" -out "$out_file"
done

Instead of echoing your commands, use "set -x" to debug your script :

#!/bin bash
set -x
for in_file in "$PWD"/*/*/*.txt; do
    out_file="$in_file/in/out" 
    /usr/local/bin/command -in "$in_file" -out "$out_file"
done

or this :

#!/bin bash
for in_file in "$PWD"/*/*/*.txt; do
    out_file="$in_file/in/out" 
    set -x
    /usr/local/bin/command -in "$in_file" -out "$out_file"
    set +x
done
share|improve this answer
    
Thanks! This solution works -- but the idea was not to have to repeat typing (or having in the script) the same command. I did not know the set -x and set -e lines, nice! (the +x is required!) –  alle_meije Feb 26 at 12:38

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.