I like the possibility in linux shell to store a command into a variable:
mycommand="cp fileOne.txt fileTwo.txt /home/myself/targetdir"
$mycommand
That's executes well.
But how to handle whitspaces inside the filenames?
The following alternatives runns well:
cp file\ One.txt file\ Two.txt /home/myself/targetdir
# and also
cp "file One.txt" "file Two.txt" /home/myself/targetdir
Now I try to put this into my variable too (To call $mycommand
).
But none of my following trys run:
mycommand="cp file\ One.txt file\ Two.txt /home/myself/targetdir"
mycommand="cp \"file One.txt\" \"file Two.txt\" /home/myself/targetdir"
mycommand="cp 'file One.txt' 'file Two.txt' /home/myself/targetdir"
mycommand='cp "file One.txt" "file Two.txt" /home/myself/targetdir'
In all options the arguments are sepparated by whitespaces, the file "file" will not be found.
What can I do instead?
(I'm wondering why I cannot find similar question already taken about this...)
EDIT:
With set -x
I get the following lines for cp
, depending from the try above:
+ cp 'file\' One.txt 'file\' Two.txt /home/myself/targetdir
+ cp '"file' 'One.txt"' '"file' 'Two.txt"' /home/myself/targetdir
+ cp ''\''file' 'One.txt'\''' ''\''file' 'Two.txt'\''' /home/myself/targetdir
+ cp '"file' 'One.txt"' '"file' 'Two.txt"' /home/myself/targetdir
The first Output line for each try, translated from german to english, are:
cp: cannot stat 'file\': No such file or directory
cp: cannot stat '"file'’: No such file or directory
cp: cannot stat ''\''file': No such file or directory
cp: cannot stat '"file': No such file or directory
It seems that the command string is separated by whitespaces, ignoring ""
and \
- but why? And how can I avoid this?
I found the solution:
eval $mycommand
and, probably more secure for other cenarios,
eval "$mycommand"
will work as expected.
(With eval
variables will be expanded before executing.)
An example of practical use:
Given a tool to concatenate pdf files like pdfunite
:
for file in *.pdf; do
inputfiles="$inputfiles \"$file\""
done
eval "pdfunite $inputfiles mergedOutputfile.pdf"
Edit:
Seeing this old post, I'm adding the better solution I'm using now - already mentioned in the link posted by @chepner:
use an array:
inputfiles=()
for file in *.pdf; do
inputfiles+=("$file")
done
pdfunite "${inputfiles[@]}" "/path/to/merged outputfile.pdf"
(It doesn't seem to answer the first question, but in many cases this scenario is the origin of the question)