The question is published on by Tutorial Guruji team.
I write a script that iterates over a set of zip files, extracts them and moves only files that match a specific filename pattern to another folder.
The script:
#!/bin/bash ARCHIVE_FILEMASK="????-??-??_E_MDT_0_0.7z" FILEMASK="?????????_?????_A_?_????????????????-?????????" extractDir=/path/to/dir dest=/path/to/dest for f in ${ARCHIVE_FILEMASK} do 7z e -aoa -o"${extractDir}" "$f" if [ $? != 0 ] then mv "${extractDir}/${FILEMASK}".xml "$dest" fi done
When the script is executed an error occurs:
mv: cannot stat `/path/to/dir/?????????_?????_A_?_????????????????-?????????.xml': No such file or directory
I’ve already tried turning on nullglob and extglob but it didn’t help. If I execute the command directly from the shell, it runs just fine. The answers from other posts with similar problems also didn’t help.
edit:
After Paweł Rumian mentioned possible problems with spaces in directory names, I tried AVJ’s tip and added set +x
before the mv
command.
The result is:
+ mv '/path/to/dir/?????????_?????_A_?_????????????????-?????????.xml' /path/to/dest
So obviously I have to get rid of the single quotes now. Any idea how?
Answer
The usual advice to put double quotes around variable expansions is because $foo
outside quotes means “take the value of foo
, split it at whitespace¹, and expand each resulting word as a wildcard pattern if it matches at least one file”, rather than “take the value of foo
”. In particular, when the variable foo
contains a file name, "$foo"
is what you need. But here the variables ARCHIVE_FILEMASK
and FILEMASK
contain wildcard patterns. So you do in fact want to expand them when they’re used, therefore you should use them unquoted. They will thus be interpreted as a whitespace-separated list of patterns; that doesn’t make a difference here since you have no whitespace in these variables’ value.
The variables f
and extractDir
contain file names, so they must remain double-quoted.
for f in ${ARCHIVE_FILEMASK} do if 7z e -aoa -o"${extractDir}" "$f"; then mv "${extractDir}/"${FILEMASK}.xml "$dest" fi done
Since you’re using bash, you can call shopt -s nullglob
to make the wildcard pattern "${extractDir}/"${FILEMASK}.xml
expand to an empty list if it doesn’t match any file. The mv
command will complain that it’s missing a file if you don’t pass it any source file; you can solve that problem by invoking it only if there is at least one match.
shopt -s nullglob for f in ${ARCHIVE_FILEMASK} do if 7z e -aoa -o"${extractDir}" "$f"; then matches=("${extractDir}/"${FILEMASK}.xml) if ((${#matches[@]})); then mv "${matches[@]}" "$dest" fi fi done
¹ Or more generally as indicated by IFS
.