Unix Command to Escape Spaces

Handling quoting and escaped spaces in Bash command arguments

It's really this simple:

your_command "$filename_with_spaces_maybe" -opt1 "$some_words" -opt2 "$a_number" -opt3 "$a_file_reference" -opt4 "$several_sentences"

That is to say:

  • Put every parameter expansion (where a variable name is replaced with its value) in double quotes -- even if you think the value will only be numeric.
  • Don't bother with any kind of eval, or assignment, etc.

The only quotes required are syntactic, not literal -- "nested" or "escaped" quotes are literal characters without syntactic meaning, so they have no effect on parsing (without an additional parse step, as generated by eval, which is indeed bad practice when at all avoidable), and just add extra unwanted content to the command actually being run.


For cases when you think you need to store a command in a variable for other reasons (to add arguments only conditionally, or to be able to reuse it), see BashFAQ #50.

If you weren't already convinced that eval is a Bad Idea, you'd want to read BashFAQ #48.

The Wooledge page on Quotes is an excellent resource for this question in general.

How to escape space in file path in a bash script

This might solve your issue:

while IFS= read -r -d $'\n'
do
echo "${REPLY}"
done < <(ios-deploy --id UUID --bundle_id BUNDLE -l | grep Documents)

Edit per Charles Duffy recommendation:

while IFS= read -r line
do
echo "${line}"
done < <(ios-deploy --id UUID --bundle_id BUNDLE -l | grep Documents)

How can I escape white space in a bash loop list?

First, don't do it that way. The best approach is to use find -exec properly:

# this is safe
find test -type d -exec echo '{}' +

The other safe approach is to use NUL-terminated list, though this requires that your find support -print0:

# this is safe
while IFS= read -r -d '' n; do
printf '%q\n' "$n"
done < <(find test -mindepth 1 -type d -print0)

You can also populate an array from find, and pass that array later:

# this is safe
declare -a myarray
while IFS= read -r -d '' n; do
myarray+=( "$n" )
done < <(find test -mindepth 1 -type d -print0)
printf '%q\n' "${myarray[@]}" # printf is an example; use it however you want

If your find doesn't support -print0, your result is then unsafe -- the below will not behave as desired if files exist containing newlines in their names (which, yes, is legal):

# this is unsafe
while IFS= read -r n; do
printf '%q\n' "$n"
done < <(find test -mindepth 1 -type d)

If one isn't going to use one of the above, a third approach (less efficient in terms of both time and memory usage, as it reads the entire output of the subprocess before doing word-splitting) is to use an IFS variable which doesn't contain the space character. Turn off globbing (set -f) to prevent strings containing glob characters such as [], * or ? from being expanded:

# this is unsafe (but less unsafe than it would be without the following precautions)
(
IFS=$'\n' # split only on newlines
set -f # disable globbing
for n in $(find test -mindepth 1 -type d); do
printf '%q\n' "$n"
done
)

Finally, for the command-line parameter case, you should be using arrays if your shell supports them (i.e. it's ksh, bash or zsh):

# this is safe
for d in "$@"; do
printf '%s\n' "$d"
done

will maintain separation. Note that the quoting (and the use of $@ rather than $*) is important. Arrays can be populated in other ways as well, such as glob expressions:

# this is safe
entries=( test/* )
for d in "${entries[@]}"; do
printf '%s\n' "$d"
done

Escape space character in command line argument for bash script

First off, if you want your parameters to be parsed comma-separated, why do you have a space (which you do not want to separate anything) in your IFS?

Change:

IFS=', ' read -r -a array <<< "${@}"

To:

IFS=',' read -r -a array <<< "${@}"

Voila, spaces are no longer separating parameters, and you get ${array[1]} to contain second path/file.txt as desired.

Then comes the second gotcha:

for t in ${array[@]};

${array[@]} gets expanded to all members of array, which are two (separated by the first character in IFS), with one of them containing a space.

And since your IFS redefinition was for your read line only, for will now use the default, which again includes the space as separating character. And for will separate the list at the space between second and path...

So redefine IFS to include comma only, for both read and for:

#!/bin/bash
IFS=','
read -r -a array <<< "${@}"

for t in ${array[@]}
do
envsubst < $t > "${t}_new"
done

There you go.



Related Topics



Leave a reply



Submit